QListCtrl.cpp 24 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019
  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. // DrawBitMap loads a DIB from the DB, draws a crRect thumbnail of the image
  412. // to pDC and caches that thumbnail as a DIB in m_ThumbNails[ ItemID ].
  413. // ALL items are cached in m_ThumbNails (those without images are cached with NULL m_hgData)
  414. BOOL CQListCtrl::DrawBitMap(int nItem, CRect &crRect, CDC *pDC)
  415. {
  416. bool bFromDB = false;
  417. long lDatabaseID = GetItemData(nItem);
  418. CClipFormat* pThumbnail = NULL; // pointer to the thumbnail in the cache map
  419. CMapIDtoCF::CPair* pPair = m_ThumbNails.PLookup(lDatabaseID);
  420. if( pPair == NULL )
  421. {
  422. pThumbnail = &(m_ThumbNails[lDatabaseID]);
  423. pThumbnail->m_cfType = CF_DIB;
  424. GetClipData(nItem, *pThumbnail);
  425. bFromDB = true;
  426. }
  427. else
  428. pThumbnail = &(pPair->value);
  429. // if there's no data, then we're done.
  430. if( pThumbnail->m_hgData == NULL )
  431. return TRUE;
  432. CBitmap Bitmap;
  433. // Convert DIB to a DDB.
  434. if( !CBitmapHelper::GetCBitmap(pThumbnail, pDC, &Bitmap, crRect.Height()) )
  435. {
  436. Bitmap.DeleteObject();
  437. pThumbnail->Free(); // the data is useless, so free it.
  438. return FALSE;
  439. }
  440. BITMAP bm;
  441. Bitmap.GetBitmap(&bm);
  442. int nWidth = bm.bmWidth;
  443. int nHeight = bm.bmHeight;
  444. // draw Bitmap
  445. CDC MemDc;
  446. MemDc.CreateCompatibleDC(pDC);
  447. CBitmap* oldBitmap = MemDc.SelectObject(&Bitmap);
  448. pDC->BitBlt(crRect.left, crRect.top, nWidth, nHeight, &MemDc, 0, 0, SRCCOPY);
  449. MemDc.SelectObject(oldBitmap);
  450. MemDc.DeleteDC();
  451. // adjust the rect so other information can be drawn next to the thumbnail
  452. crRect.left += nWidth + 3;
  453. // if the original DIB came from the DB, cache the smaller version we created above.
  454. if( bFromDB )
  455. {
  456. pThumbnail->Free(); // delete the large image data loaded from the db
  457. pThumbnail->m_cfType = CF_DIB;
  458. // Convert the smaller DDB to a DIB to store in the cache
  459. //caching the bitmap had problems (???)
  460. HPALETTE hPal = NULL;
  461. pThumbnail->m_hgData = CBitmapHelper::hBitmapToDIB( (HBITMAP)Bitmap, BI_RGB, hPal );
  462. // cf.m_hgData = BitmapToDIB(Bitmap.operator HBITMAP(), hPal);
  463. ASSERT( pThumbnail->bDeleteData ); // the map owns the data.
  464. }
  465. Bitmap.DeleteObject();
  466. return TRUE;
  467. }
  468. void CQListCtrl::RefreshVisibleRows()
  469. {
  470. int nTopIndex = GetTopIndex();
  471. int nLastIndex = nTopIndex + GetCountPerPage();
  472. RedrawItems(nTopIndex, nLastIndex);
  473. ::UpdateWindow(m_hWnd);
  474. }
  475. void CQListCtrl::OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
  476. {
  477. if(GetKeyState(VK_RETURN) & 0x800)
  478. GetParent()->SendMessage(NM_PROPERTIES, 0, 0);
  479. else
  480. CListCtrl::OnSysKeyDown(nChar, nRepCnt, nFlags);
  481. }
  482. BOOL CQListCtrl::OnEraseBkgnd(CDC* pDC)
  483. {
  484. // Simply returning TRUE seems OK since we do custom item
  485. // painting. However, there is a pixel buffer around the
  486. // border of this control (not within the item rects)
  487. // which becomes visually corrupt if it is not erased.
  488. // In most cases, I do not notice the erasure, so I have kept
  489. // the call to CListCtrl::OnEraseBkgnd(pDC);
  490. // However, for some reason, bulk erasure is very noticeable when
  491. // shift-scrolling the page to select a block of items, so
  492. // I made a special case for that:
  493. if( GetSelectedCount() >= 2 )
  494. return TRUE;
  495. return CListCtrl::OnEraseBkgnd(pDC);
  496. }
  497. BOOL CQListCtrl::OnToolTipText( UINT id, NMHDR * pNMHDR, LRESULT * pResult )
  498. {
  499. // need to handle both ANSI and UNICODE versions of the message
  500. TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
  501. TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
  502. CString strTipText;
  503. UINT nID = pNMHDR->idFrom;
  504. if(nID == 0) // Notification in NT from automatically
  505. return FALSE; // created tooltip
  506. ::SendMessage(pNMHDR->hwndFrom, TTM_SETMAXTIPWIDTH, 0, 500);
  507. // Use Item's name as the tool tip. Change this for something different.
  508. // Like use its file size, etc.
  509. GetToolTipText(nID-1, strTipText);
  510. //Replace the tabs with spaces, the tooltip didn't like the \t s
  511. strTipText.Replace("\t", " ");
  512. #ifndef _UNICODE
  513. if (pNMHDR->code == TTN_NEEDTEXTA)
  514. {
  515. if(m_pchTip != NULL)
  516. delete m_pchTip;
  517. m_pchTip = new TCHAR[strTipText.GetLength()+1];
  518. lstrcpyn(m_pchTip, strTipText, strTipText.GetLength());
  519. m_pchTip[strTipText.GetLength()] = 0;
  520. pTTTW->lpszText = (WCHAR*)m_pchTip;
  521. }
  522. else
  523. {
  524. if(m_pwchTip != NULL)
  525. delete m_pwchTip;
  526. m_pwchTip = new WCHAR[strTipText.GetLength()+1];
  527. _mbstowcsz(m_pwchTip, strTipText, strTipText.GetLength());
  528. m_pwchTip[strTipText.GetLength()] = 0; // end of text
  529. pTTTW->lpszText = (WCHAR*)m_pwchTip;
  530. }
  531. #else
  532. if(pNMHDR->code == TTN_NEEDTEXTA)
  533. {
  534. if(m_pchTip != NULL)
  535. delete m_pchTip;
  536. m_pchTip = new TCHAR[strTipText.GetLength()+1];
  537. _wcstombsz(m_pchTip, strTipText, strTipText.GetLength());
  538. m_pchTip[strTipText.GetLength()] = 0; // end of text
  539. pTTTA->lpszText = (LPTSTR)m_pchTip;
  540. }
  541. else
  542. {
  543. if(m_pwchTip != NULL)
  544. delete m_pwchTip;
  545. m_pwchTip = new WCHAR[strTipText.GetLength()+1];
  546. lstrcpyn(m_pwchTip, strTipText, strTipText.GetLength());
  547. m_pwchTip[strTipText.GetLength()] = 0;
  548. pTTTA->lpszText = (LPTSTR) m_pwchTip;
  549. }
  550. #endif
  551. *pResult = 0;
  552. return TRUE; // message was handled
  553. }
  554. int CQListCtrl::OnToolHitTest(CPoint point, TOOLINFO * pTI) const
  555. {
  556. CRect rect;
  557. GetClientRect(&rect);
  558. if(rect.PtInRect(point))
  559. {
  560. if(GetItemCount())
  561. {
  562. int nTopIndex = GetTopIndex();
  563. int nBottomIndex = nTopIndex + GetCountPerPage();
  564. if(nBottomIndex > GetItemCount()) nBottomIndex = GetItemCount();
  565. for(int nIndex = nTopIndex; nIndex <= nBottomIndex; nIndex++)
  566. {
  567. GetItemRect(nIndex, rect, LVIR_BOUNDS);
  568. if(rect.PtInRect(point))
  569. {
  570. pTI->hwnd = m_hWnd;
  571. pTI->uId = (UINT)(nIndex+1);
  572. pTI->lpszText = LPSTR_TEXTCALLBACK;
  573. pTI->rect = rect;
  574. return pTI->uId;
  575. }
  576. }
  577. }
  578. }
  579. return -1;
  580. }
  581. int CQListCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
  582. {
  583. if (CListCtrl::OnCreate(lpCreateStruct) == -1)
  584. return -1;
  585. EnableToolTips();
  586. m_pToolTip = new CToolTipEx;
  587. m_pToolTip->Create(this);
  588. return 0;
  589. }
  590. BOOL CQListCtrl::PreTranslateMessage(MSG* pMsg)
  591. {
  592. DWORD dID;
  593. if(m_Accels.OnMsg(pMsg, dID))
  594. if(GetParent()->SendMessage(NM_SELECT_DB_ID, dID, 0) )
  595. return TRUE;
  596. if(m_pToolTip)
  597. {
  598. if(m_pToolTip->OnMsg(pMsg))
  599. return TRUE;
  600. }
  601. switch(pMsg->message)
  602. {
  603. case WM_KEYDOWN:
  604. WPARAM vk = pMsg->wParam;
  605. // if a number key was pressed
  606. if( '0' <= vk && vk <= '9' )
  607. {
  608. // if <Ctrl> is required but is absent, then break
  609. if( g_Opt.m_bUseCtrlNumAccel && !(GetKeyState(VK_CONTROL) & 0x8000) )
  610. break;
  611. int index = vk - '0';
  612. // '0' is actually 10 in the ditto window
  613. if( index == 0 )
  614. index = 10;
  615. // translate num 1-10 into the actual index (based upon m_bStartTop)
  616. index = GetFirstTenIndex( index );
  617. GetParent()->SendMessage(NM_SELECT_INDEX, index, 0);
  618. return TRUE;
  619. }
  620. switch( vk )
  621. {
  622. case 'X': // Ctrl-X = Cut (prepare for moving the items into a Group)
  623. if(GetKeyState(VK_CONTROL) & 0x8000)
  624. {
  625. LoadCopyOrCutToClipboard();
  626. theApp.IC_Cut(); // uses selection
  627. return TRUE;
  628. }
  629. break;
  630. case 'C': // Ctrl-C = Copy (prepare for copying the items into a Group)
  631. if(GetKeyState(VK_CONTROL) & 0x8000)
  632. {
  633. LoadCopyOrCutToClipboard();
  634. theApp.IC_Copy(); // uses selection
  635. return TRUE;
  636. }
  637. break;
  638. case 'V': // Ctrl-V = Paste (actually performs the copy or move of items into the current Group)
  639. if(GetKeyState(VK_CONTROL) & 0x8000)
  640. {
  641. theApp.IC_Paste();
  642. return TRUE;
  643. }
  644. break;
  645. case 'A': // Ctrl-A = Select All
  646. if(GetKeyState(VK_CONTROL) & 0x8000)
  647. {
  648. int nCount = GetItemCount();
  649. for(int i = 0; i < nCount; i++)
  650. {
  651. SetSelection(i);
  652. }
  653. return TRUE;
  654. }
  655. break;
  656. case VK_F3:
  657. {
  658. ShowFullDescription();
  659. return TRUE;
  660. }
  661. case VK_BACK:
  662. theApp.EnterGroupID( theApp.m_GroupParentID );
  663. return TRUE;
  664. case VK_SPACE:
  665. if(GetKeyState(VK_CONTROL) & 0x8000)
  666. {
  667. theApp.ShowPersistent( !g_Opt.m_bShowPersistent );
  668. return TRUE;
  669. }
  670. break;
  671. } // end switch(vk)
  672. break; // end case WM_KEYDOWN
  673. } // end switch(pMsg->message)
  674. return CListCtrl::PreTranslateMessage(pMsg);
  675. }
  676. void CQListCtrl::LoadCopyOrCutToClipboard()
  677. {
  678. ARRAY arr;
  679. GetSelectionItemData(arr);
  680. int nCount = arr.GetSize();
  681. if(nCount <= 0)
  682. return;
  683. CProcessPaste paste;
  684. //Don't send the paste just load it into memory
  685. paste.m_bSendPaste = false;
  686. if(nCount > 1)
  687. paste.GetClipIDs().Copy(arr);
  688. else
  689. paste.GetClipIDs().Add(arr[0]);
  690. paste.DoPaste();
  691. }
  692. void CQListCtrl::ShowFullDescription(bool bFromAuto)
  693. {
  694. int nItem = GetCaret();
  695. CRect rc, crWindow;
  696. GetWindowRect(&crWindow);
  697. GetItemRect(nItem, rc, LVIR_BOUNDS);
  698. ClientToScreen(rc);
  699. CPoint pt;
  700. if(bFromAuto == false)
  701. {
  702. pt = CPoint(rc.left, rc.bottom);
  703. }
  704. else
  705. pt = CPoint((crWindow.left + (crWindow.right - crWindow.left)/2), rc.bottom);
  706. CString cs;
  707. GetToolTipText(nItem, cs);
  708. if(m_pToolTip)
  709. {
  710. CClipFormat Clip;
  711. Clip.m_cfType = CF_DIB;
  712. static CBitmap *pBitMap = NULL;
  713. if(GetClipData(nItem, Clip) && Clip.m_hgData)
  714. {
  715. pBitMap = new CBitmap;
  716. if(pBitMap)
  717. {
  718. CRect rcItem;
  719. GetWindowRect(rcItem);
  720. CDC *pDC = GetDC();;
  721. CBitmapHelper::GetCBitmap(&Clip, pDC, pBitMap, (rcItem.Width() * 2));
  722. ReleaseDC(pDC);
  723. m_pToolTip->SetBitmap(pBitMap);
  724. }
  725. }
  726. m_pToolTip->SetToolTipText(cs);
  727. m_pToolTip->Show(pt);
  728. }
  729. }
  730. void CQListCtrl::GetToolTipText(int nItem, CString &csText)
  731. {
  732. if((GetStyle() & LVS_OWNERDATA))
  733. {
  734. CWnd* pParent=GetParent();
  735. if(pParent && (pParent->GetSafeHwnd() != NULL))
  736. {
  737. CQListToolTipText info;
  738. memset(&info, 0, sizeof(info));
  739. info.hdr.code = NM_GETTOOLTIPTEXT;
  740. info.hdr.hwndFrom = GetSafeHwnd();
  741. info.hdr.idFrom = GetDlgCtrlID();
  742. info.lItem = nItem;
  743. //plus 100 for extra info - shortcut and such
  744. info.cchTextMax = g_Opt.m_bDescTextSize + 100;
  745. info.pszText = csText.GetBufferSetLength(info.cchTextMax);
  746. pParent->SendMessage(WM_NOTIFY,(WPARAM)info.hdr.idFrom,(LPARAM)&info);
  747. csText.ReleaseBuffer();
  748. }
  749. }
  750. }
  751. BOOL CQListCtrl::GetClipData(int nItem, CClipFormat &Clip)
  752. {
  753. CWnd* pParent=GetParent();
  754. if(pParent && (pParent->GetSafeHwnd() != NULL))
  755. {
  756. if(GetParent()->SendMessage(NM_GET_CLIP_DATA, nItem, (LPARAM) &Clip))
  757. return TRUE;
  758. }
  759. return FALSE;
  760. }
  761. DWORD CQListCtrl::GetItemData(int nItem)
  762. {
  763. if((GetStyle() & LVS_OWNERDATA))
  764. {
  765. CWnd* pParent=GetParent();
  766. if(pParent && (pParent->GetSafeHwnd() != NULL))
  767. {
  768. LV_DISPINFO info;
  769. memset(&info, 0, sizeof(info));
  770. info.hdr.code = LVN_GETDISPINFO;
  771. info.hdr.hwndFrom = GetSafeHwnd();
  772. info.hdr.idFrom = GetDlgCtrlID();
  773. info.item.iItem = nItem;
  774. info.item.lParam = -1;
  775. info.item.mask = LVIF_PARAM;
  776. pParent->SendMessage(WM_NOTIFY,(WPARAM)info.hdr.idFrom,(LPARAM)&info);
  777. return info.item.lParam;
  778. }
  779. }
  780. return CListCtrl::GetItemData(nItem);
  781. }
  782. void CQListCtrl::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
  783. {
  784. CListCtrl::OnVScroll(nSBCode, nPos, pScrollBar);
  785. }
  786. void CQListCtrl::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
  787. {
  788. CListCtrl::OnHScroll(nSBCode, nPos, pScrollBar);
  789. }
  790. void CQListCtrl::DestroyAndCreateAccelerator(BOOL bCreate)
  791. {
  792. if( bCreate )
  793. CMainTable::LoadAcceleratorKeys( m_Accels );
  794. }
  795. void CQListCtrl::OnKillFocus(CWnd* pNewWnd)
  796. {
  797. CListCtrl::OnKillFocus(pNewWnd);
  798. m_pToolTip->Hide();
  799. }
  800. BOOL CQListCtrl::SetItemCountEx(int iCount, DWORD dwFlags /* = LVSICF_NOINVALIDATEALL */)
  801. {
  802. theApp.SetStatus(NULL, TRUE);
  803. return CListCtrl::SetItemCountEx(iCount, dwFlags);
  804. }
  805. #define TIMER_SHOW_PROPERTIES 1
  806. void CQListCtrl::OnSelectionChange(NMHDR* pNMHDR, LRESULT* pResult)
  807. {
  808. NMLISTVIEW *pnmv = (NMLISTVIEW *) pNMHDR;
  809. if((pnmv->uNewState == 3) ||
  810. (pnmv->uNewState == 1))
  811. {
  812. if(g_Opt.m_bAllwaysShowDescription)
  813. {
  814. KillTimer(TIMER_SHOW_PROPERTIES);
  815. SetTimer(TIMER_SHOW_PROPERTIES, 300, NULL);
  816. }
  817. if(GetSelectedCount() > 0 )
  818. theApp.SetStatus(NULL, FALSE);
  819. }
  820. }
  821. void CQListCtrl::OnTimer(UINT nIDEvent)
  822. {
  823. if(nIDEvent == TIMER_SHOW_PROPERTIES)
  824. {
  825. if( theApp.m_bShowingQuickPaste )
  826. ShowFullDescription(true);
  827. KillTimer(TIMER_SHOW_PROPERTIES);
  828. }
  829. CListCtrl::OnTimer(nIDEvent);
  830. }
  831. void CQListCtrl::SetLogFont(LOGFONT &font)
  832. {
  833. m_Font.DeleteObject();
  834. m_Font.CreateFontIndirect(&font);
  835. SetFont(&m_Font);
  836. }