Windows-Server-2003/sdktools/sdv/lvframe.cpp

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);
}
}