windows-nt/Source/XPSP1/NT/sdktools/tweakui/lv.cpp

585 lines
16 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*
* lv - common dialog proc handler for listview pages
*/
#include "tweakui.h"
#pragma BEGIN_CONST_DATA
#pragma END_CONST_DATA
/*****************************************************************************
*
* LV_AddItem
*
* Add an entry to the listview.
*
* Returns the resulting item number.
*
*****************************************************************************/
int PASCAL
LV_AddItem(HWND hwnd, int ix, LPCTSTR ptszDesc, int iImage, BOOL fState)
{
LV_ITEM lvi;
lvi.mask = LVIF_TEXT | LVIF_PARAM;
lvi.iItem = MAXLONG;
lvi.iSubItem = 0; /* Must be zero */
lvi.pszText = (LPTSTR)ptszDesc;
lvi.lParam = ix; /* Take it */
if (iImage >= 0) {
lvi.iImage = iImage;
lvi.mask |= LVIF_IMAGE;
}
if (fState >= 0) {
lvi.state = INDEXTOSTATEIMAGEMASK(fState + 1);
lvi.mask |= LVIF_STATE;
}
return ListView_InsertItem(hwnd, &lvi);
}
/*****************************************************************************
*
* LV_Toggle
*
* Toggle the state icon of the current selection.
*
*****************************************************************************/
void PASCAL
LV_Toggle(HWND hwnd, int iItem)
{
LV_ITEM lvi;
lvi.stateMask = LVIS_STATEIMAGEMASK;
Misc_LV_GetItemInfo(hwnd, &lvi, iItem, LVIF_STATE);
if (lvi.state & LVIS_STATEIMAGEMASK) {
lvi.state ^= INDEXTOSTATEIMAGEMASK(1) ^
INDEXTOSTATEIMAGEMASK(2); /* toggle checkmark */
ListView_SetItem(hwnd, &lvi); /* Set the state */
Common_SetDirty(GetParent(hwnd));
}
}
/*****************************************************************************
*
* LV_Rename
*
* Rename an item in the list view.
*
*****************************************************************************/
void PASCAL
LV_Rename(HWND hwnd, int iItem)
{
ListView_EditLabel(hwnd, iItem);
}
/*****************************************************************************
*
* LV_ResizeReportColumn
*
* Resize the column in the report to be as large as possible without
* colliding with the vertical scrollbar (if any).
*
*****************************************************************************/
void PASCAL
LV_ResizeReportColumn(HWND hwnd)
{
ListView_SetColumnWidth(hwnd, 0, LVSCW_AUTOSIZE);
}
/*****************************************************************************
*
* LV_OnInitDialog
*
* The callback will initialize the listview.
*
* Once the callback is happy, we initialize the columns.
*
* All of our listviews are simple reports with but one column.
*
*****************************************************************************/
/* New IE5 feature we will use if available */
#define LVS_EX_LABELTIP 0x00004000
#define LVM_SETEXTENDEDLISTVIEWSTYLE (LVM_FIRST + 54)
#define ListView_SetExtendedListViewStyle(hwndLV, dw)\
(DWORD)SNDMSG((hwndLV), LVM_SETEXTENDEDLISTVIEWSTYLE, 0, dw)
BOOL PASCAL
LV_OnInitDialog(PLVV plvv, HWND hdlg)
{
HWND hwnd = GetDlgItem(hdlg, IDC_LISTVIEW);
ListView_SetExtendedListViewStyle(hwnd, LVS_EX_LABELTIP);
if (plvv->lvvfl & lvvflCanCheck) {
ListView_SetImageList(hwnd, pcdii->himlState, LVSIL_STATE);
}
if (plvv->lvvfl & lvvflIcons) {
ListView_SetImageList(hwnd, GetSystemImageList(SHGFI_SMALLICON),
LVSIL_SMALL);
}
if (plvv->OnInitDialog(hwnd)) { /* Add the column to the report */
LV_COLUMN col;
col.mask = 0;
ListView_InsertColumn(hwnd, 0, &col);
LV_ResizeReportColumn(hwnd);
Misc_LV_SetCurSel(hwnd, 0);
}
return 1;
}
/*****************************************************************************
*
* LV_OnLvContextMenu
*
* The context menu shall appear in the listview.
*
* Allow the callback to modify the menu before we pop it up.
* Then let the window procedure's WM_COMMAND do the rest.
*
* If lvvflCanCheck is set, we will automatically adjust IDC_LVTOGGLE
* to match.
*
* If lvvflCanDelete is set, we will adjust IDC_LVDELETE to match.
*
*****************************************************************************/
void PASCAL
LV_OnLvContextMenu(PLVV plvv, HWND hdlg, HWND hwnd, POINT pt)
{
int iItem = Misc_LV_GetCurSel(hwnd);
if (iItem != -1) {
HMENU hmenu;
ClientToScreen(hwnd, &pt); /* Make it screen coordinates */
hmenu = GetSubMenu(pcdii->hmenu, plvv->iMenu);
if (plvv->lvvfl & lvvflCanCheck) {
MENUITEMINFO mii;
LV_ITEM lvi;
lvi.stateMask = LVIS_STATEIMAGEMASK;
Misc_LV_GetItemInfo(hwnd, &lvi, iItem, LVIF_STATE);
mii.cbSize = cbX(mii);
mii.fMask = MIIM_STATE;
switch (isiPlvi(&lvi)) {
case isiUnchecked: mii.fState = MFS_ENABLED | MFS_UNCHECKED; break;
case isiChecked: mii.fState = MFS_ENABLED | MFS_CHECKED; break;
default: mii.fState = MFS_DISABLED; break;
}
SetMenuItemInfo(hmenu, IDC_LVTOGGLE, 0, &mii);
}
if (plvv->lvvfl & lvvflCanDelete) {
Misc_EnableMenuFromHdlgId(hmenu, hdlg, IDC_LVDELETE);
}
if (plvv->OnInitContextMenu) {
plvv->OnInitContextMenu(hwnd, iItem, hmenu);
}
TrackPopupMenuEx(hmenu, TPM_RIGHTBUTTON | TPM_VERTICAL |
TPM_LEFTALIGN | TPM_TOPALIGN, pt.x, pt.y, hdlg, 0);
}
}
/*****************************************************************************
*
* LV_OnContextMenu
*
* If the context menu came from the listview, figure out which
* item got clicked on. If we find an item, pop up its context
* menu. Otherwise, just do the standard help thing.
*
* NOTE! We don't use LVHT_ONITEM because ListView is broken!
* Watch:
*
* #define LVHT_ONITEMSTATEICON 0x0008
* #define LVHT_ABOVE 0x0008
*
* Oops. This means that clicks above the item are treated as
* clicks on the state icon.
*
* Fortunately, we reside completely in report view, so you can't
* legally click above the item. The only way it can happen is
* if the coordinates to OnContextMenu are out of range, so we
* catch that up front and munge it accordingly.
*
*****************************************************************************/
void PASCAL
LV_OnContextMenu(PLVV plvv, HWND hdlg, HWND hwnd, LPARAM lp)
{
if (GetDlgCtrlID(hwnd) == IDC_LISTVIEW) {
LV_HITTESTINFO hti;
if ((DWORD)lp == 0xFFFFFFFF) {
/* Pretend it was on the center of the small icon */
ListView_GetItemPosition(hwnd, Misc_LV_GetCurSel(hwnd),
&hti.pt);
hti.pt.x += GetSystemMetrics(SM_CXSMICON) / 2;
hti.pt.y += GetSystemMetrics(SM_CYSMICON) / 2;
LV_OnLvContextMenu(plvv, hdlg, hwnd, hti.pt);
} else {
Misc_LV_HitTest(hwnd, &hti, lp);
if ((hti.flags & LVHT_ONITEM)) {
/* Because LV sometimes forgets to move the focus... */
Misc_LV_SetCurSel(hwnd, hti.iItem);
LV_OnLvContextMenu(plvv, hdlg, hwnd, hti.pt);
} else {
Common_OnContextMenu((WPARAM)hwnd, plvv->pdwHelp);
}
}
} else {
Common_OnContextMenu((WPARAM)hwnd, plvv->pdwHelp);
}
}
/*****************************************************************************
*
* LV_OnCommand_Dispatch
*
* Dispatch a recognized command to the handler.
*
*****************************************************************************/
void PASCAL
LV_OnCommand_Dispatch(void (PASCAL *pfn)(HWND hwnd, int iItem), HWND hdlg)
{
HWND hwnd = GetDlgItem(hdlg, IDC_LISTVIEW);
int iItem = Misc_LV_GetCurSel(hwnd);
if (iItem != -1) {
pfn(hwnd, iItem);
}
}
/*****************************************************************************
*
* LV_OnCommand
*
* Ooh, we got a command.
*
* See if it's one of ours. If not, pass it through to the handler.
*
*****************************************************************************/
BOOL PASCAL
LV_OnCommand(PLVV plvv, HWND hdlg, int id, UINT codeNotify)
{
PLVCI plvci;
switch (id) {
case IDC_LVTOGGLE:
LV_OnCommand_Dispatch(LV_Toggle, hdlg);
break;
case IDC_LVRENAME:
if (plvv->Dirtify) LV_OnCommand_Dispatch(LV_Rename, hdlg);
break;
default:
if (plvv->rglvci) {
for (plvci = plvv->rglvci; plvci[0].id; plvci++) {
if (id == plvci->id) {
LV_OnCommand_Dispatch(plvci->pfn, hdlg);
goto dispatched;
}
}
}
if (plvv->OnCommand) {
plvv->OnCommand(hdlg, id, codeNotify);
}
dispatched:;
break;
}
return 0;
}
/*****************************************************************************
*
* LV_OnNotify_OnClick
*
* Somebody clicked or double-clicked on the listview.
*
*****************************************************************************/
void PASCAL
LV_OnNotify_OnClick(PLVV plvv, HWND hwnd, NMHDR FAR *pnm)
{
LV_HITTESTINFO hti;
Misc_LV_HitTest(hwnd, &hti, (LPARAM)GetMessagePos());
/*
* A click/dblclick on the item state icon toggles.
*
* A click/dblclick anywhere toggles *if* you can't rename.
*/
if (((hti.flags & LVHT_ONITEMSTATEICON) ||
((hti.flags & LVHT_ONITEM) && !(plvv->lvvfl & lvvflCanRename)))) {
Misc_LV_SetCurSel(hwnd, hti.iItem); /* LV doesn't do this, oddly */
LV_Toggle(hwnd, hti.iItem);
} else if (pnm->code == NM_DBLCLK && plvv->idDblClk) {
LV_OnCommand(plvv, GetParent(hwnd), plvv->idDblClk, 0);
} else if (pnm->code == NM_DBLCLK && (hti.flags & LVHT_ONITEMLABEL)) {
if (plvv->lvvfl & lvvflCanRename) {
LV_Rename(hwnd, hti.iItem);
}
}
}
/*****************************************************************************
*
* LV_OnNotify_OnKeyDown
*
* Somebody pressed a key while focus is on a listview.
*
* F2 = Rename
* Space = Toggle
* Del = Delete
*
*****************************************************************************/
void PASCAL
LV_OnNotify_OnKeyDown(PLVV plvv, HWND hwnd, LV_KEYDOWN FAR *lvkd)
{
int iItem = Misc_LV_GetCurSel(hwnd);
if (iItem != -1) {
switch (lvkd->wVKey) {
case VK_SPACE:
/*
* But not if the ALT key is down!
*/
if (GetKeyState(VK_MENU) >= 0) {
LV_Toggle(hwnd, iItem);
}
break;
case VK_F2:
if (plvv->lvvfl & lvvflCanRename) {
LV_Rename(hwnd, iItem);
}
break;
case VK_DELETE:
LV_OnCommand(plvv, GetParent(hwnd), IDC_LVDELETE, 0); break;
break;
}
}
}
/*****************************************************************************
*
* LV_OnNotify_OnBeginLabelEdit
*
* Allow it to go through if label editing is permitted.
*
*****************************************************************************/
BOOL INLINE
LV_OnNotify_OnBeginLabelEdit(PLVV plvv, HWND hdlg, HWND hwnd)
{
if (plvv->lvvfl & lvvflCanRename) {
return 0;
} else {
SetWindowLongPtr(hdlg, DWLP_MSGRESULT, TRUE);
return 1;
}
}
/*****************************************************************************
*
* LV_OnNotify_OnEndLabelEdit
*
* Trim leading and trailing whitespace. If there's anything left,
* then we'll accept it.
*
*****************************************************************************/
void PASCAL
LV_OnNotify_OnEndLabelEdit(PLVV plvv, HWND hwnd, LV_DISPINFO FAR *lpdi)
{
if (lpdi->item.iItem != -1 && lpdi->item.pszText) {
LV_ITEM lvi;
lvi.pszText = Misc_Trim(lpdi->item.pszText);
if (lvi.pszText[0]) {
Misc_LV_GetItemInfo(hwnd, &lvi, lpdi->item.iItem, LVIF_PARAM);
lvi.mask ^= LVIF_TEXT ^ LVIF_PARAM;
ListView_SetItem(hwnd, &lvi);
Common_SetDirty(GetParent(hwnd));
plvv->Dirtify(lvi.lParam);
}
}
}
/*****************************************************************************
*
* LV_OnNotify_OnItemChanged
*
* If we are being told about a new selection, call the callback.
*
*****************************************************************************/
void PASCAL
LV_OnNotify_OnItemChanged(PLVV plvv, HWND hwnd, NM_LISTVIEW *pnmlv)
{
if ((pnmlv->uChanged & LVIF_STATE) && (pnmlv->uNewState & LVIS_SELECTED)) {
if (plvv->OnSelChange) {
plvv->OnSelChange(hwnd, pnmlv->iItem);
}
}
}
/*****************************************************************************
*
* LV_OnNotify
*
* Ooh, we got a notification. See if it's something we recognize.
*
* NOTE! We don't support private notifications.
*
*****************************************************************************/
BOOL PASCAL
LV_OnNotify(PLVV plvv, HWND hdlg, NMHDR FAR *pnm)
{
switch (pnm->idFrom) {
case 0: /* Property sheet */
switch (pnm->code) {
case PSN_APPLY:
plvv->OnApply(hdlg);
break;
}
break;
case IDC_LISTVIEW: /* List view */
{
HWND hwnd = GetDlgItem(hdlg, IDC_LISTVIEW);
switch (pnm->code) {
case NM_CLICK:
case NM_DBLCLK:
LV_OnNotify_OnClick(plvv, hwnd, pnm);
break;
case LVN_KEYDOWN:
LV_OnNotify_OnKeyDown(plvv, hwnd, (LV_KEYDOWN *)pnm);
break;
case LVN_BEGINLABELEDIT:
return LV_OnNotify_OnBeginLabelEdit(plvv, hdlg, hwnd);
case LVN_ENDLABELEDIT:
LV_OnNotify_OnEndLabelEdit(plvv, hwnd, (LV_DISPINFO *)pnm);
break;
case LVN_ITEMCHANGED:
LV_OnNotify_OnItemChanged(plvv, hwnd, (NM_LISTVIEW *)pnm);
break;
}
}
break;
}
return 0;
}
/*****************************************************************************
*
* LV_OnSettingChange
*
*****************************************************************************/
void PASCAL
LV_OnSettingChange(PLVV plvv, HWND hdlg, WPARAM wp, LPARAM lp)
{
HWND hwnd = GetDlgItem(hdlg, IDC_LISTVIEW);
SendMessage(hwnd, WM_SETTINGCHANGE, wp, lp);
if (wp == SPI_SETNONCLIENTMETRICS) {
/* If we have icons, then go rebuild them. */
if (plvv->GetIcon) {
int iItem;
int cItem = ListView_GetItemCount(hwnd);
for (iItem = 0; iItem < cItem; iItem++) {
LV_ITEM lvi;
Misc_LV_GetItemInfo(hwnd, &lvi, iItem, LVIF_PARAM);
lvi.iImage = plvv->GetIcon(lvi.lParam);
lvi.mask |= LVIF_IMAGE;
ListView_SetItem(hwnd, &lvi);
}
}
/* In case the scrollbars changed size, resize to accomodate */
LV_ResizeReportColumn(hwnd);
/*
* HACK AROUND BUG IN COMCTL32.DLL - Explicitly hide and show
* the window. This tickles report view into recalculating
* its scrollbars.
*/
ShowWindow(hwnd, SW_HIDE);
ShowWindow(hwnd, SW_SHOW);
}
/*
* Note: Do not need to handle WM_SETICONTITLELOGFONT because we
* are in a dialog and therefore will use the dialog font, not the
* icon title LOGFONT.
*/
}
/*****************************************************************************
*
* The common listview window procedure.
*
*****************************************************************************/
BOOL EXPORT
LV_DlgProc(PLVV plvv, HWND hdlg, UINT wm, WPARAM wParam, LPARAM lParam)
{
switch (wm) {
case WM_INITDIALOG:
return LV_OnInitDialog(plvv, hdlg);
case WM_COMMAND:
return LV_OnCommand(plvv, hdlg,
(int)GET_WM_COMMAND_ID(wParam, lParam),
(UINT)GET_WM_COMMAND_CMD(wParam, lParam));
case WM_HELP: Common_OnHelp(lParam, plvv->pdwHelp); break;
case WM_NOTIFY:
return LV_OnNotify(plvv, hdlg, (NMHDR FAR *)lParam);
case WM_SYSCOLORCHANGE:
FORWARD_WM_SYSCOLORCHANGE(GetDlgItem(hdlg, IDC_LISTVIEW), SendMessage);
break;
case WM_DESTROY:
if (plvv->OnDestroy) plvv->OnDestroy(hdlg);
break;
case WM_CONTEXTMENU:
LV_OnContextMenu(plvv, hdlg, (HWND)wParam, lParam);
break;
case WM_SETTINGCHANGE:
LV_OnSettingChange(plvv, hdlg, wParam, lParam);
break;
default: return 0; /* Unhandled */
}
return 1; /* Handled */
}