QListCtrl.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017
  1. // QListCtrl.cpp : implementation file
  2. //
  3. #include "stdafx.h"
  4. #include "CP_Main.h"
  5. #include "QListCtrl.h"
  6. #include "ProcessPaste.h"
  7. #include "BitmapHelper.h"
  8. #ifdef _DEBUG
  9. #define new DEBUG_NEW
  10. #undef THIS_FILE
  11. static char THIS_FILE[] = __FILE__;
  12. #endif
  13. #define ROW_BOTTOM_BORDER 2
  14. #define ROW_LEFT_BORDER 3
  15. #define COLOR_SHADOW RGB(245, 245, 245)
  16. #define DUMMY_COL_WIDTH 1
  17. /////////////////////////////////////////////////////////////////////////////
  18. // CQListCtrl
  19. CQListCtrl::CQListCtrl()
  20. {
  21. m_pchTip = NULL;
  22. m_pwchTip = NULL;
  23. LOGFONT lf;
  24. lf.lfHeight = -9;
  25. lf.lfWidth = 0;
  26. lf.lfEscapement = 0;
  27. lf.lfOrientation = 0;
  28. lf.lfWeight = FW_LIGHT;
  29. lf.lfItalic = FALSE;
  30. lf.lfUnderline = FALSE;
  31. lf.lfStrikeOut = FALSE;
  32. lf.lfCharSet = ANSI_CHARSET;
  33. lf.lfOutPrecision = OUT_STRING_PRECIS;
  34. lf.lfClipPrecision = CLIP_STROKE_PRECIS;
  35. lf.lfQuality = DEFAULT_QUALITY;
  36. lf.lfPitchAndFamily = VARIABLE_PITCH | FF_DONTCARE;
  37. lstrcpy(lf.lfFaceName, "Small Font");
  38. m_SmallFont = ::CreateFontIndirect(&lf);
  39. m_bShowTextForFirstTenHotKeys = true;
  40. m_bStartTop = true;
  41. m_pToolTip = NULL;
  42. }
  43. CQListCtrl::~CQListCtrl()
  44. {
  45. if(m_pchTip != NULL)
  46. delete m_pchTip;
  47. if(m_pwchTip != NULL)
  48. delete m_pwchTip;
  49. if( m_SmallFont )
  50. ::DeleteObject( m_SmallFont );
  51. DestroyAndCreateAccelerator(FALSE);
  52. m_Font.DeleteObject();
  53. }
  54. // returns the position 1-10 if the index is in the FirstTen block else -1
  55. int CQListCtrl::GetFirstTenNum( int index )
  56. {
  57. // set firstTenNum to the first ten number (1-10) corresponding to the given index
  58. int firstTenNum = -1; // -1 means that nItem is not in the FirstTen block.
  59. int count = GetItemCount();
  60. if( m_bStartTop )
  61. {
  62. if( 0 <= index && index <= 9 )
  63. firstTenNum = index + 1;
  64. }
  65. else // we are starting at the bottom and going up
  66. {
  67. int idxStartFirstTen = count-10; // start of the FirstTen block
  68. // if index is within the FirstTen block
  69. if( idxStartFirstTen <= index && index < count )
  70. firstTenNum = count - index;
  71. }
  72. return firstTenNum;
  73. }
  74. // returns the list index corresponding to the given FirstTen position number.
  75. // (ret < 0) means that "num" is not in the FirstTen block
  76. int CQListCtrl::GetFirstTenIndex( int num )
  77. {
  78. if( num <= 0 || num > 10 )
  79. return -1;
  80. if( m_bStartTop )
  81. return num-1;
  82. // else we are starting at the bottom and going up
  83. int count = GetItemCount();
  84. return count - num;
  85. }
  86. BEGIN_MESSAGE_MAP(CQListCtrl, CListCtrl)
  87. //{{AFX_MSG_MAP(CQListCtrl)
  88. ON_NOTIFY_REFLECT(LVN_KEYDOWN, OnKeydown)
  89. ON_NOTIFY_REFLECT(NM_DBLCLK, OnDblclk)
  90. ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomdrawList)
  91. ON_WM_SYSKEYDOWN()
  92. ON_WM_ERASEBKGND()
  93. ON_WM_CREATE()
  94. ON_WM_VSCROLL()
  95. ON_WM_HSCROLL()
  96. ON_WM_TIMER()
  97. ON_WM_WINDOWPOSCHANGED()
  98. ON_NOTIFY_REFLECT(LVN_ITEMCHANGED, OnSelectionChange)
  99. //}}AFX_MSG_MAP
  100. ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipText)
  101. ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipText)
  102. ON_WM_KILLFOCUS()
  103. END_MESSAGE_MAP()
  104. /////////////////////////////////////////////////////////////////////////////
  105. // CQListCtrl message handlers
  106. void CQListCtrl::OnKeydown(NMHDR* pNMHDR, LRESULT* pResult)
  107. {
  108. LV_KEYDOWN* pLVKeyDown = (LV_KEYDOWN*)pNMHDR;
  109. switch (pLVKeyDown->wVKey)
  110. {
  111. case VK_RETURN:
  112. {
  113. ARRAY arr;
  114. GetSelectionIndexes(arr);
  115. SendSelection(arr);
  116. }
  117. break;
  118. case VK_ESCAPE:
  119. GetParent()->SendMessage(NM_END, 0, 0);
  120. break;
  121. case VK_RIGHT:
  122. {
  123. int nItem = GetNextItem(-1, LVNI_SELECTED);
  124. if (nItem != -1)
  125. GetParent()->SendMessage(NM_RIGHT, nItem, 0);
  126. }
  127. break;
  128. case VK_LEFT:
  129. GetParent()->SendMessage(NM_LEFT, 0, 0);
  130. break;
  131. case VK_DELETE:
  132. GetParent()->SendMessage(NM_DELETE, 0, 0);
  133. break;
  134. }
  135. *pResult = 0;
  136. }
  137. void CQListCtrl::OnDblclk(NMHDR* pNMHDR, LRESULT* pResult)
  138. {
  139. LPNMITEMACTIVATE lpnmItem = (LPNMITEMACTIVATE) pNMHDR;
  140. UINT Flags;
  141. int nItem = -1;
  142. if ((nItem = HitTest(lpnmItem->ptAction, &Flags)) != -1)
  143. {
  144. if (Flags | LVHT_ONITEM)
  145. SendSelection(nItem);
  146. }
  147. *pResult = 0;
  148. }
  149. void CQListCtrl::SendSelection(int nItem)
  150. {
  151. GetParent()->SendMessage(NM_SELECT, 1, (LPARAM) &nItem);
  152. }
  153. void CQListCtrl::SendSelection(ARRAY &arrItems)
  154. {
  155. GetParent()->SendMessage(NM_SELECT, arrItems.GetSize(), (LPARAM) arrItems.GetData());
  156. }
  157. void CQListCtrl::GetSelectionIndexes(ARRAY &arr)
  158. {
  159. arr.RemoveAll();
  160. POSITION pos = GetFirstSelectedItemPosition();
  161. while (pos)
  162. arr.Add(GetNextSelectedItem(pos));
  163. /*
  164. int nItem = GetNextItem(-1, LVNI_SELECTED);
  165. while (nItem != -1)
  166. {
  167. arr.Add(nItem);
  168. nItem = GetNextItem(nItem, LVNI_SELECTED);
  169. }
  170. */
  171. }
  172. void CQListCtrl::GetSelectionItemData(ARRAY &arr)
  173. {
  174. DWORD dwData;
  175. int i;
  176. arr.RemoveAll();
  177. POSITION pos = GetFirstSelectedItemPosition();
  178. while (pos)
  179. {
  180. i = GetNextSelectedItem(pos);
  181. dwData = GetItemData(i);
  182. arr.Add( dwData );
  183. }
  184. /*
  185. int nItem = GetNextItem(-1, LVNI_SELECTED);
  186. while (nItem != -1)
  187. {
  188. arr.Add((int)GetItemData(nItem));
  189. nItem = GetNextItem(nItem, LVNI_SELECTED);
  190. }
  191. */
  192. }
  193. void CQListCtrl::RemoveAllSelection()
  194. {
  195. POSITION pos = GetFirstSelectedItemPosition();
  196. while (pos)
  197. {
  198. SetSelection(GetNextSelectedItem(pos), FALSE);
  199. }
  200. }
  201. BOOL CQListCtrl::SetSelection(int nRow, BOOL bSelect)
  202. {
  203. if(bSelect)
  204. return SetItemState(nRow, LVIS_SELECTED, LVIS_SELECTED);
  205. else
  206. return SetItemState(nRow, ~LVIS_SELECTED, LVIS_SELECTED);
  207. }
  208. BOOL CQListCtrl::SetText(int nRow, int nCol, CString cs)
  209. {
  210. return SetItemText(nRow, nCol, cs);
  211. }
  212. BOOL CQListCtrl::SetCaret(int nRow, BOOL bFocus)
  213. {
  214. if(bFocus)
  215. return SetItemState(nRow, LVIS_FOCUSED, LVIS_FOCUSED);
  216. else
  217. return SetItemState(nRow, ~LVIS_FOCUSED, LVIS_FOCUSED);
  218. }
  219. long CQListCtrl::GetCaret()
  220. {
  221. return GetNextItem(-1, LVNI_FOCUSED);
  222. }
  223. // moves the caret to the given index, selects it, and ensures it is visible.
  224. BOOL CQListCtrl::SetListPos( int index )
  225. {
  226. if( index < 0 || index >= GetItemCount() )
  227. return FALSE;
  228. RemoveAllSelection();
  229. SetCaret(index);
  230. SetSelection(index);
  231. EnsureVisible(index,FALSE);
  232. return TRUE;
  233. }
  234. BOOL CQListCtrl::SetFormattedText(int nRow, int nCol, LPCTSTR lpszFormat,...)
  235. {
  236. CString csText;
  237. va_list vlist;
  238. ASSERT(AfxIsValidString(lpszFormat));
  239. va_start(vlist,lpszFormat);
  240. csText.FormatV(lpszFormat,vlist);
  241. va_end(vlist);
  242. return SetText(nRow,nCol,csText);
  243. }
  244. void CQListCtrl::SetNumberOfLinesPerRow(int nLines)
  245. {
  246. CDC *pDC = GetDC();
  247. CRect crRect(0, 0, 0, 0);
  248. CFont *pOldFont = pDC->SelectObject(GetFont());
  249. //Get the height to draw one character
  250. pDC->DrawText("W", crRect, DT_VCENTER | DT_EXPANDTABS | DT_CALCRECT);
  251. pDC->SelectObject(pOldFont);
  252. //Get the total height of each row
  253. int nHeight = (crRect.Height() * nLines) + ROW_BOTTOM_BORDER;
  254. //Create a image list of that height and set it to the list box
  255. CImageList imglist;
  256. imglist.Create(DUMMY_COL_WIDTH, nHeight, ILC_COLOR16 | ILC_MASK, 1, 1);
  257. SetImageList(&imglist, LVSIL_SMALL );
  258. ReleaseDC(pDC);
  259. }
  260. void CQListCtrl::OnCustomdrawList(NMHDR* pNMHDR, LRESULT* pResult)
  261. {
  262. NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>( pNMHDR );
  263. *pResult = 0;
  264. // Request item-specific notifications if this is the
  265. // beginning of the paint cycle.
  266. if ( CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage )
  267. {
  268. *pResult = CDRF_NOTIFYITEMDRAW;
  269. }
  270. else if ( CDDS_ITEMPREPAINT == pLVCD->nmcd.dwDrawStage )
  271. {
  272. LVITEM rItem;
  273. int nItem = static_cast<int>( pLVCD->nmcd.dwItemSpec );
  274. CDC* pDC = CDC::FromHandle ( pLVCD->nmcd.hdc );
  275. COLORREF crBkgnd;
  276. BOOL bListHasFocus;
  277. CRect rcItem;
  278. bListHasFocus = ( GetSafeHwnd() == ::GetFocus() );
  279. // Get the image index and selected/focused state of the
  280. // item being drawn.
  281. ZeroMemory ( &rItem, sizeof(LVITEM) );
  282. rItem.mask = LVIF_STATE;
  283. rItem.iItem = nItem;
  284. rItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
  285. GetItem(&rItem);
  286. // Get the rect that bounds the text label.
  287. GetItemRect(nItem, rcItem, LVIR_LABEL);
  288. rcItem.left -= DUMMY_COL_WIDTH;
  289. CPen cpWhite;
  290. cpWhite.CreatePen(PS_SOLID, 1, RGB(255, 255, 255));
  291. CPen *pOldPen = NULL;
  292. COLORREF OldColor = -1;
  293. int nOldBKMode = -1;
  294. // Draw the background of the list item. Colors are selected
  295. // according to the item's state.
  296. if(rItem.state & LVIS_SELECTED)
  297. {
  298. if(bListHasFocus)
  299. {
  300. crBkgnd = GetSysColor(COLOR_HIGHLIGHT);
  301. OldColor = pDC->SetTextColor(GetSysColor(COLOR_HIGHLIGHTTEXT));
  302. pOldPen = pDC->SelectObject((CPen*)&cpWhite);
  303. }
  304. else
  305. {
  306. crBkgnd = GetSysColor(COLOR_BTNFACE);
  307. OldColor = pDC->SetTextColor(GetSysColor(COLOR_BTNTEXT));
  308. }
  309. }
  310. else
  311. {
  312. //Shade alternating Rows
  313. if((nItem % 2) == 0)
  314. crBkgnd = COLOR_SHADOW;
  315. else
  316. crBkgnd = GetSysColor(COLOR_WINDOW);
  317. OldColor = pDC->SetTextColor(GetSysColor(COLOR_BTNTEXT));
  318. }
  319. pDC->FillSolidRect(rcItem, crBkgnd);
  320. nOldBKMode = pDC->SetBkMode(TRANSPARENT);
  321. CRect rcText = rcItem;
  322. rcText.left += ROW_LEFT_BORDER;
  323. rcText.top++;
  324. // Draw the text.
  325. //CString csText = GetItemText(nItem, 0);
  326. CString csText;
  327. LPTSTR lpszText = csText.GetBufferSetLength(g_Opt.m_bDescTextSize);
  328. GetItemText(nItem, 0, lpszText, g_Opt.m_bDescTextSize);
  329. csText.ReleaseBuffer();
  330. // extract symbols
  331. CString strSymbols;
  332. int nSymEnd = csText.Find('|');
  333. if( nSymEnd >= 0 )
  334. {
  335. strSymbols = csText.Left(nSymEnd);
  336. csText = csText.Mid(nSymEnd+1);
  337. }
  338. // set firstTenNum to the first ten number (1-10) corresponding to
  339. // the current nItem.
  340. // -1 means that nItem is not in the FirstTen block.
  341. int firstTenNum = GetFirstTenNum(nItem);
  342. if( m_bShowTextForFirstTenHotKeys && firstTenNum > 0 )
  343. {
  344. rcText.left += 12;
  345. }
  346. // if we are inside a group, don't display the "in group" flag
  347. if( theApp.m_GroupID > 0 )
  348. {
  349. int nFlag = strSymbols.Find("!");
  350. if( nFlag >= 0 )
  351. strSymbols.Delete(nFlag);
  352. }
  353. DrawBitMap(nItem, rcText, pDC);
  354. // draw the symbol box
  355. if( strSymbols.GetLength() > 0 )
  356. {
  357. strSymbols = " " + strSymbols + " "; // leave space for box
  358. // add spaces to leave room for the symbols
  359. CRect rectSym(rcText.left, rcText.top+1, rcText.left, rcText.top+1);
  360. CRect rectSpace(0,0,0,0);
  361. //Get text bounds
  362. pDC->DrawText(" ", &rectSpace, DT_VCENTER | DT_EXPANDTABS | DT_CALCRECT);
  363. pDC->DrawText(strSymbols, &rectSym, DT_VCENTER | DT_EXPANDTABS | DT_CALCRECT);
  364. VERIFY( rectSpace.Width() > 0 );
  365. int numSpaces = rectSym.Width() / rectSpace.Width();
  366. numSpaces++;
  367. csText = CString(' ',numSpaces) + csText;
  368. // draw the symbols
  369. pDC->FillSolidRect( rectSym, GetSysColor(COLOR_ACTIVECAPTION) );
  370. //pDC->FillSolidRect( rectSym, RGB(0,255,255) );
  371. pDC->Draw3dRect(rectSym, GetSysColor(COLOR_3DLIGHT), GetSysColor(COLOR_3DDKSHADOW));
  372. // COLORREF crOld = pDC->SetTextColor(GetSysColor(COLOR_INFOTEXT));
  373. COLORREF crOld = pDC->SetTextColor(RGB(255, 255, 255));
  374. pDC->DrawText(strSymbols, rectSym, DT_VCENTER | DT_EXPANDTABS);
  375. pDC->SetTextColor(crOld);
  376. }
  377. pDC->DrawText(csText, rcText, DT_VCENTER | DT_EXPANDTABS);
  378. // Draw a focus rect around the item if necessary.
  379. if(bListHasFocus && (rItem.state & LVIS_FOCUSED))
  380. pDC->DrawFocusRect(rcItem);
  381. if( m_bShowTextForFirstTenHotKeys && firstTenNum > 0 )
  382. {
  383. CString cs;
  384. if( firstTenNum == 10 )
  385. cs = "0";
  386. else
  387. cs.Format("%d", firstTenNum);
  388. CRect crClient;
  389. GetWindowRect(crClient);
  390. ScreenToClient(crClient);
  391. CRect crHotKey = rcItem;
  392. crHotKey.right = crHotKey.left + 11;
  393. crHotKey.left += 2;
  394. crHotKey.top += 2;
  395. HFONT hOldFont = (HFONT)pDC->SelectObject(m_SmallFont);
  396. pDC->DrawText(cs, crHotKey, DT_BOTTOM);
  397. pDC->MoveTo(CPoint(rcItem.left + 11, rcItem.top));
  398. pDC->LineTo(CPoint(rcItem.left + 11, rcItem.bottom));
  399. pDC->SelectObject(hOldFont);
  400. }
  401. // restore the previous values
  402. if(pOldPen)
  403. pDC->SelectObject(pOldPen);
  404. if(OldColor > -1)
  405. pDC->SetTextColor(OldColor);
  406. if(nOldBKMode > -1)
  407. pDC->SetBkMode(nOldBKMode);
  408. *pResult = CDRF_SKIPDEFAULT; // We've painted everything.
  409. }
  410. }
  411. BOOL CQListCtrl::DrawBitMap(int nItem, CRect &crRect, CDC *pDC)
  412. {
  413. CClipFormat Clip;
  414. Clip.m_cfType = CF_DIB;
  415. CBitmap Bitmap;
  416. bool bAddToMap = false;
  417. bool bGetDIB = false;
  418. long lDatabaseID = GetItemData(nItem);
  419. if(m_ThumbNails.Lookup(lDatabaseID, Clip) == FALSE)
  420. {
  421. GetClipData(nItem, Clip);
  422. bAddToMap = true;
  423. }
  424. if(Clip.m_hgData)
  425. {
  426. if(CBitmapHelper::GetCBitmap(&Clip, pDC, &Bitmap, crRect.Height()))
  427. {
  428. int nWidth = CBitmapHelper::GetCBitmapWidth(Bitmap);
  429. int nHeight = CBitmapHelper::GetCBitmapHeight(Bitmap);
  430. CDC MemDc;
  431. MemDc.CreateCompatibleDC(pDC);
  432. CBitmap* oldBitmap = MemDc.SelectObject(&Bitmap);
  433. pDC->BitBlt(crRect.left, crRect.top, nWidth, nHeight, &MemDc, 0, 0, SRCCOPY);
  434. MemDc.SelectObject(oldBitmap);
  435. crRect.left += nWidth + 3;
  436. bGetDIB = true;
  437. }
  438. else
  439. {
  440. bAddToMap = false;
  441. }
  442. }
  443. if(bAddToMap)
  444. {
  445. CClipFormat ThumbData(CF_DIB);
  446. //Convert the bitmap to a DIB to cache the bitmap
  447. //caching the bitmap had problems
  448. if(bGetDIB)
  449. {
  450. HPALETTE hPal = NULL;
  451. ThumbData.m_hgData = CBitmapHelper::hBitmapToDIB(Bitmap.operator HBITMAP(), BI_RGB, hPal);
  452. // ThumbData.m_hgData = BitmapToDIB(Bitmap.operator HBITMAP(), hPal);
  453. //Map will delete memory in destructor
  454. ThumbData.bDeleteData = false;
  455. }
  456. m_ThumbNails.SetAt(lDatabaseID, ThumbData);
  457. }
  458. return TRUE;
  459. }
  460. void CQListCtrl::RefreshVisibleRows()
  461. {
  462. int nTopIndex = GetTopIndex();
  463. int nLastIndex = nTopIndex + GetCountPerPage();
  464. RedrawItems(nTopIndex, nLastIndex);
  465. ::UpdateWindow(m_hWnd);
  466. }
  467. void CQListCtrl::OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
  468. {
  469. if(GetKeyState(VK_RETURN) & 0x800)
  470. GetParent()->SendMessage(NM_PROPERTIES, 0, 0);
  471. else
  472. CListCtrl::OnSysKeyDown(nChar, nRepCnt, nFlags);
  473. }
  474. BOOL CQListCtrl::OnEraseBkgnd(CDC* pDC)
  475. {
  476. // Simply returning TRUE seems OK since we do custom item
  477. // painting. However, there is a pixel buffer around the
  478. // border of this control (not within the item rects)
  479. // which becomes visually corrupt if it is not erased.
  480. // In most cases, I do not notice the erasure, so I have kept
  481. // the call to CListCtrl::OnEraseBkgnd(pDC);
  482. // However, for some reason, bulk erasure is very noticeable when
  483. // shift-scrolling the page to select a block of items, so
  484. // I made a special case for that:
  485. if( GetSelectedCount() >= 2 )
  486. return TRUE;
  487. return CListCtrl::OnEraseBkgnd(pDC);
  488. }
  489. BOOL CQListCtrl::OnToolTipText( UINT id, NMHDR * pNMHDR, LRESULT * pResult )
  490. {
  491. // need to handle both ANSI and UNICODE versions of the message
  492. TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
  493. TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
  494. CString strTipText;
  495. UINT nID = pNMHDR->idFrom;
  496. if(nID == 0) // Notification in NT from automatically
  497. return FALSE; // created tooltip
  498. ::SendMessage(pNMHDR->hwndFrom, TTM_SETMAXTIPWIDTH, 0, 500);
  499. // Use Item's name as the tool tip. Change this for something different.
  500. // Like use its file size, etc.
  501. GetToolTipText(nID-1, strTipText);
  502. //Replace the tabs with spaces, the tooltip didn't like the \t s
  503. strTipText.Replace("\t", " ");
  504. #ifndef _UNICODE
  505. if (pNMHDR->code == TTN_NEEDTEXTA)
  506. {
  507. if(m_pchTip != NULL)
  508. delete m_pchTip;
  509. m_pchTip = new TCHAR[strTipText.GetLength()+1];
  510. lstrcpyn(m_pchTip, strTipText, strTipText.GetLength());
  511. m_pchTip[strTipText.GetLength()] = 0;
  512. pTTTW->lpszText = (WCHAR*)m_pchTip;
  513. }
  514. else
  515. {
  516. if(m_pwchTip != NULL)
  517. delete m_pwchTip;
  518. m_pwchTip = new WCHAR[strTipText.GetLength()+1];
  519. _mbstowcsz(m_pwchTip, strTipText, strTipText.GetLength());
  520. m_pwchTip[strTipText.GetLength()] = 0; // end of text
  521. pTTTW->lpszText = (WCHAR*)m_pwchTip;
  522. }
  523. #else
  524. if(pNMHDR->code == TTN_NEEDTEXTA)
  525. {
  526. if(m_pchTip != NULL)
  527. delete m_pchTip;
  528. m_pchTip = new TCHAR[strTipText.GetLength()+1];
  529. _wcstombsz(m_pchTip, strTipText, strTipText.GetLength());
  530. m_pchTip[strTipText.GetLength()] = 0; // end of text
  531. pTTTA->lpszText = (LPTSTR)m_pchTip;
  532. }
  533. else
  534. {
  535. if(m_pwchTip != NULL)
  536. delete m_pwchTip;
  537. m_pwchTip = new WCHAR[strTipText.GetLength()+1];
  538. lstrcpyn(m_pwchTip, strTipText, strTipText.GetLength());
  539. m_pwchTip[strTipText.GetLength()] = 0;
  540. pTTTA->lpszText = (LPTSTR) m_pwchTip;
  541. }
  542. #endif
  543. *pResult = 0;
  544. return TRUE; // message was handled
  545. }
  546. int CQListCtrl::OnToolHitTest(CPoint point, TOOLINFO * pTI) const
  547. {
  548. CRect rect;
  549. GetClientRect(&rect);
  550. if(rect.PtInRect(point))
  551. {
  552. if(GetItemCount())
  553. {
  554. int nTopIndex = GetTopIndex();
  555. int nBottomIndex = nTopIndex + GetCountPerPage();
  556. if(nBottomIndex > GetItemCount()) nBottomIndex = GetItemCount();
  557. for(int nIndex = nTopIndex; nIndex <= nBottomIndex; nIndex++)
  558. {
  559. GetItemRect(nIndex, rect, LVIR_BOUNDS);
  560. if(rect.PtInRect(point))
  561. {
  562. pTI->hwnd = m_hWnd;
  563. pTI->uId = (UINT)(nIndex+1);
  564. pTI->lpszText = LPSTR_TEXTCALLBACK;
  565. pTI->rect = rect;
  566. return pTI->uId;
  567. }
  568. }
  569. }
  570. }
  571. return -1;
  572. }
  573. int CQListCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
  574. {
  575. if (CListCtrl::OnCreate(lpCreateStruct) == -1)
  576. return -1;
  577. EnableToolTips();
  578. m_pToolTip = new CToolTipEx;
  579. m_pToolTip->Create(this);
  580. return 0;
  581. }
  582. BOOL CQListCtrl::PreTranslateMessage(MSG* pMsg)
  583. {
  584. DWORD dID;
  585. if(m_Accels.OnMsg(pMsg, dID))
  586. if(GetParent()->SendMessage(NM_SELECT_DB_ID, dID, 0) )
  587. return TRUE;
  588. if(m_pToolTip)
  589. {
  590. if(m_pToolTip->OnMsg(pMsg))
  591. return TRUE;
  592. }
  593. switch(pMsg->message)
  594. {
  595. case WM_KEYDOWN:
  596. WPARAM vk = pMsg->wParam;
  597. // if a number key was pressed
  598. if( '0' <= vk && vk <= '9' )
  599. {
  600. // if <Ctrl> is required but is absent, then break
  601. if( g_Opt.m_bUseCtrlNumAccel && !(GetKeyState(VK_CONTROL) & 0x8000) )
  602. break;
  603. int index = vk - '0';
  604. // '0' is actually 10 in the ditto window
  605. if( index == 0 )
  606. index = 10;
  607. // translate num 1-10 into the actual index (based upon m_bStartTop)
  608. index = GetFirstTenIndex( index );
  609. GetParent()->SendMessage(NM_SELECT_INDEX, index, 0);
  610. return TRUE;
  611. }
  612. switch( vk )
  613. {
  614. case 'X': // Ctrl-X = Cut (prepare for moving the items into a Group)
  615. if(GetKeyState(VK_CONTROL) & 0x8000)
  616. {
  617. LoadCopyOrCutToClipboard();
  618. theApp.IC_Cut(); // uses selection
  619. return TRUE;
  620. }
  621. break;
  622. case 'C': // Ctrl-C = Copy (prepare for copying the items into a Group)
  623. if(GetKeyState(VK_CONTROL) & 0x8000)
  624. {
  625. LoadCopyOrCutToClipboard();
  626. theApp.IC_Copy(); // uses selection
  627. return TRUE;
  628. }
  629. break;
  630. case 'V': // Ctrl-V = Paste (actually performs the copy or move of items into the current Group)
  631. if(GetKeyState(VK_CONTROL) & 0x8000)
  632. {
  633. theApp.IC_Paste();
  634. return TRUE;
  635. }
  636. break;
  637. case 'A': // Ctrl-A = Select All
  638. if(GetKeyState(VK_CONTROL) & 0x8000)
  639. {
  640. int nCount = GetItemCount();
  641. for(int i = 0; i < nCount; i++)
  642. {
  643. SetSelection(i);
  644. }
  645. return TRUE;
  646. }
  647. break;
  648. case VK_F3:
  649. {
  650. ShowFullDescription();
  651. return TRUE;
  652. }
  653. case VK_BACK:
  654. theApp.EnterGroupID( theApp.m_GroupParentID );
  655. return TRUE;
  656. case VK_SPACE:
  657. if(GetKeyState(VK_CONTROL) & 0x8000)
  658. {
  659. theApp.ShowPersistent( !g_Opt.m_bShowPersistent );
  660. return TRUE;
  661. }
  662. break;
  663. } // end switch(vk)
  664. break; // end case WM_KEYDOWN
  665. } // end switch(pMsg->message)
  666. return CListCtrl::PreTranslateMessage(pMsg);
  667. }
  668. void CQListCtrl::LoadCopyOrCutToClipboard()
  669. {
  670. ARRAY arr;
  671. GetSelectionItemData(arr);
  672. int nCount = arr.GetSize();
  673. if(nCount <= 0)
  674. return;
  675. CProcessPaste paste;
  676. //Don't send the paste just load it into memory
  677. paste.m_bSendPaste = false;
  678. if(nCount > 1)
  679. paste.GetClipIDs().Copy(arr);
  680. else
  681. paste.GetClipIDs().Add(arr[0]);
  682. paste.DoPaste();
  683. }
  684. void CQListCtrl::ShowFullDescription(bool bFromAuto)
  685. {
  686. int nItem = GetCaret();
  687. CRect rc, crWindow;
  688. GetWindowRect(&crWindow);
  689. GetItemRect(nItem, rc, LVIR_BOUNDS);
  690. ClientToScreen(rc);
  691. CPoint pt;
  692. if(bFromAuto == false)
  693. {
  694. pt = CPoint(rc.left, rc.bottom);
  695. }
  696. else
  697. pt = CPoint((crWindow.left + (crWindow.right - crWindow.left)/2), rc.bottom);
  698. CString cs;
  699. GetToolTipText(nItem, cs);
  700. if(m_pToolTip)
  701. {
  702. CClipFormat Clip;
  703. Clip.m_cfType = CF_DIB;
  704. static CBitmap *pBitMap = NULL;
  705. if(GetClipData(nItem, Clip) && Clip.m_hgData)
  706. {
  707. pBitMap = new CBitmap;
  708. if(pBitMap)
  709. {
  710. CRect rcItem;
  711. GetWindowRect(rcItem);
  712. CDC *pDC = GetDC();;
  713. CBitmapHelper::GetCBitmap(&Clip, pDC, pBitMap, (rcItem.Width() * 2));
  714. ReleaseDC(pDC);
  715. m_pToolTip->SetBitmap(pBitMap);
  716. }
  717. }
  718. m_pToolTip->SetToolTipText(cs);
  719. m_pToolTip->Show(pt);
  720. }
  721. }
  722. void CQListCtrl::GetToolTipText(int nItem, CString &csText)
  723. {
  724. if((GetStyle() & LVS_OWNERDATA))
  725. {
  726. CWnd* pParent=GetParent();
  727. if(pParent && (pParent->GetSafeHwnd() != NULL))
  728. {
  729. CQListToolTipText info;
  730. memset(&info, 0, sizeof(info));
  731. info.hdr.code = NM_GETTOOLTIPTEXT;
  732. info.hdr.hwndFrom = GetSafeHwnd();
  733. info.hdr.idFrom = GetDlgCtrlID();
  734. info.lItem = nItem;
  735. //plus 100 for extra info - shortcut and such
  736. info.cchTextMax = g_Opt.m_bDescTextSize + 100;
  737. info.pszText = csText.GetBufferSetLength(info.cchTextMax);
  738. pParent->SendMessage(WM_NOTIFY,(WPARAM)info.hdr.idFrom,(LPARAM)&info);
  739. csText.ReleaseBuffer();
  740. }
  741. }
  742. }
  743. BOOL CQListCtrl::GetClipData(int nItem, CClipFormat &Clip)
  744. {
  745. CWnd* pParent=GetParent();
  746. if(pParent && (pParent->GetSafeHwnd() != NULL))
  747. {
  748. if(GetParent()->SendMessage(NM_GET_CLIP_DATA, nItem, (LPARAM) &Clip))
  749. return TRUE;
  750. }
  751. return FALSE;
  752. }
  753. DWORD CQListCtrl::GetItemData(int nItem)
  754. {
  755. if((GetStyle() & LVS_OWNERDATA))
  756. {
  757. CWnd* pParent=GetParent();
  758. if(pParent && (pParent->GetSafeHwnd() != NULL))
  759. {
  760. LV_DISPINFO info;
  761. memset(&info, 0, sizeof(info));
  762. info.hdr.code = LVN_GETDISPINFO;
  763. info.hdr.hwndFrom = GetSafeHwnd();
  764. info.hdr.idFrom = GetDlgCtrlID();
  765. info.item.iItem = nItem;
  766. info.item.lParam = -1;
  767. info.item.mask = LVIF_PARAM;
  768. pParent->SendMessage(WM_NOTIFY,(WPARAM)info.hdr.idFrom,(LPARAM)&info);
  769. return info.item.lParam;
  770. }
  771. }
  772. return CListCtrl::GetItemData(nItem);
  773. }
  774. void CQListCtrl::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
  775. {
  776. CListCtrl::OnVScroll(nSBCode, nPos, pScrollBar);
  777. }
  778. void CQListCtrl::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
  779. {
  780. CListCtrl::OnHScroll(nSBCode, nPos, pScrollBar);
  781. }
  782. void CQListCtrl::DestroyAndCreateAccelerator(BOOL bCreate)
  783. {
  784. if( bCreate )
  785. CMainTable::LoadAcceleratorKeys( m_Accels );
  786. }
  787. void CQListCtrl::OnKillFocus(CWnd* pNewWnd)
  788. {
  789. CListCtrl::OnKillFocus(pNewWnd);
  790. m_pToolTip->Hide();
  791. }
  792. BOOL CQListCtrl::SetItemCountEx(int iCount, DWORD dwFlags /* = LVSICF_NOINVALIDATEALL */)
  793. {
  794. theApp.SetStatus(NULL, TRUE);
  795. return CListCtrl::SetItemCountEx(iCount, dwFlags);
  796. }
  797. #define TIMER_SHOW_PROPERTIES 1
  798. void CQListCtrl::OnSelectionChange(NMHDR* pNMHDR, LRESULT* pResult)
  799. {
  800. NMLISTVIEW *pnmv = (NMLISTVIEW *) pNMHDR;
  801. if((pnmv->uNewState == 3) ||
  802. (pnmv->uNewState == 1))
  803. {
  804. if(g_Opt.m_bAllwaysShowDescription)
  805. {
  806. KillTimer(TIMER_SHOW_PROPERTIES);
  807. SetTimer(TIMER_SHOW_PROPERTIES, 300, NULL);
  808. }
  809. if(GetSelectedCount() > 0 )
  810. theApp.SetStatus(NULL, FALSE);
  811. }
  812. }
  813. void CQListCtrl::OnTimer(UINT nIDEvent)
  814. {
  815. if(nIDEvent == TIMER_SHOW_PROPERTIES)
  816. {
  817. if( theApp.m_bShowingQuickPaste )
  818. ShowFullDescription(true);
  819. KillTimer(TIMER_SHOW_PROPERTIES);
  820. }
  821. CListCtrl::OnTimer(nIDEvent);
  822. }
  823. void CQListCtrl::SetLogFont(LOGFONT &font)
  824. {
  825. m_Font.DeleteObject();
  826. m_Font.CreateFontIndirect(&font);
  827. SetFont(&m_Font);
  828. }