AutoCompl.cpp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. ////////////////////////////////////////////////////////////////
  2. // VCKBASE -- August 2000
  3. // Compiles with Visual C++ 6.0, runs on Windows 98 and probably NT too.
  4. //
  5. #include "stdafx.h"
  6. #include "autocompl.h"
  7. #include "YLGL.h"
  8. //////////////////
  9. // ctor: initialize stuff to zero
  10. //
  11. CAutoComplete::CAutoComplete()
  12. {
  13. m_bIgnoreChangeMsg=0;
  14. m_iType = 0;
  15. m_idMyControl = 0;
  16. m_iCurString = 0;
  17. m_mode=0;
  18. }
  19. CAutoComplete::~CAutoComplete()
  20. {
  21. }
  22. //////////////////
  23. // Install hook. Initialize control ID and type of control based on
  24. // classname
  25. //
  26. void CAutoComplete::Init(CWnd* pWnd)
  27. {
  28. CSubclassWnd::HookWindow(pWnd->GetParent());
  29. CString sClassName;
  30. ::GetClassName(pWnd->GetSafeHwnd(), sClassName.GetBuffer(32), 32);
  31. sClassName.ReleaseBuffer();
  32. if (sClassName=="Edit") {
  33. m_iType = Edit;
  34. } else if (sClassName=="ComboBox") {
  35. m_iType = ComboBox;
  36. }
  37. m_idMyControl = pWnd->GetDlgCtrlID();
  38. }
  39. //////////////////
  40. // Scan string array for strings that match text, and add the matches
  41. // to a new array. Returns number of matches. For edit controls, only
  42. // need to find the first match, so to be efficient BOOL arg tells me
  43. // that.
  44. //
  45. UINT CAutoComplete::GetMatches(LPCTSTR pszText, CStringArray& arMatches,
  46. BOOL bFirstOnly)
  47. {
  48. arMatches.RemoveAll();
  49. int nMatch = 0;
  50. CString s=pszText;
  51. if (s.GetLength()>0) {
  52. OnFirstString();
  53. CString sMatch;
  54. while (OnNextString(sMatch)) {
  55. if (OnMatchString(s, sMatch)) {
  56. TRACE("Add %s\n",(LPCTSTR)sMatch);
  57. arMatches.Add(sMatch);
  58. nMatch++;
  59. if (bFirstOnly)
  60. break;
  61. }
  62. }
  63. }
  64. return nMatch;
  65. }
  66. //////////////////
  67. // This virtual function takes the string entered and a potential match
  68. // and returns TRUE if the strings match. Default implementation does a
  69. // normal prefix compare--but you could override, for example to ignore
  70. // 'www' at the start of either string.
  71. //
  72. BOOL CAutoComplete::OnMatchString(const CString& s, const CString& sMatch)
  73. {
  74. return s==sMatch.Left(s.GetLength());
  75. }
  76. void CAutoComplete::OnFirstString()
  77. {
  78. m_iCurString=0;
  79. }
  80. BOOL CAutoComplete::OnNextString(CString& sNext)
  81. {
  82. if (m_iCurString < m_arStrings.GetSize()) {
  83. sNext = m_arStrings[m_iCurString++];
  84. return TRUE;
  85. }
  86. sNext = (LPCTSTR)NULL;
  87. return FALSE;
  88. }
  89. //////////////////
  90. // "hook" function traps messages sent to edit control/combobox. I am
  91. // interested in EN_CHANGE or CBN_EDITCHANGE: contents of edit control
  92. // have changed.
  93. //
  94. LRESULT CAutoComplete::WindowProc(UINT msg, WPARAM wp, LPARAM lp)
  95. {
  96. if ((msg==WM_COMMAND && LOWORD(wp)==m_idMyControl) &&
  97. ((m_iType==Edit && HIWORD(wp)==EN_CHANGE) ||
  98. (m_iType==ComboBox && HIWORD(wp)==CBN_EDITCHANGE))) {
  99. // since I will be changing the contents of the control, which
  100. // will trigger more EN_CHANGE messages, turn off processing
  101. // while I have control, using m_bIgnoreChangeMsg.
  102. if (!m_bIgnoreChangeMsg++) {
  103. CString s;
  104. CWnd* pWnd = CWnd::FromHandle((HWND)lp);
  105. pWnd->GetWindowText(s);
  106. OnComplete(pWnd, s);
  107. }
  108. m_bIgnoreChangeMsg--;
  109. }
  110. return CSubclassWnd::WindowProc(msg, wp, lp);
  111. }
  112. //////////////////
  113. // This is the main function that does the completion.
  114. //
  115. void CAutoComplete::OnComplete(CWnd* pWnd, CString m_filter)
  116. {
  117. if(m_mode)
  118. {
  119. CStringArray arMatches; // strings that match
  120. m_filter.TrimLeft ();
  121. int ii;
  122. if (m_filter.GetLength()>0)
  123. {m_filter.MakeUpper ();
  124. m_posarray.RemoveAll ();
  125. int type=-1;//GetType(m_filter);
  126. if(type==1)//µç»°
  127. {
  128. for(ii=0; ii<m_pArray->GetSize (); ii++)
  129. {
  130. if(m_pArray->ElementAt (ii).ElementAt (12).Find (m_filter)!=-1 || \
  131. m_pArray->ElementAt (ii).ElementAt (13).Find (m_filter)!=-1)
  132. {
  133. m_posarray.Add (ii);
  134. arMatches.Add (m_pArray->ElementAt (ii).ElementAt (1)+m_pArray->ElementAt (ii).ElementAt (2));
  135. }
  136. }
  137. }
  138. else if(type==2)//Æ´Òô
  139. {
  140. m_filter.MakeUpper ();
  141. for(ii=0; ii<m_pArray->GetSize (); ii++)
  142. {
  143. if(m_pArray->ElementAt (ii).ElementAt (14).Find (m_filter)!=-1 || \
  144. m_pArray->ElementAt (ii).ElementAt (15).Find (m_filter)!=-1)
  145. {
  146. m_posarray.Add (ii);
  147. arMatches.Add (m_pArray->ElementAt (ii).ElementAt (1)+m_pArray->ElementAt (ii).ElementAt (2));
  148. }
  149. }
  150. }
  151. else
  152. {
  153. for(ii=0; ii<m_pArray->GetSize (); ii++)
  154. {
  155. if(m_pArray->ElementAt (ii).ElementAt (0).Find (m_filter)!=-1 ||\
  156. m_pArray->ElementAt (ii).ElementAt (1).Find (m_filter)!=-1 || \
  157. m_pArray->ElementAt (ii).ElementAt (2).Find (m_filter)!=-1 || \
  158. m_pArray->ElementAt (ii).ElementAt (3).Find (m_filter)!=-1 || \
  159. m_pArray->ElementAt (ii).ElementAt (4).Find (m_filter)!=-1 || \
  160. m_pArray->ElementAt (ii).ElementAt (5).Find (m_filter)!=-1|| \
  161. m_pArray->ElementAt (ii).ElementAt (6).Find (m_filter)!=-1|| \
  162. m_pArray->ElementAt (ii).ElementAt (7).Find (m_filter)!=-1 || \
  163. m_pArray->ElementAt (ii).ElementAt (8).Find (m_filter)!=-1 || \
  164. m_pArray->ElementAt (ii).ElementAt (9).Find (m_filter)!=-1 || \
  165. m_pArray->ElementAt (ii).ElementAt (10).Find (m_filter)!=-1 || \
  166. m_pArray->ElementAt (ii).ElementAt (11).Find (m_filter)!=-1 || \
  167. m_pArray->ElementAt (ii).ElementAt (12).Find (m_filter)!=-1 || \
  168. m_pArray->ElementAt (ii).ElementAt (13).Find (m_filter)!=-1 )
  169. {
  170. m_posarray.Add (ii);
  171. arMatches.Add (m_pArray->ElementAt (ii).ElementAt (1)+m_pArray->ElementAt (ii).ElementAt (2));
  172. }
  173. }
  174. }
  175. DoCompletion(pWnd, m_filter, arMatches);
  176. }
  177. m_sPrevious=m_filter; // remember current string
  178. }
  179. else
  180. {
  181. CStringArray arMatches; // strings that match
  182. m_filter.TrimLeft ();
  183. int ii;
  184. if (m_filter.GetLength()>0)
  185. {m_filter.MakeUpper ();
  186. m_posarray.RemoveAll ();
  187. int type=-1;//GetType(m_filter);
  188. if(type==1)//µç»°
  189. {
  190. for(ii=0; ii<m_pArray->GetSize (); ii++)
  191. {
  192. if(m_pArray->ElementAt (ii).ElementAt (12).Find (m_filter)!=-1 || \
  193. m_pArray->ElementAt (ii).ElementAt (13).Find (m_filter)!=-1)
  194. {
  195. m_posarray.Add (ii);
  196. arMatches.Add (m_pArray->ElementAt (ii).ElementAt (1)+m_pArray->ElementAt (ii).ElementAt (2));
  197. }
  198. }
  199. }
  200. else if(type==2)//Æ´Òô
  201. {
  202. m_filter.MakeUpper ();
  203. for(ii=0; ii<m_pArray->GetSize (); ii++)
  204. {
  205. if(m_pArray->ElementAt (ii).ElementAt (14).Find (m_filter)!=-1 || \
  206. m_pArray->ElementAt (ii).ElementAt (15).Find (m_filter)!=-1)
  207. {
  208. m_posarray.Add (ii);
  209. arMatches.Add (m_pArray->ElementAt (ii).ElementAt (1)+m_pArray->ElementAt (ii).ElementAt (2));
  210. }
  211. }
  212. }
  213. else
  214. {
  215. for(ii=0; ii<m_pArray->GetSize (); ii++)
  216. {
  217. if(m_pArray->ElementAt (ii).ElementAt (0).Find (m_filter)!=-1 ||\
  218. m_pArray->ElementAt (ii).ElementAt (1).Find (m_filter)!=-1 || \
  219. m_pArray->ElementAt (ii).ElementAt (2).Find (m_filter)!=-1 || \
  220. m_pArray->ElementAt (ii).ElementAt (7).Find (m_filter)!=-1 || \
  221. m_pArray->ElementAt (ii).ElementAt (8).Find (m_filter)!=-1 || \
  222. m_pArray->ElementAt (ii).ElementAt (9).Find (m_filter)!=-1|| \
  223. m_pArray->ElementAt (ii).ElementAt (10).Find (m_filter)!=-1|| \
  224. m_pArray->ElementAt (ii).ElementAt (12).Find (m_filter)!=-1 || \
  225. m_pArray->ElementAt (ii).ElementAt (13).Find (m_filter)!=-1 || \
  226. m_pArray->ElementAt (ii).ElementAt (14).Find (m_filter)!=-1 || \
  227. m_pArray->ElementAt (ii).ElementAt (15).Find (m_filter)!=-1 )
  228. {
  229. m_posarray.Add (ii);
  230. arMatches.Add (m_pArray->ElementAt (ii).ElementAt (1)+m_pArray->ElementAt (ii).ElementAt (2));
  231. }
  232. }
  233. }
  234. DoCompletion(pWnd, m_filter, arMatches);
  235. }
  236. m_sPrevious=m_filter; // remember current string
  237. }
  238. }
  239. void CAutoComplete::DoCompletion(CWnd* pWnd, CString s, //m_pArray
  240. const CStringArray& arMatches)
  241. {
  242. if (m_iType==ComboBox) {
  243. // This cast is technically incorrect, but a standard MFC trick.
  244. CComboBox* pComboBox = (CComboBox*)pWnd;
  245. // update dropdown to reflect possible matches
  246. pComboBox->ResetContent();
  247. for (int i=0; i<arMatches.GetSize(); i++) {
  248. pComboBox->AddString(arMatches[i]);
  249. }
  250. // user arrow cursor so user can select
  251. ::SetCursor(LoadCursor(NULL,MAKEINTRESOURCE(IDC_ARROW)));
  252. // show dropdown
  253. pComboBox->ShowDropDown();
  254. // pComboBox->SetWindowText(IgnoreCompletion(s) ? s : arMatches[0]);
  255. pComboBox->SetWindowText(s);
  256. pComboBox->SetEditSel(s.GetLength(),-1);
  257. } else if (m_iType==Edit && !IgnoreCompletion(s)) {
  258. // This cast is technically incorrect, but a standard MFC trick.
  259. CEdit* pEdit = (CEdit*)pWnd;
  260. pEdit->SetWindowText(arMatches[0]);
  261. pEdit->SetSel(s.GetLength(),-1);
  262. }
  263. }
  264. //////////////////
  265. // This function is used to turn off the completion feature when the
  266. // user presses Backspace to delete a character typed. In that case,
  267. // the current string (s) will match the last (previously) entered
  268. // string. If this is the case, don't do the completion. For example,
  269. // if user types 'foo' and this causes me to complete to 'foobar', with
  270. // 'bar' highlighted, I don't want to complete to foobar again if the
  271. // user presses Backspace or Delete to delete 'bar'. Instead, 'foo'
  272. // should remain--and likewise if the user keeps pressing backspace.
  273. // This is one of the only functions I have ever written where the
  274. // explanation is longer than the code itself!
  275. //
  276. BOOL CAutoComplete::IgnoreCompletion(CString s)
  277. {
  278. return s==m_sPrevious.Left(s.GetLength());
  279. }