PageRenderTime 51ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/src/Utils/MiscUI/SciEdit.cpp

http://tortoisesvn.googlecode.com/
C++ | 1383 lines | 1182 code | 101 blank | 100 comment | 254 complexity | ed68f131702d393cab3fd9d845bbe0a3 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, GPL-3.0, MPL-2.0-no-copyleft-exception, GPL-2.0, LGPL-2.0, LGPL-2.1, BSD-3-Clause, Apache-2.0, LGPL-3.0

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

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

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