303 lines
8.4 KiB
C++
303 lines
8.4 KiB
C++
|
/*****************************************************************************
|
||
|
*
|
||
|
* lvframe.cpp
|
||
|
*
|
||
|
* Frame window that hosts a listview.
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
#include "sdview.h"
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* class LVFrame
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
LRESULT
|
||
|
LVFrame::HandleMessage(UINT uiMsg, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
switch (uiMsg) {
|
||
|
FW_MSG(WM_NOTIFY);
|
||
|
FW_MSG(WM_COMMAND);
|
||
|
FW_MSG(WM_CONTEXTMENU);
|
||
|
}
|
||
|
|
||
|
return super::HandleMessage(uiMsg, wParam, lParam);
|
||
|
}
|
||
|
|
||
|
LRESULT LVFrame::ON_WM_NOTIFY(UINT uiMsg, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
NMHDR *pnm = RECAST(NMHDR *, lParam);
|
||
|
|
||
|
if (pnm->idFrom == IDC_LIST) {
|
||
|
switch (pnm->code) {
|
||
|
|
||
|
case LVN_ITEMACTIVATE:
|
||
|
{
|
||
|
NMITEMACTIVATE *pia = CONTAINING_RECORD(pnm, NMITEMACTIVATE, hdr);
|
||
|
return SendSelfMessage(LM_ITEMACTIVATE, pia->iItem, 0);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case LVN_GETINFOTIP:
|
||
|
{
|
||
|
NMLVGETINFOTIP *pgit = CONTAINING_RECORD(pnm, NMLVGETINFOTIP, hdr);
|
||
|
LPTSTR pszBuf = pgit->pszText;
|
||
|
SendSelfMessage(LM_GETINFOTIP, pgit->iItem, RECAST(LPARAM, pgit));
|
||
|
LPTSTR pszInfoTip = pgit->pszText;
|
||
|
pgit->pszText = pszBuf;
|
||
|
_it.SetInfoTip(pgit, pszInfoTip);
|
||
|
}
|
||
|
return 0;
|
||
|
|
||
|
case LVN_DELETEITEM:
|
||
|
{
|
||
|
NMLISTVIEW *plv = CONTAINING_RECORD(pnm, NMLISTVIEW, hdr);
|
||
|
SendSelfMessage(LM_DELETEITEM, plv->iItem, plv->lParam);
|
||
|
}
|
||
|
return 0;
|
||
|
|
||
|
default:;
|
||
|
}
|
||
|
}
|
||
|
return super::HandleMessage(uiMsg, wParam, lParam);
|
||
|
}
|
||
|
|
||
|
LRESULT LVFrame::ON_WM_COMMAND(UINT uiMsg, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
int iSel;
|
||
|
|
||
|
switch (GET_WM_COMMAND_ID(wParam, lParam)) {
|
||
|
case IDM_COPY:
|
||
|
iSel = GetCurSel();
|
||
|
if (iSel >= 0) {
|
||
|
SendSelfMessage(LM_COPYTOCLIPBOARD, iSel, iSel + 1);
|
||
|
}
|
||
|
return 0;
|
||
|
|
||
|
case IDM_COPYALL:
|
||
|
SendSelfMessage(LM_COPYTOCLIPBOARD, 0, ListView_GetItemCount(_hwndChild));
|
||
|
return 0;
|
||
|
}
|
||
|
return super::HandleMessage(uiMsg, wParam, lParam);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
LRESULT LVFrame::ON_WM_CONTEXTMENU(UINT uiMsg, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
int iItem;
|
||
|
HMENU hmenu;
|
||
|
|
||
|
if ((DWORD)lParam == 0xFFFFFFFF) {
|
||
|
iItem = ListView_GetCurSel(_hwndChild);
|
||
|
if (iItem < 0) {
|
||
|
goto fail;
|
||
|
}
|
||
|
|
||
|
RECT rc;
|
||
|
if (!ListView_GetItemRect(_hwndChild, iItem, &rc, LVIR_LABEL)) {
|
||
|
goto fail;
|
||
|
}
|
||
|
MapWindowRect(_hwndChild, HWND_DESKTOP, &rc);
|
||
|
int cyHalf = (rc.bottom - rc.top)/2;
|
||
|
lParam = MAKELPARAM(rc.left + cyHalf, rc.top + cyHalf);
|
||
|
} else {
|
||
|
LVHITTESTINFO hti;
|
||
|
hti.pt.x = GET_X_LPARAM(lParam);
|
||
|
hti.pt.y = GET_Y_LPARAM(lParam);
|
||
|
ScreenToClient(_hwndChild, &hti.pt);
|
||
|
iItem = ListView_HitTest(_hwndChild, &hti);
|
||
|
if (iItem < 0) {
|
||
|
goto fail;
|
||
|
}
|
||
|
ListView_SetCurSel(_hwndChild, iItem);
|
||
|
}
|
||
|
|
||
|
hmenu = RECAST(HMENU, SendSelfMessage(LM_GETCONTEXTMENU, iItem, 0));
|
||
|
if (!hmenu) {
|
||
|
goto fail;
|
||
|
}
|
||
|
|
||
|
TrackPopupMenuEx(hmenu,
|
||
|
TPM_LEFTALIGN | TPM_TOPALIGN |
|
||
|
TPM_RIGHTBUTTON | TPM_NONOTIFY,
|
||
|
GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam),
|
||
|
_hwnd, NULL);
|
||
|
DestroyMenu(hmenu);
|
||
|
return 0;
|
||
|
|
||
|
fail:
|
||
|
return super::HandleMessage(uiMsg, wParam, lParam);
|
||
|
}
|
||
|
|
||
|
BOOL LVFrame::CreateChild(DWORD dwStyle, DWORD dwExStyle)
|
||
|
{
|
||
|
_hwndChild = CreateWindowEx(WS_EX_CLIENTEDGE,
|
||
|
WC_LISTVIEW, NULL,
|
||
|
WS_CHILD | WS_VISIBLE |
|
||
|
WS_BORDER | WS_TABSTOP |
|
||
|
WS_CLIPSIBLINGS | WS_CLIPCHILDREN |
|
||
|
dwStyle,
|
||
|
0,0,0,0,
|
||
|
_hwnd, RECAST(HMENU, IDC_LIST), g_hinst, 0);
|
||
|
|
||
|
if (!_hwndChild) return FALSE;
|
||
|
|
||
|
ListView_SetExtendedListViewStyleEx(_hwndChild, dwExStyle, dwExStyle);
|
||
|
|
||
|
SetFocus(_hwndChild);
|
||
|
|
||
|
_it.Attach(_hwndChild);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
BOOL LVFrame::AddColumns(const LVFCOLUMN *pcol)
|
||
|
{
|
||
|
int cxChar = LOWORD(GetDialogBaseUnits());
|
||
|
|
||
|
for (; pcol->cch; pcol++) {
|
||
|
LVCOLUMN lvc;
|
||
|
TCHAR szName[MAX_PATH];
|
||
|
LoadString(g_hinst, pcol->ids, szName, ARRAYSIZE(szName));
|
||
|
|
||
|
lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
|
||
|
lvc.fmt = pcol->fmt;
|
||
|
lvc.pszText = szName;
|
||
|
lvc.cx = pcol->cch * cxChar;
|
||
|
ListView_InsertColumn(_hwndChild, MAXLONG, &lvc);
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
void *LVFrame::GetLVItem(int iItem)
|
||
|
{
|
||
|
LVITEM lvi;
|
||
|
lvi.iItem = iItem;
|
||
|
lvi.iSubItem = 0;
|
||
|
lvi.mask = LVIF_PARAM;
|
||
|
lvi.lParam = 0;
|
||
|
ListView_GetItem(_hwndChild, &lvi);
|
||
|
return RECAST(LPVOID, lvi.lParam);
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* LVInfoTip
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
void LVInfoTip::Attach(HWND hwnd)
|
||
|
{
|
||
|
if (SetProp(hwnd, GetSubclassProperty(), RECAST(HANDLE, this))) {
|
||
|
_wndprocPrev = SubclassWindow(hwnd, SubclassWndProc);
|
||
|
}
|
||
|
|
||
|
// Those infotips can get really long, so set the autopop delay
|
||
|
// to the maximum allowable value.
|
||
|
HWND hwndTT = ListView_GetToolTips(hwnd);
|
||
|
if (hwndTT) {
|
||
|
SendMessage(hwndTT, TTM_SETDELAYTIME, TTDT_AUTOPOP, MAXSHORT);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
void LVInfoTip::FreeLastTipAlt()
|
||
|
{
|
||
|
if (_pszLastTipAlt) {
|
||
|
LPSSTR pszFree = _pszLastTipAlt;
|
||
|
_pszLastTipAlt = NULL;
|
||
|
delete [] pszFree;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void LVInfoTip::SetInfoTip(NMLVGETINFOTIP *pgit, LPCTSTR pszTip)
|
||
|
{
|
||
|
_pszLastTip = NULL;
|
||
|
FreeLastTipAlt();
|
||
|
|
||
|
if (pgit->pszText != pszTip) {
|
||
|
if (lstrlen(pszTip) >= pgit->cchTextMax) {
|
||
|
_pszLastTip = pszTip;
|
||
|
}
|
||
|
lstrcpyn(pgit->pszText, pszTip, pgit->cchTextMax);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Convert from TCHAR to SCHAR.
|
||
|
//
|
||
|
inline int T2S(LPCTSTR pszIn, int cchIn, LPSSTR pszOut, int cchOut)
|
||
|
{
|
||
|
#ifdef UNICODE
|
||
|
return WideCharToMultiByte(CP_ACP, 0, pszIn, cchIn, pszOut, cchOut, NULL, NULL);
|
||
|
#else
|
||
|
return MultiByteToWideChar(CP_ACP, 0, pszIn, cchIn, pszOut, cchOut);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Make _pszLastTipAlt match _pszLastTip, but of opposite character set.
|
||
|
//
|
||
|
BOOL LVInfoTip::ThunkLastTip()
|
||
|
{
|
||
|
ASSERT(_pszLastTip);
|
||
|
FreeLastTipAlt();
|
||
|
int cch = T2S(_pszLastTip, -1, NULL, 0);
|
||
|
if (cch) {
|
||
|
_pszLastTipAlt = new SCHAR[cch];
|
||
|
if (_pszLastTipAlt &&
|
||
|
T2S(_pszLastTip, -1, _pszLastTipAlt, cch)) {
|
||
|
return TRUE;
|
||
|
}
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
LRESULT LVInfoTip::SubclassWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
LVInfoTip *self = RECAST(LVInfoTip *, GetProp(hwnd, GetSubclassProperty()));
|
||
|
if (self) {
|
||
|
LRESULT lres;
|
||
|
NMHDR *pnm;
|
||
|
switch (uMsg) {
|
||
|
case WM_NOTIFY:
|
||
|
pnm = RECAST(NMHDR *, lParam);
|
||
|
switch (pnm->code) {
|
||
|
case TTN_GETDISPINFOA:
|
||
|
case TTN_GETDISPINFOW:
|
||
|
lres = CallWindowProc(self->_wndprocPrev, hwnd, uMsg, wParam, lParam);
|
||
|
if (SendMessage(pnm->hwndFrom, TTM_GETMAXTIPWIDTH, 0, 0) >= 0) {
|
||
|
|
||
|
// It's an infotip. Tweak it to suit our needs.
|
||
|
NMTTDISPINFO *ptdi = CONTAINING_RECORD(pnm, NMTTDISPINFO, hdr);
|
||
|
|
||
|
// Set the width to the maximum allowed without going single-line.
|
||
|
SendMessage(pnm->hwndFrom, TTM_SETMAXTIPWIDTH, 0, MAXLONG);
|
||
|
|
||
|
// If we overflowed the returned buffer, then listview used
|
||
|
// only a partial infotip. So fill in the rest here.
|
||
|
if (self->_pszLastTip) {
|
||
|
if (pnm->code == TTN_GETDISPINFO) {
|
||
|
ptdi->lpszText = CCAST(LPTSTR, self->_pszLastTip);
|
||
|
} else {
|
||
|
if (self->ThunkLastTip()) {
|
||
|
ptdi->lpszText = RECAST(LPTSTR, self->_pszLastTipAlt);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
self->_pszLastTip = NULL;
|
||
|
}
|
||
|
return lres;
|
||
|
}
|
||
|
}
|
||
|
return CallWindowProc(self->_wndprocPrev, hwnd, uMsg, wParam, lParam);
|
||
|
} else {
|
||
|
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
||
|
}
|
||
|
}
|