PageRenderTime 77ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/src/Utils/MiscUI/SciEdit.cpp

https://github.com/YueLinHo/TortoiseGit
C++ | 1671 lines | 1444 code | 117 blank | 110 comment | 345 complexity | f4d30f5ce07f91174ebb780998489b45 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", 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()
  518. {
  519. if (pChecker == NULL)
  520. return;
  521. TEXTRANGEA textrange;
  522. LRESULT firstline = Call(SCI_GETFIRSTVISIBLELINE);
  523. LRESULT lastline = firstline + Call(SCI_LINESONSCREEN);
  524. textrange.chrg.cpMin = (LONG)Call(SCI_POSITIONFROMLINE, firstline);
  525. textrange.chrg.cpMax = (LONG)textrange.chrg.cpMin;
  526. LRESULT lastpos = Call(SCI_POSITIONFROMLINE, lastline) + Call(SCI_LINELENGTH, lastline);
  527. if (lastpos < 0)
  528. lastpos = Call(SCI_GETLENGTH)-textrange.chrg.cpMin;
  529. Call(SCI_SETINDICATORCURRENT, INDIC_MISSPELLED);
  530. while (textrange.chrg.cpMax < lastpos)
  531. {
  532. textrange.chrg.cpMin = (LONG)Call(SCI_WORDSTARTPOSITION, textrange.chrg.cpMax+1, TRUE);
  533. if (textrange.chrg.cpMin < textrange.chrg.cpMax)
  534. break;
  535. textrange.chrg.cpMax = (LONG)Call(SCI_WORDENDPOSITION, textrange.chrg.cpMin, TRUE);
  536. if (textrange.chrg.cpMin == textrange.chrg.cpMax)
  537. {
  538. textrange.chrg.cpMax++;
  539. // since Scintilla squiggles to the end of the text even if told to stop one char before it,
  540. // we have to clear here the squiggly lines to the end.
  541. if (textrange.chrg.cpMin)
  542. Call(SCI_INDICATORCLEARRANGE, textrange.chrg.cpMin-1, textrange.chrg.cpMax - textrange.chrg.cpMin + 1);
  543. continue;
  544. }
  545. ATLASSERT(textrange.chrg.cpMax >= textrange.chrg.cpMin);
  546. std::unique_ptr<char[]> textbuffer(new char[textrange.chrg.cpMax - textrange.chrg.cpMin + 2]);
  547. SecureZeroMemory(textbuffer.get(), textrange.chrg.cpMax - textrange.chrg.cpMin + 2);
  548. textrange.lpstrText = textbuffer.get();
  549. textrange.chrg.cpMax++;
  550. Call(SCI_GETTEXTRANGE, 0, (LPARAM)&textrange);
  551. int len = (int)strlen(textrange.lpstrText);
  552. if (len == 0)
  553. {
  554. textrange.chrg.cpMax--;
  555. Call(SCI_GETTEXTRANGE, 0, (LPARAM)&textrange);
  556. len = (int)strlen(textrange.lpstrText);
  557. textrange.chrg.cpMax++;
  558. len++;
  559. }
  560. if (len && textrange.lpstrText[len - 1] == '.')
  561. {
  562. // Try to ignore file names from the auto list.
  563. // Do do this, for each word ending with '.' we extract next word and check
  564. // whether the combined string is present in auto list.
  565. TEXTRANGEA twoWords;
  566. twoWords.chrg.cpMin = textrange.chrg.cpMin;
  567. twoWords.chrg.cpMax = (LONG)Call(SCI_WORDENDPOSITION, textrange.chrg.cpMax + 1, TRUE);
  568. std::unique_ptr<char[]> twoWordsBuffer(new char[twoWords.chrg.cpMax - twoWords.chrg.cpMin + 1]);
  569. twoWords.lpstrText = twoWordsBuffer.get();
  570. SecureZeroMemory(twoWords.lpstrText, twoWords.chrg.cpMax - twoWords.chrg.cpMin + 1);
  571. Call(SCI_GETTEXTRANGE, 0, (LPARAM)&twoWords);
  572. CString sWord = StringFromControl(twoWords.lpstrText);
  573. if (m_autolist.find(sWord) != m_autolist.end())
  574. {
  575. //mark word as correct (remove the squiggle line)
  576. Call(SCI_INDICATORCLEARRANGE, twoWords.chrg.cpMin, twoWords.chrg.cpMax - twoWords.chrg.cpMin);
  577. textrange.chrg.cpMax = twoWords.chrg.cpMax;
  578. continue;
  579. }
  580. }
  581. if (len)
  582. textrange.lpstrText[len - 1] = 0;
  583. textrange.chrg.cpMax--;
  584. if (strlen(textrange.lpstrText) > 0)
  585. {
  586. CString sWord = StringFromControl(textrange.lpstrText);
  587. if ((GetStyleAt(textrange.chrg.cpMin) != STYLE_URL) && IsMisspelled(sWord))
  588. {
  589. //mark word as misspelled
  590. Call(SCI_INDICATORFILLRANGE, textrange.chrg.cpMin, textrange.chrg.cpMax - textrange.chrg.cpMin);
  591. }
  592. else
  593. {
  594. //mark word as correct (remove the squiggle line)
  595. Call(SCI_INDICATORCLEARRANGE, textrange.chrg.cpMin, textrange.chrg.cpMax - textrange.chrg.cpMin);
  596. Call(SCI_INDICATORCLEARRANGE, textrange.chrg.cpMin, textrange.chrg.cpMax - textrange.chrg.cpMin + 1);
  597. }
  598. }
  599. }
  600. }
  601. void CSciEdit::SuggestSpellingAlternatives()
  602. {
  603. if (pChecker == NULL)
  604. return;
  605. CString word = GetWordUnderCursor(true);
  606. Call(SCI_SETCURRENTPOS, Call(SCI_WORDSTARTPOSITION, Call(SCI_GETCURRENTPOS), TRUE));
  607. if (word.IsEmpty())
  608. return;
  609. char ** wlst = nullptr;
  610. int ns = pChecker->suggest(&wlst, CStringA(word));
  611. if (ns > 0)
  612. {
  613. CString suggestions;
  614. for (int i=0; i < ns; i++)
  615. {
  616. suggestions.AppendFormat(_T("%s%c%d%c"), CString(wlst[i]), m_typeSeparator, AUTOCOMPLETE_SPELLING, m_separator);
  617. free(wlst[i]);
  618. }
  619. free(wlst);
  620. suggestions.TrimRight(m_separator);
  621. if (suggestions.IsEmpty())
  622. return;
  623. Call(SCI_AUTOCSETSEPARATOR, (WPARAM)CStringA(m_separator).GetAt(0));
  624. Call(SCI_AUTOCSETTYPESEPARATOR, (WPARAM)m_typeSeparator);
  625. Call(SCI_AUTOCSETDROPRESTOFWORD, 1);
  626. Call(SCI_AUTOCSHOW, 0, (LPARAM)(LPCSTR)StringForControl(suggestions));
  627. return;
  628. }
  629. free(wlst);
  630. }
  631. void CSciEdit::DoAutoCompletion(int nMinPrefixLength)
  632. {
  633. if (m_autolist.empty())
  634. return;
  635. if (Call(SCI_AUTOCACTIVE))
  636. return;
  637. CString word = GetWordUnderCursor();
  638. if (word.GetLength() < nMinPrefixLength)
  639. return; //don't auto complete yet, word is too short
  640. int pos = (int)Call(SCI_GETCURRENTPOS);
  641. if (pos != Call(SCI_WORDENDPOSITION, pos, TRUE))
  642. return; //don't auto complete if we're not at the end of a word
  643. CString sAutoCompleteList;
  644. std::vector<CString> words;
  645. pos = word.Find('-');
  646. CString wordLower = word;
  647. wordLower.MakeLower();
  648. CString wordHigher = word;
  649. wordHigher.MakeUpper();
  650. words.push_back(wordLower);
  651. words.push_back(wordHigher);
  652. if (pos >= 0)
  653. {
  654. CString s = wordLower.Left(pos);
  655. if (s.GetLength() >= nMinPrefixLength)
  656. words.push_back(s);
  657. s = wordLower.Mid(pos+1);
  658. if (s.GetLength() >= nMinPrefixLength)
  659. words.push_back(s);
  660. s = wordHigher.Left(pos);
  661. if (s.GetLength() >= nMinPrefixLength)
  662. words.push_back(wordHigher.Left(pos));
  663. s = wordHigher.Mid(pos+1);
  664. if (s.GetLength() >= nMinPrefixLength)
  665. words.push_back(wordHigher.Mid(pos+1));
  666. }
  667. std::map<CString, int> wordset;
  668. for (const auto& w : words)
  669. {
  670. for (auto lowerit = m_autolist.lower_bound(w);
  671. lowerit != m_autolist.end(); ++lowerit)
  672. {
  673. int compare = w.CompareNoCase(lowerit->first.Left(w.GetLength()));
  674. if (compare>0)
  675. continue;
  676. else if (compare == 0)
  677. {
  678. wordset.insert(std::make_pair(lowerit->first, lowerit->second));
  679. }
  680. else
  681. {
  682. break;
  683. }
  684. }
  685. }
  686. for (const auto& w : wordset)
  687. sAutoCompleteList.AppendFormat(_T("%s%c%d%c"), w.first, m_typeSeparator, w.second, m_separator);
  688. sAutoCompleteList.TrimRight(m_separator);
  689. if (sAutoCompleteList.IsEmpty())
  690. return;
  691. Call(SCI_AUTOCSETSEPARATOR, (WPARAM)CStringA(m_separator).GetAt(0));
  692. Call(SCI_AUTOCSETTYPESEPARATOR, (WPARAM)m_typeSeparator);
  693. Call(SCI_AUTOCSHOW, word.GetLength(), (LPARAM)(LPCSTR)StringForControl(sAutoCompleteList));
  694. }
  695. BOOL CSciEdit::OnChildNotify(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pLResult)
  696. {
  697. if (message != WM_NOTIFY)
  698. return CWnd::OnChildNotify(message, wParam, lParam, pLResult);
  699. LPNMHDR lpnmhdr = (LPNMHDR) lParam;
  700. SCNotification * lpSCN = (SCNotification *)lParam;
  701. if(lpnmhdr->hwndFrom==m_hWnd)
  702. {
  703. switch(lpnmhdr->code)
  704. {
  705. case SCN_CHARADDED:
  706. {
  707. if ((lpSCN->ch < 32)&&(lpSCN->ch != 13)&&(lpSCN->ch != 10))
  708. Call(SCI_DELETEBACK);
  709. else
  710. {
  711. DoAutoCompletion(m_nAutoCompleteMinChars);
  712. }
  713. return TRUE;
  714. }
  715. break;
  716. case SCN_AUTOCSELECTION:
  717. {
  718. CString text = StringFromControl(lpSCN->text);
  719. if (m_autolist[text] == AUTOCOMPLETE_SNIPPET)
  720. {
  721. Call(SCI_AUTOCCANCEL);
  722. for (INT_PTR handlerindex = 0; handlerindex < m_arContextHandlers.GetCount(); ++handlerindex)
  723. {
  724. CSciEditContextMenuInterface * pHandler = m_arContextHandlers.GetAt(handlerindex);
  725. pHandler->HandleSnippet(m_autolist[text], text, this);
  726. }
  727. }
  728. return TRUE;
  729. }
  730. case SCN_STYLENEEDED:
  731. {
  732. int startstylepos = (int)Call(SCI_GETENDSTYLED);
  733. int endstylepos = ((SCNotification *)lpnmhdr)->position;
  734. MarkEnteredBugID(startstylepos, endstylepos);
  735. if (m_bDoStyle)
  736. StyleEnteredText(startstylepos, endstylepos);
  737. StyleURLs(startstylepos, endstylepos);
  738. CheckSpelling();
  739. WrapLines(startstylepos, endstylepos);
  740. return TRUE;
  741. }
  742. break;
  743. case SCN_DWELLSTART:
  744. case SCN_HOTSPOTCLICK:
  745. {
  746. TEXTRANGEA textrange;
  747. textrange.chrg.cpMin = lpSCN->position;
  748. textrange.chrg.cpMax = lpSCN->position;
  749. DWORD style = GetStyleAt(lpSCN->position);
  750. if (style != STYLE_ISSUEBOLDITALIC && style != STYLE_URL)
  751. break;
  752. while (GetStyleAt(textrange.chrg.cpMin - 1) == style)
  753. --textrange.chrg.cpMin;
  754. while (GetStyleAt(textrange.chrg.cpMax + 1) == style)
  755. ++textrange.chrg.cpMax;
  756. ++textrange.chrg.cpMax;
  757. std::unique_ptr<char[]> textbuffer(new char[textrange.chrg.cpMax - textrange.chrg.cpMin + 1]);
  758. textrange.lpstrText = textbuffer.get();
  759. Call(SCI_GETTEXTRANGE, 0, (LPARAM)&textrange);
  760. CString url;
  761. if (style == STYLE_URL)
  762. url = StringFromControl(textbuffer.get());
  763. else
  764. {
  765. url = m_sUrl;
  766. url.Replace(L"%BUGID%", StringFromControl(textbuffer.get()));
  767. }
  768. if (!url.IsEmpty())
  769. {
  770. if (lpnmhdr->code == SCN_HOTSPOTCLICK)
  771. ShellExecute(GetParent()->GetSafeHwnd(), _T("open"), url, NULL, NULL, SW_SHOWDEFAULT);
  772. else
  773. {
  774. CStringA sTextA = StringForControl(url);
  775. Call(SCI_CALLTIPSHOW, lpSCN->position + 3, (LPARAM)(LPCSTR)sTextA);
  776. }
  777. }
  778. }
  779. break;
  780. case SCN_DWELLEND:
  781. Call(SCI_CALLTIPCANCEL);
  782. break;
  783. }
  784. }
  785. return CWnd::OnChildNotify(message, wParam, lParam, pLResult);
  786. }
  787. BEGIN_MESSAGE_MAP(CSciEdit, CWnd)
  788. ON_WM_KEYDOWN()
  789. ON_WM_CONTEXTMENU()
  790. END_MESSAGE_MAP()
  791. void CSciEdit::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
  792. {
  793. switch (nChar)
  794. {
  795. case (VK_ESCAPE):
  796. {
  797. if ((Call(SCI_AUTOCACTIVE)==0)&&(Call(SCI_CALLTIPACTIVE)==0))
  798. ::SendMessage(GetParent()->GetSafeHwnd(), WM_CLOSE, 0, 0);
  799. }
  800. break;
  801. }
  802. CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
  803. }
  804. BOOL CSciEdit::PreTranslateMessage(MSG* pMsg)
  805. {
  806. if (pMsg->message == WM_KEYDOWN)
  807. {
  808. switch (pMsg->wParam)
  809. {
  810. case VK_SPACE:
  811. {
  812. if (GetKeyState(VK_CONTROL) & 0x8000)
  813. {
  814. DoAutoCompletion(1);
  815. return TRUE;
  816. }
  817. }
  818. break;
  819. case VK_TAB:
  820. // The TAB cannot be handled in OnKeyDown because it is too late by then.
  821. {
  822. if (GetKeyState(VK_CONTROL)&0x8000)
  823. {
  824. //Ctrl-Tab was pressed, this means we should provide the user with
  825. //a list of possible spell checking alternatives to the word under
  826. //the cursor
  827. SuggestSpellingAlternatives();
  828. return TRUE;
  829. }
  830. else if (!Call(SCI_AUTOCACTIVE))
  831. {
  832. ::PostMessage(GetParent()->GetSafeHwnd(), WM_NEXTDLGCTL, GetKeyState(VK_SHIFT)&0x8000, 0);
  833. return TRUE;
  834. }
  835. }
  836. break;
  837. }
  838. }
  839. return CWnd::PreTranslateMessage(pMsg);
  840. }
  841. void CSciEdit::OnContextMenu(CWnd* /*pWnd*/, CPoint point)
  842. {
  843. int anchor = (int)Call(SCI_GETANCHOR);
  844. int currentpos = (int)Call(SCI_GETCURRENTPOS);
  845. int selstart = (int)Call(SCI_GETSELECTIONSTART);
  846. int selend = (int)Call(SCI_GETSELECTIONEND);
  847. int pointpos = 0;
  848. if ((point.x == -1) && (point.y == -1))
  849. {
  850. CRect rect;
  851. GetClientRect(&rect);
  852. ClientToScreen(&rect);
  853. point = rect.CenterPoint();
  854. pointpos = (int)Call(SCI_GETCURRENTPOS);
  855. }
  856. else
  857. {
  858. // change the cursor position to the point where the user
  859. // right-clicked.
  860. CPoint clientpoint = point;
  861. ScreenToClient(&clientpoint);
  862. pointpos = (int)Call(SCI_POSITIONFROMPOINT, clientpoint.x, clientpoint.y);
  863. }
  864. CString sMenuItemText;
  865. CMenu popup;
  866. bool bRestoreCursor = true;
  867. if (popup.CreatePopupMenu())
  868. {
  869. bool bCanUndo = !!Call(SCI_CANUNDO);
  870. bool bCanRedo = !!Call(SCI_CANREDO);
  871. bool bHasSelection = (selend-selstart > 0);
  872. bool bCanPaste = !!Call(SCI_CANPASTE);
  873. bool bIsReadOnly = !!Call(SCI_GETREADONLY);
  874. UINT uEnabledMenu = MF_STRING | MF_ENABLED;
  875. UINT uDisabledMenu = MF_STRING | MF_GRAYED;
  876. // find the word under the cursor
  877. CString sWord;
  878. if (pointpos)
  879. {
  880. // setting the cursor clears the selection
  881. Call(SCI_SETANCHOR, pointpos);
  882. Call(SCI_SETCURRENTPOS, pointpos);
  883. sWord = GetWordUnderCursor();
  884. // restore the selection
  885. Call(SCI_SETSELECTIONSTART, selstart);
  886. Call(SCI_SETSELECTIONEND, selend);
  887. }
  888. else
  889. sWord = GetWordUnderCursor();
  890. CStringA worda = CStringA(sWord);
  891. int nCorrections = 1;
  892. bool bSpellAdded = false;
  893. // check if the word under the cursor is spelled wrong
  894. if ((pChecker)&&(!worda.IsEmpty()) && !bIsReadOnly)
  895. {
  896. char ** wlst = nullptr;
  897. // get the spell suggestions
  898. int ns = pChecker->suggest(&wlst,worda);
  899. if (ns > 0)
  900. {
  901. // add the suggestions to the context menu
  902. for (int i=0; i < ns; i++)
  903. {
  904. bSpellAdded = true;
  905. CString sug = CString(wlst[i]);
  906. popup.InsertMenu((UINT)-1, 0, nCorrections++, sug);
  907. free(wlst[i]);
  908. }
  909. free(wlst);
  910. }
  911. else
  912. free(wlst);
  913. }
  914. // only add a separator if spelling correction suggestions were added
  915. if (bSpellAdded)
  916. popup.AppendMenu(MF_SEPARATOR);
  917. // also allow the user to add the word to the custom dictionary so
  918. // it won't show up as misspelled anymore
  919. if ((sWord.GetLength()<PDICT_MAX_WORD_LENGTH)&&((pChecker)&&(m_autolist.find(sWord) == m_autolist.end())&&(!pChecker->spell(worda)))&&
  920. (!_istdigit(sWord.GetAt(0)))&&(!m_personalDict.FindWord(sWord)) && !bIsReadOnly)
  921. {
  922. sMenuItemText.Format(IDS_SCIEDIT_ADDWORD, sWord);
  923. popup.AppendMenu(uEnabledMenu, SCI_ADDWORD, sMenuItemText);
  924. // another separator
  925. popup.AppendMenu(MF_SEPARATOR);
  926. }
  927. // add the 'default' entries
  928. sMenuItemText.LoadString(IDS_SCIEDIT_UNDO);
  929. popup.AppendMenu(bCanUndo ? uEnabledMenu : uDisabledMenu, SCI_UNDO, sMenuItemText);
  930. sMenuItemText.LoadString(IDS_SCIEDIT_REDO);
  931. popup.AppendMenu(bCanRedo ? uEnabledMenu : uDisabledMenu, SCI_REDO, sMenuItemText);
  932. popup.AppendMenu(MF_SEPARATOR);
  933. sMenuItemText.LoadString(IDS_SCIEDIT_CUT);
  934. popup.AppendMenu(!bIsReadOnly && bHasSelection ? uEnabledMenu : uDisabledMenu, SCI_CUT, sMenuItemText);
  935. sMenuItemText.LoadString(IDS_SCIEDIT_COPY);
  936. popup.AppendMenu(bHasSelection ? uEnabledMenu : uDisabledMenu, SCI_COPY, sMenuItemText);
  937. sMenuItemText.LoadString(IDS_SCIEDIT_PASTE);
  938. popup.AppendMenu(bCanPaste ? uEnabledMenu : uDisabledMenu, SCI_PASTE, sMenuItemText);
  939. popup.AppendMenu(MF_SEPARATOR);
  940. sMenuItemText.LoadString(IDS_SCIEDIT_SELECTALL);
  941. popup.AppendMenu(uEnabledMenu, SCI_SELECTALL, sMenuItemText);
  942. popup.AppendMenu(MF_SEPARATOR);
  943. sMenuItemText.LoadString(IDS_SCIEDIT_SPLITLINES);
  944. popup.AppendMenu(bHasSelection ? uEnabledMenu : uDisabledMenu, SCI_LINESSPLIT, sMenuItemText);
  945. if (m_arContextHandlers.GetCount() > 0)
  946. popup.AppendMenu(MF_SEPARATOR);
  947. int nCustoms = nCorrections;
  948. // now add any custom context menus
  949. for (INT_PTR handlerindex = 0; handlerindex < m_arContextHandlers.GetCount(); ++handlerindex)
  950. {
  951. CSciEditContextMenuInterface * pHandler = m_arContextHandlers.GetAt(handlerindex);
  952. pHandler->InsertMenuItems(popup, nCustoms);
  953. }
  954. #if THESAURUS
  955. if (nCustoms > nCorrections)
  956. {
  957. // custom menu entries present, so add another separator
  958. popup.AppendMenu(MF_SEPARATOR);
  959. }
  960. // add found thesauri to sub menu's
  961. CMenu thesaurs;
  962. int nThesaurs = 0;
  963. CPtrArray menuArray;
  964. if (thesaurs.CreatePopupMenu())
  965. {
  966. if ((pThesaur)&&(!worda.IsEmpty()))
  967. {
  968. mentry * pmean;
  969. worda.MakeLower();
  970. int count = pThesaur->Lookup(worda, worda.GetLength(),&pmean);
  971. if (count)
  972. {
  973. mentry * pm = pmean;
  974. for (int i=0; i < count; i++)
  975. {
  976. CMenu * submenu = new CMenu();
  977. menuArray.Add(submenu);
  978. submenu->CreateMenu();
  979. for (int j=0; j < pm->count; j++)
  980. {
  981. CString sug = CString(pm->psyns[j]);
  982. submenu->InsertMenu((UINT)-1, 0, nCorrections + nCustoms + (nThesaurs++), sug);
  983. }
  984. thesaurs.InsertMenu((UINT)-1, MF_POPUP, (UINT_PTR)(submenu->m_hMenu), CString(pm->defn));
  985. pm++;
  986. }
  987. }
  988. if ((count > 0)&&(point.x >= 0))
  989. {
  990. #ifdef IDS_SPELLEDIT_THESAURUS
  991. sMenuItemText.LoadString(IDS_SPELLEDIT_THESAURUS);
  992. popup.InsertMenu((UINT)-1, MF_POPUP, (UINT_PTR)thesaurs.m_hMenu, sMenuItemText);
  993. #else
  994. popup.InsertMenu((UINT)-1, MF_POPUP, (UINT_PTR)thesaurs.m_hMenu, _T("Thesaurus"));
  995. #endif
  996. nThesaurs = nCustoms;
  997. }
  998. else
  999. {
  1000. sMenuItemText.LoadString(IDS_SPELLEDIT_NOTHESAURUS);
  1001. popup.AppendMenu(MF_DISABLED | MF_GRAYED | MF_STRING, 0, sMenuItemText);
  1002. }
  1003. pThesaur->CleanUpAfterLookup(&pmean, count);
  1004. }
  1005. else
  1006. {
  1007. sMenuItemText.LoadString(IDS_SPELLEDIT_NOTHESAURUS);
  1008. popup.AppendMenu(MF_DISABLED | MF_GRAYED | MF_STRING, 0, sMenuItemText);
  1009. }
  1010. }
  1011. #endif
  1012. int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);
  1013. switch (cmd)
  1014. {
  1015. case 0:
  1016. break; // no command selected
  1017. case SCI_SELECTALL:
  1018. bRestoreCursor = false;
  1019. // fall through
  1020. case SCI_UNDO:
  1021. case SCI_REDO:
  1022. case SCI_CUT:
  1023. case SCI_COPY:
  1024. case SCI_PASTE:
  1025. Call(cmd);
  1026. break;
  1027. case SCI_ADDWORD:
  1028. m_personalDict.AddWord(sWord);
  1029. CheckSpelling();
  1030. break;
  1031. case SCI_LINESSPLIT:
  1032. {
  1033. int marker = (int)(Call(SCI_GETEDGECOLUMN) * Call(SCI_TEXTWIDTH, 0, (LPARAM)" "));
  1034. if (marker)
  1035. {
  1036. Call(SCI_TARGETFROMSELECTION);
  1037. Call(SCI_LINESJOIN);
  1038. Call(SCI_LINESSPLIT, marker);
  1039. }
  1040. }
  1041. break;
  1042. default:
  1043. if (cmd < nCorrections)
  1044. {
  1045. Call(SCI_SETANCHOR, pointpos);
  1046. Call(SCI_SETCURRENTPOS, pointpos);
  1047. GetWordUnderCursor(true);
  1048. CString temp;
  1049. popup.GetMenuString(cmd, temp, 0);
  1050. // setting the cursor clears the selection
  1051. Call(SCI_REPLACESEL, 0, (LPARAM)(LPCSTR)StringForControl(temp));
  1052. }
  1053. else if (cmd < (nCorrections+nCustoms))
  1054. {
  1055. for (INT_PTR handlerindex = 0; handlerindex < m_arContextHandlers.GetCount(); ++handlerindex)
  1056. {
  1057. CSciEditContextMenuInterface * pHandler = m_arContextHandlers.GetAt(handlerindex);
  1058. if (pHandler->HandleMenuItemClick(cmd, this))
  1059. break;
  1060. }
  1061. }
  1062. #if THESAURUS
  1063. else if (cmd <= (nThesaurs+nCorrections+nCustoms))
  1064. {
  1065. Call(SCI_SETANCHOR, pointpos);
  1066. Call(SCI_SETCURRENTPOS, pointpos);
  1067. GetWordUnderCursor(true);
  1068. CString temp;
  1069. thesaurs.GetMenuString(cmd, temp, 0);
  1070. Call(SCI_REPLACESEL, 0, (LPARAM)(LPCSTR)StringForControl(temp));
  1071. }
  1072. #endif
  1073. }
  1074. #ifdef THESAURUS
  1075. for (INT_PTR index = 0; index < menuArray.GetCount(); ++index)
  1076. {
  1077. CMenu * pMenu = (CMenu*)menuArray[index];
  1078. delete pMenu;
  1079. }
  1080. #endif
  1081. }
  1082. if (bRestoreCursor)
  1083. {
  1084. // restore the anchor and cursor position
  1085. Call(SCI_SETCURRENTPOS, currentpos);
  1086. Call(SCI_SETANCHOR, anchor);
  1087. }
  1088. }
  1089. bool CSciEdit::StyleEnteredText(int startstylepos, int endstylepos)
  1090. {
  1091. bool bStyled = false;
  1092. const int line = (int)Call(SCI_LINEFROMPOSITION, startstylepos);
  1093. const int line_number_end = (int)Call(SCI_LINEFROMPOSITION, endstylepos);
  1094. for (int line_number = line; line_number <= line_number_end; ++line_number)
  1095. {
  1096. int offset = (int)Call(SCI_POSITIONFROMLINE, line_number);
  1097. int line_len = (int)Call(SCI_LINELENGTH, line_number);
  1098. std::unique_ptr<char[]> linebuffer(new char[line_len+1]);
  1099. Call(SCI_GETLINE, line_number, (LPARAM)linebuffer.get());
  1100. linebuffer[line_len] = 0;
  1101. int start = 0;
  1102. int end = 0;
  1103. while (FindStyleChars(linebuffer.get(), '*', start, end))
  1104. {
  1105. Call(SCI_STARTSTYLING, start+offset, STYLE_MASK);
  1106. Call(SCI_SETSTYLING, end-start, STYLE_BOLD);
  1107. bStyled = true;
  1108. start = end;
  1109. }
  1110. start = 0;
  1111. end = 0;
  1112. while (FindStyleChars(linebuffer.get(), '^', start, end))
  1113. {
  1114. Call(SCI_STARTSTYLING, start+offset, STYLE_MASK);
  1115. Call(SCI_SETSTYLING, end-start, STYLE_ITALIC);
  1116. bStyled = true;
  1117. start = end;
  1118. }
  1119. start = 0;
  1120. end = 0;
  1121. while (FindStyleChars(linebuffer.get(), '_', start, end))
  1122. {
  1123. Call(SCI_STARTSTYLING, start+offset, STYLE_MASK);
  1124. Call(SCI_SETSTYLING, end-start, STYLE_UNDERLINED);
  1125. bStyled = true;
  1126. start = end;
  1127. }
  1128. }
  1129. return bStyled;
  1130. }
  1131. bool CSciEdit::WrapLines(int startpos, int endpos)
  1132. {
  1133. int markerX = (int)(Call(SCI_GETEDGECOLUMN) * Call(SCI_TEXTWIDTH, 0, (LPARAM)" "));
  1134. if (markerX)
  1135. {
  1136. Call(SCI_SETTARGETSTART, startpos);
  1137. Call(SCI_SETTARGETEND, endpos);
  1138. Call(SCI_LINESSPLIT, markerX);
  1139. return true;
  1140. }
  1141. return false;
  1142. }
  1143. void CSciEdit::AdvanceUTF8(const char * str, int& pos)
  1144. {
  1145. if ((str[pos] & 0xE0)==0xC0)
  1146. {
  1147. // utf8 2-byte sequence
  1148. pos += 2;
  1149. }
  1150. else if ((str[pos] & 0xF0)==0xE0)
  1151. {
  1152. // utf8 3-byte sequence
  1153. pos += 3;
  1154. }
  1155. else if ((str[pos] & 0xF8)==0xF0)
  1156. {
  1157. // utf8 4-byte sequence
  1158. pos += 4;
  1159. }
  1160. else
  1161. pos++;
  1162. }
  1163. bool CSciEdit::FindStyleChars(const char * line, char styler, int& start, int& end)
  1164. {
  1165. int i=0;
  1166. int u=0;
  1167. while (i < start)
  1168. {
  1169. AdvanceUTF8(line, i);
  1170. u++;
  1171. }
  1172. bool bFoundMarker = false;
  1173. CString sULine = CUnicodeUtils::GetUnicode(line);
  1174. // find a starting marker
  1175. while (line[i] != 0)
  1176. {
  1177. if (line[i] == styler)
  1178. {
  1179. if ((line[i+1]!=0)&&(IsCharAlphaNumeric(sULine[u+1]))&&
  1180. (((u>0)&&(!IsCharAlphaNumeric(sULine[u-1]))) || (u==0)))
  1181. {
  1182. start = i+1;
  1183. AdvanceUTF8(line, i);
  1184. u++;
  1185. bFoundMarker = true;
  1186. break;
  1187. }
  1188. }
  1189. AdvanceUTF8(line, i);
  1190. u++;
  1191. }
  1192. if (!bFoundMarker)
  1193. return false;
  1194. // find ending marker
  1195. bFoundMarker = false;
  1196. while (line[i] != 0)
  1197. {
  1198. if (line[i] == styler)
  1199. {
  1200. if ((IsCharAlphaNumeric(sULine[u-1]))&&
  1201. ((((u+1)<sULine.GetLength())&&(!IsCharAlphaNumeric(sULine[u+1]))) || ((u+1) == sULine.GetLength()))
  1202. )
  1203. {
  1204. end = i;
  1205. i++;
  1206. bFoundMarker = true;
  1207. break;
  1208. }
  1209. }
  1210. AdvanceUTF8(line, i);
  1211. u++;
  1212. }
  1213. return bFoundMarker;
  1214. }
  1215. BOOL CSciEdit::MarkEnteredBugID(int startstylepos, int endstylepos)
  1216. {
  1217. if (m_sCommand.IsEmpty())
  1218. return FALSE;
  1219. // get the text between the start and end position we have to style
  1220. const int line_number = (int)Call(SCI_LINEFROMPOSITION, startstylepos);
  1221. int start_pos = (int)Call(SCI_POSITIONFROMLINE, (WPARAM)line_number);
  1222. int end_pos = endstylepos;
  1223. if (start_pos == end_pos)
  1224. return FALSE;
  1225. if (start_pos > end_pos)
  1226. {
  1227. int switchtemp = start_pos;
  1228. start_pos = end_pos;
  1229. end_pos = switchtemp;
  1230. }
  1231. std::unique_ptr<char[]> textbuffer(new char[end_pos - start_pos + 2]);
  1232. TEXTRANGEA textrange;
  1233. textrange.lpstrText = textbuffer.get();
  1234. textrange.chrg.cpMin = start_pos;
  1235. textrange.chrg.cpMax = end_pos;
  1236. Call(SCI_GETTEXTRANGE, 0, (LPARAM)&textrange);
  1237. CStringA msg = CStringA(textbuffer.get());
  1238. Call(SCI_STARTSTYLING, start_pos, STYLE_MASK);
  1239. try
  1240. {
  1241. if (!m_sBugID.IsEmpty())
  1242. {
  1243. // match with two regex strings (without grouping!)
  1244. const std::tr1::regex regCheck(m_sCommand);
  1245. const std::tr1::regex regBugID(m_sBugID);
  1246. const std::tr1::sregex_iterator end;
  1247. std::string s = msg;
  1248. LONG pos = 0;
  1249. // note:
  1250. // if start_pos is 0, we're styling from the beginning and let the ^ char match the beginning of the line
  1251. // that way, the ^ matches the very beginning of the log message and not the beginning of further lines.
  1252. // problem is: this only works *while* entering log messages. If a log message is pasted in whole or
  1253. // multiple lines are pasted, start_pos can be 0 and styling goes over multiple lines. In that case, those
  1254. // additional line starts also match ^
  1255. 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)
  1256. {
  1257. // clear the styles up to the match position
  1258. Call(SCI_SETSTYLING, it->position(0)-pos, STYLE_DEFAULT);
  1259. // (*it)[0] is the matched string
  1260. std::string matchedString = (*it)[0];
  1261. LONG matchedpos = 0;
  1262. for (std::tr1::sregex_iterator it2(matchedString.begin(), matchedString.end(), regBugID); it2 != end; ++it2)
  1263. {
  1264. ATLTRACE("matched id : %s\n", std::string((*it2)[0]).c_str());
  1265. // bold style up to the id match
  1266. ATLTRACE("position = %ld\n", it2->position(0));
  1267. if (it2->position(0))
  1268. Call(SCI_SETSTYLING, it2->position(0) - matchedpos, STYLE_ISSUEBOLD);
  1269. // bold and recursive style for the bug ID itself
  1270. if ((*it2)[0].str().size())
  1271. Call(SCI_SETSTYLING, (*it2)[0].str().size(), STYLE_ISSUEBOLDITALIC);
  1272. matchedpos = (LONG)(it2->position(0) + (*it2)[0].str().size());
  1273. }
  1274. if ((matchedpos)&&(matchedpos < (LONG)matchedString.size()))
  1275. {
  1276. Call(SCI_SETSTYLING, matchedString.size() - matchedpos, STYLE_ISSUEBOLD);
  1277. }
  1278. pos = (LONG)(it->position(0) + matchedString.size());
  1279. }
  1280. // bold style for the rest of the string which isn't matched
  1281. if (s.size()-pos)
  1282. Call(SCI_SETSTYLING, s.size()-pos, STYLE_DEFAULT);
  1283. }
  1284. else
  1285. {
  1286. const std::tr1::regex regCheck(m_sCommand);
  1287. const std::tr1::sregex_iterator end;
  1288. std::string s = msg;
  1289. LONG pos = 0;
  1290. for (std::tr1::sregex_iterator it(s.begin(), s.end(), regCheck); it != end; ++it)
  1291. {
  1292. // clear the styles up to the match position
  1293. if (it->position(0) - pos >= 0)
  1294. Call(SCI_SETSTYLING, it->position(0) - pos, STYLE_DEFAULT);
  1295. pos = (LONG)it->position(0);
  1296. const std::tr1::smatch match = *it;
  1297. // we define group 1 as the whole issue text and
  1298. // group 2 as the bug ID
  1299. if (match.size() >= 2)
  1300. {
  1301. ATLTRACE("matched id : %s\n", std::string(match[1]).c_str());
  1302. if (match[1].first - s.begin() - pos >= 0)
  1303. Call(SCI_SETSTYLING, match[1].first - s.begin() - pos, STYLE_ISSUEBOLD);
  1304. Call(SCI_SETSTYLING, std::string(match[1]).size(), STYLE_ISSUEBOLDITALIC);
  1305. pos = (LONG)(match[1].second-s.begin());
  1306. }
  1307. }
  1308. }
  1309. }
  1310. catch (std::exception) {}
  1311. return FALSE;
  1312. }
  1313. //similar code in AppUtils.cpp
  1314. bool CSciEdit::IsValidURLChar(unsigned char ch)
  1315. {
  1316. return isalnum(ch) ||
  1317. ch == '_' || ch == '/' || ch == ';' || ch == '?' || ch == '&' || ch == '=' ||
  1318. ch == '%' || ch == ':' || ch == '.' || ch == '#' || ch == '-' || ch == '+' ||
  1319. ch == '|' || ch == '>' || ch == '<';
  1320. }
  1321. //similar code in AppUtils.cpp
  1322. void CSciEdit::StyleURLs(int startstylepos, int endstylepos)
  1323. {
  1324. const int line_number = (int)Call(SCI_LINEFROMPOSITION, startstylepos);
  1325. startstylepos = (int)Call(SCI_POSITIONFROMLINE, (WPARAM)line_number);
  1326. int len = endstylepos - startstylepos + 1;
  1327. std::unique_ptr<char[]> textbuffer(new char[len + 1]);
  1328. TEXTRANGEA textrange;
  1329. textrange.lpstrText = textbuffer.get();
  1330. textrange.chrg.cpMin = startstylepos;
  1331. textrange.chrg.cpMax = endstylepos;
  1332. Call(SCI_GETTEXTRANGE, 0, (LPARAM)&textrange);
  1333. // we're dealing with utf8 encoded text here, which means one glyph is
  1334. // not necessarily one byte/wchar_t
  1335. // that's why we use CStringA to still get a correct char index
  1336. CStringA msg = textbuffer.get();
  1337. int starturl = -1;
  1338. for (int i = 0; i <= msg.GetLength(); AdvanceUTF8(msg, i))
  1339. {
  1340. if ((i < len) && IsValidURLChar(msg[i]))
  1341. {
  1342. if (starturl < 0)
  1343. starturl = i;
  1344. }
  1345. else
  1346. {
  1347. if (starturl >= 0)
  1348. {
  1349. bool strip = true;
  1350. if (msg[starturl] == '<' && i < len) // try to detect and do not strip URLs put within <>
  1351. {
  1352. while (starturl <= i && msg[starturl] == '<') // strip leading '<'
  1353. ++starturl;
  1354. strip = false;
  1355. i = starturl;
  1356. while (i < len && msg[i] != '\r' && msg[i] != '\n' && msg[i] != '>') // find first '>' or new line after resetting i to start position
  1357. AdvanceUTF8(msg, i);
  1358. }
  1359. if (!IsUrl(msg.Mid(starturl, i - starturl)))
  1360. {
  1361. starturl = -1;
  1362. continue;
  1363. }
  1364. int skipTrailing = 0;
  1365. 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] == '<'))
  1366. ++skipTrailing;
  1367. ASSERT(startstylepos + i - skipTrailing <= endstylepos);
  1368. Call(SCI_STARTSTYLING, startstylepos + starturl, STYLE_MASK);
  1369. Call(SCI_SETSTYLING, i - starturl - skipTrailing, STYLE_URL);
  1370. }
  1371. starturl = -1;
  1372. }
  1373. }
  1374. }
  1375. bool CSciEdit::IsUrl(const CStringA& sText)
  1376. {
  1377. if (!PathIsURLA(sText))
  1378. return false;
  1379. CStringA prefixes[] = { "http://", "https://", "git://", "ftp://", "file://", "mailto:" };
  1380. for (const CStringA& prefix : prefixes)
  1381. {
  1382. if (sText.Find(prefix) == 0 && sText.GetLength() != prefix.GetLength())
  1383. return true;
  1384. }
  1385. return false;
  1386. }
  1387. bool CSciEdit::IsUTF8(LPVOID pBuffer, size_t cb)
  1388. {
  1389. if (cb < 2)
  1390. return true;
  1391. UINT16 * pVal = (UINT16 *)pBuffer;
  1392. UINT8 * pVal2 = (UINT8 *)(pVal+1);
  1393. // scan the whole buffer for a 0x0000 sequence
  1394. // if found, we assume a binary file
  1395. for (size_t i=0; i<(cb-2); i=i+2)
  1396. {
  1397. if (0x0000 == *pVal++)
  1398. return false;
  1399. }
  1400. pVal = (UINT16 *)pBuffer;
  1401. if (*pVal == 0xFEFF)
  1402. return false;
  1403. if (cb < 3)
  1404. return false;
  1405. if (*pVal == 0xBBEF)
  1406. {
  1407. if (*pVal2 == 0xBF)
  1408. return true;
  1409. }
  1410. // check for illegal UTF8 chars
  1411. pVal2 = (UINT8 *)pBuffer;
  1412. for (size_t i=0; i<cb; ++i)
  1413. {
  1414. if ((*pVal2 == 0xC0)||(*pVal2 == 0xC1)||(*pVal2 >= 0xF5))
  1415. return false;
  1416. pVal2++;
  1417. }
  1418. pVal2 = (UINT8 *)pBuffer;
  1419. bool bUTF8 = false;
  1420. for (size_t i=0; i<(cb-3); ++i)
  1421. {
  1422. if ((*pVal2 & 0xE0)==0xC0)
  1423. {
  1424. pVal2++;i++;
  1425. if ((*pVal2 & 0xC0)!=0x80)
  1426. return false;
  1427. bUTF8 = true;
  1428. }
  1429. if ((*pVal2 & 0xF0)==0xE0)
  1430. {
  1431. pVal2++;i++;
  1432. if ((*pVal2 & 0xC0)!=0x80)
  1433. return false;
  1434. pVal2++;i++;
  1435. if ((*pVal2 & 0xC0)!=0x80)
  1436. return false;
  1437. bUTF8 = true;
  1438. }
  1439. if ((*pVal2 & 0xF8)==0xF0)
  1440. {
  1441. pVal2++;i++;
  1442. if ((*pVal2 & 0xC0)!=0x80)
  1443. return false;
  1444. pVal2++;i++;
  1445. if ((*pVal2 & 0xC0)!=0x80)
  1446. return false;
  1447. pVal2++;i++;
  1448. if ((*pVal2 & 0xC0)!=0x80)
  1449. return false;
  1450. bUTF8 = true;
  1451. }
  1452. pVal2++;
  1453. }
  1454. if (bUTF8)
  1455. return true;
  1456. return false;
  1457. }
  1458. void CSciEdit::SetAStyle(int style, COLORREF fore, COLORREF back, int size, const char *face)
  1459. {
  1460. Call(SCI_STYLESETFORE, style, fore);
  1461. Call(SCI_STYLESETBACK, style, back);
  1462. if (size >= 1)
  1463. Call(SCI_STYLESETSIZE, style, size);
  1464. if (face)
  1465. Call(SCI_STYLESETFONT, style, reinterpret_cast<LPARAM>(face));
  1466. }
  1467. void CSciEdit::SetU

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