windows-nt/Source/XPSP1/NT/shell/shell32/defviewcoldlg.cpp
2020-09-26 16:20:57 +08:00

493 lines
16 KiB
C++

#include "shellprv.h"
#include "defviewp.h"
#include "ids.h"
CColumnDlg::CColumnDlg(CDefView *pdsv) :
_pdsv(pdsv), _bChanged(FALSE), _pdwOrder(NULL), _pWidths(NULL), _bLoaded(FALSE), _bUpdating(FALSE), _ppui(NULL)
{
_cColumns = _pdsv->_vs.GetColumnCount();
}
CColumnDlg::~CColumnDlg()
{
if (_pdwOrder)
LocalFree(_pdwOrder);
if (_pWidths)
LocalFree(_pWidths);
if (_ppui)
_ppui->Release();
}
HRESULT CColumnDlg::ShowDialog(HWND hwnd)
{
_bChanged = FALSE; // We are on the stack, so no zero allocator
_pdwOrder = (UINT *) LocalAlloc(LPTR, sizeof(*_pdwOrder) * _cColumns); // total columns
_pWidths = (int *) LocalAlloc(LPTR, sizeof(*_pWidths) * _cColumns); // total columns
if (_pdwOrder && _pWidths)
{
DialogBoxParam(HINST_THISDLL, MAKEINTRESOURCE(DLG_COLUMN_SETTINGS), hwnd, s_DlgProc, (LPARAM)this);
return S_OK;
}
return E_OUTOFMEMORY;
}
// Remember, each column is identified in 3 ways...
// 1. A 'real' column number, the ordinal out of all possible columns
// 2. A 'visible' column number, the index to this column in the listview
// 3. A 'column order #', the position in the header's columnorderarray
void CColumnDlg::_OnInitDlg()
{
// Fill in order array with visible columns, and set up inverse table
UINT cVisible = _pdsv->_RealToVisibleCol(-1) + 1; // count
ListView_GetColumnOrderArray(_pdsv->_hwndListview, cVisible, _pdwOrder);
UINT *pOrderInverse = (UINT *)LocalAlloc(LPTR, sizeof(*pOrderInverse) * cVisible);
if (pOrderInverse)
{
for (UINT i = 0; i < cVisible; i++)
pOrderInverse[_pdwOrder[i]] = i;
_hwndLVAll = GetDlgItem(_hdlg, IDC_COL_LVALL);
ListView_SetExtendedListViewStyle(_hwndLVAll, LVS_EX_CHECKBOXES);
LV_COLUMN lvc = {0};
lvc.mask = (LVCF_FMT | LVCF_SUBITEM);
lvc.fmt = LVCFMT_LEFT;
ListView_InsertColumn(_hwndLVAll, 0, &lvc);
LV_ITEM lvi = {0};
lvi.mask = LVIF_TEXT;
// Add entry for each column (except non-UI columns)
for (i = 0; i < (int)_cColumns; i++)
{
if (!_pdsv->_IsColumnHidden(i)) // Don't put in entries for hidden columns
{
lvi.iItem = i;
lvi.pszText = LPSTR_TEXTCALLBACK;
ListView_InsertItem(_hwndLVAll, &lvi);
}
}
lvi.mask = LVIF_TEXT | LVIF_STATE | LVIF_PARAM;
// set the visible columns
for (i = 0; i < (int) cVisible; i++)
{
UINT iReal = _pdsv->_VisibleToRealCol(i);
lvi.pszText = _pdsv->_vs.GetColumnName(iReal);
lvi.state = INDEXTOSTATEIMAGEMASK(_pdsv->_IsDetailsColumn(iReal) ? 2 : 1); // on check mark (or off for tileview columns)
lvi.stateMask = LVIS_STATEIMAGEMASK;
lvi.lParam = iReal; // store the real col index in the lParam
lvi.iItem = pOrderInverse[i];
ListView_SetItem(_hwndLVAll, &lvi);
// Get the column width from the view's listview
_pWidths[iReal] = ListView_GetColumnWidth(_pdsv->_hwndListview, i);
}
UINT iItem = cVisible;
for (i = 0; i < (int)_cColumns; i++)
{
if (!_pdsv->_IsColumnInListView(i) && !_pdsv->_IsColumnHidden(i))
{
lvi.pszText = _pdsv->_vs.GetColumnName(i);
lvi.state = INDEXTOSTATEIMAGEMASK(1); // off check mark
lvi.stateMask = LVIS_STATEIMAGEMASK;
lvi.lParam = i;
lvi.iItem = iItem;
ListView_SetItem(_hwndLVAll, &lvi);
iItem++;
// get the default width we've got saved away
_pWidths[i] = _pdsv->_vs.GetColumnCharCount(i) * _pdsv->_cxChar;
}
}
// set the size properly
ListView_SetColumnWidth(_hwndLVAll, 0, LVSCW_AUTOSIZE);
ListView_SetItemState(_hwndLVAll, 0, LVIS_FOCUSED|LVIS_SELECTED, LVIS_FOCUSED|LVIS_SELECTED);
LocalFree(pOrderInverse);
_bLoaded = TRUE;
}
SendDlgItemMessage(_hdlg, IDC_COL_WIDTH, EM_LIMITTEXT, 3, 0); // 3 digits
}
#define SWAP(x,y) {(x) ^= (y); (y) ^= (x); (x) ^= (y);}
void CColumnDlg::_MoveItem(int iDelta)
{
int i = ListView_GetSelectionMark(_hwndLVAll);
if (i != -1)
{
int iNew = i + iDelta;
if (iNew >= 0 && iNew <= (ListView_GetItemCount(_hwndLVAll) - 1))
{
LV_ITEM lvi = {0}, lvi2 = {0};
TCHAR szTmp1[MAX_COLUMN_NAME_LEN], szTmp2[MAX_COLUMN_NAME_LEN];
_bChanged = TRUE;
_bUpdating = TRUE;
lvi.iItem = i;
lvi.pszText = szTmp1;
lvi.cchTextMax = ARRAYSIZE(szTmp1);
lvi.stateMask = LVIS_STATEIMAGEMASK;
lvi.mask = LVIF_TEXT | LVIF_STATE | LVIF_PARAM;
lvi2.iItem = iNew;
lvi2.pszText = szTmp2;
lvi2.cchTextMax = ARRAYSIZE(szTmp2);
lvi2.stateMask = LVIS_STATEIMAGEMASK;
lvi2.mask = LVIF_TEXT | LVIF_STATE | LVIF_PARAM;
ListView_GetItem(_hwndLVAll, &lvi);
ListView_GetItem(_hwndLVAll, &lvi2);
SWAP(lvi.iItem, lvi2.iItem);
ListView_SetItem(_hwndLVAll, &lvi);
ListView_SetItem(_hwndLVAll, &lvi2);
_bUpdating = FALSE;
// update selection
ListView_SetSelectionMark(_hwndLVAll, iNew);
ListView_SetItemState(_hwndLVAll, iNew , LVIS_FOCUSED|LVIS_SELECTED, LVIS_FOCUSED|LVIS_SELECTED);
// HACK: SetItemState sends notifications for i, iNew, then i again.
// we need to call it twice in a row, so _UpdateDlgButtons will get the right item
ListView_SetItemState(_hwndLVAll, iNew , LVIS_FOCUSED|LVIS_SELECTED, LVIS_FOCUSED|LVIS_SELECTED);
return;
}
}
TraceMsg(TF_WARNING, "ccd.mi couldn't move %d to %d",i, i+iDelta);
MessageBeep(MB_ICONEXCLAMATION);
}
BOOL CColumnDlg::_SaveState()
{
// Check order
if (_bChanged)
{
int iOrderIndex = 0;
LV_ITEM lvi = {0};
lvi.stateMask = LVIS_STATEIMAGEMASK;
lvi.mask = LVIF_PARAM | LVIF_STATE;
int cItems = ListView_GetItemCount(_hwndLVAll);
for (int i = 0; i < cItems; i++)
{
lvi.iItem = i;
ListView_GetItem(_hwndLVAll, &lvi);
// toggle it, if the state in the dialog doesn't match the listview state
if (BOOLIFY(ListView_GetCheckState(_hwndLVAll, i)) != BOOLIFY(_pdsv->_IsDetailsColumn((UINT)lvi.lParam)))
{
_pdsv->_HandleColumnToggle((UINT)lvi.lParam, FALSE);
}
if (_pdsv->_IsColumnInListView((UINT)lvi.lParam))
_pdwOrder[iOrderIndex++] = (UINT)lvi.lParam; // incorrectly store real (not vis) col #, fix up below
}
// must be in a separate loop. (can't map real to visible, if we aren't done setting visible)
for (i = 0; i < iOrderIndex; i++)
{
UINT iReal = _pdwOrder[i];
_pdwOrder[i] = _pdsv->_RealToVisibleCol(iReal);
if (_pWidths[iReal] < 0) // negative width means they edited it
ListView_SetColumnWidth(_pdsv->_hwndListview, _pdwOrder[i], -_pWidths[iReal]);
}
ListView_SetColumnOrderArray(_pdsv->_hwndListview, iOrderIndex, _pdwOrder);
// kick the listview into repainting everything
InvalidateRect(_pdsv->_hwndListview, NULL, TRUE);
_bChanged = FALSE;
}
return !_bChanged;
}
BOOL EnableDlgItem(HWND hdlg, UINT idc, BOOL f)
{
return EnableWindow(GetDlgItem(hdlg, idc), f);
}
void CColumnDlg::_UpdateDlgButtons(NMLISTVIEW *pnmlv)
{
BOOL bChecked, bOldUpdateState = _bUpdating;
int iItem = ListView_GetSelectionMark(_hwndLVAll);
// to disable checking
_bUpdating = TRUE;
if (pnmlv->uNewState & LVIS_STATEIMAGEMASK)
bChecked = (pnmlv->uNewState & LVIS_STATEIMAGEMASK) == (UINT)INDEXTOSTATEIMAGEMASK(2);
else
bChecked = ListView_GetCheckState(_hwndLVAll, pnmlv->iItem);
EnableDlgItem(_hdlg, IDC_COL_UP, pnmlv->iItem > 0);
EnableDlgItem(_hdlg, IDC_COL_DOWN, pnmlv->iItem < (int)_cColumns - 1);
EnableDlgItem(_hdlg, IDC_COL_SHOW, !bChecked && (pnmlv->lParam != 0));
EnableDlgItem(_hdlg, IDC_COL_HIDE, bChecked && (pnmlv->lParam != 0));
// update the width edit box
int iWidth = _pWidths[pnmlv->lParam];
if (iWidth < 0)
iWidth = -iWidth; // we store negative values to track if it changed or not
SetDlgItemInt(_hdlg, IDC_COL_WIDTH, iWidth, TRUE);
_bUpdating = bOldUpdateState;
}
BOOL_PTR CALLBACK CColumnDlg::s_DlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CColumnDlg *pcd = (CColumnDlg*) GetWindowLongPtr(hdlg, DWLP_USER);
if (uMsg == WM_INITDIALOG)
{
pcd = (CColumnDlg *) lParam;
pcd->_hdlg = hdlg;
SetWindowLongPtr(hdlg, DWLP_USER, (LONG_PTR) pcd);
}
return pcd ? pcd->DlgProc(uMsg, wParam, lParam) : FALSE;
}
HRESULT CColumnDlg::_GetPropertyUI(IPropertyUI **pppui)
{
if (!_ppui)
SHCoCreateInstance(NULL, &CLSID_PropertiesUI, NULL, IID_PPV_ARG(IPropertyUI, &_ppui));
return _ppui ? _ppui->QueryInterface(IID_PPV_ARG(IPropertyUI, pppui)) : E_NOTIMPL;
}
UINT CColumnDlg::_HelpIDForItem(int iItem, LPTSTR pszHelpFile, UINT cch)
{
UINT uHelpID = 0;
*pszHelpFile = 0;
LV_ITEM lvi = {0};
lvi.iItem = iItem;
lvi.mask = LVIF_PARAM;
if (ListView_GetItem(_hwndLVAll, &lvi))
{
IShellFolder2 *psf;
if (SUCCEEDED(_pdsv->GetFolder(IID_PPV_ARG(IShellFolder2, &psf))))
{
SHCOLUMNID scid;
if (SUCCEEDED(psf->MapColumnToSCID(lvi.lParam, &scid)))
{
IPropertyUI *ppui;
if (SUCCEEDED(_GetPropertyUI(&ppui)))
{
ppui->GetHelpInfo(scid.fmtid, scid.pid, pszHelpFile, cch, &uHelpID);
ppui->Release();
}
}
psf->Release();
}
}
return uHelpID; // IDH_ values
}
const static DWORD c_rgColumnDlgHelpIDs[] =
{
IDC_COL_UP, 1,
IDC_COL_DOWN, 1,
IDC_COL_SHOW, 1,
IDC_COL_HIDE, 1,
IDC_COL_WIDTH, 10055,
IDC_COL_WIDTH_TEXT, 10055,
0, 0
};
BOOL_PTR CColumnDlg::DlgProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_INITDIALOG:
_OnInitDlg();
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDC_COL_UP:
_MoveItem(- 1);
SetFocus(_hwndLVAll);
break;
case IDC_COL_DOWN:
_MoveItem(+ 1);
SetFocus(_hwndLVAll);
break;
case IDC_COL_SHOW:
case IDC_COL_HIDE:
{
UINT iItem = ListView_GetSelectionMark(_hwndLVAll);
ListView_SetCheckState(_hwndLVAll, iItem, LOWORD(wParam) == IDC_COL_SHOW);
SetFocus(_hwndLVAll);
break;
}
case IDC_COL_WIDTH:
if (HIWORD(wParam) == EN_CHANGE && !_bUpdating)
{
LV_ITEM lvi = {0};
lvi.iItem = ListView_GetSelectionMark(_hwndLVAll);
lvi.mask = LVIF_PARAM;
ListView_GetItem(_hwndLVAll, &lvi);
_pWidths[lvi.lParam] = - (int)GetDlgItemInt(_hdlg, IDC_COL_WIDTH, NULL, FALSE);
_bChanged = TRUE;
}
break;
case IDOK:
_SaveState();
// fall through
case IDCANCEL:
return EndDialog(_hdlg, TRUE);
}
break;
case WM_NOTIFY:
if (_bLoaded && !_bUpdating)
{
NMLISTVIEW * pnmlv = (NMLISTVIEW *)lParam;
switch (((NMHDR *)lParam)->code)
{
case LVN_ITEMCHANGING:
// fix up the buttons & such here
if (pnmlv->uChanged & LVIF_STATE)
_UpdateDlgButtons(pnmlv);
// We want to reject turning off the name column
// it both doesn't make sense to have no name column, and defview assumes there will be one
if (pnmlv->lParam == 0 &&
(pnmlv->uNewState & LVIS_STATEIMAGEMASK) == INDEXTOSTATEIMAGEMASK(1))
{
MessageBeep(MB_ICONEXCLAMATION);
SetWindowLongPtr(_hdlg, DWLP_MSGRESULT, TRUE);
return TRUE;
}
else
{
// if something besides focus changed
if ((pnmlv->uChanged & ~LVIF_STATE) ||
((pnmlv->uNewState & LVIS_STATEIMAGEMASK) != (pnmlv->uOldState & LVIS_STATEIMAGEMASK)))
_bChanged = TRUE;
}
break;
case NM_DBLCLK:
{
BOOL bCheck = ListView_GetCheckState(_hwndLVAll, pnmlv->iItem);
ListView_SetCheckState(_hwndLVAll, pnmlv->iItem, !bCheck);
}
break;
}
}
break;
case WM_SYSCOLORCHANGE:
SendMessage(_hwndLVAll, uMsg, wParam, lParam);
break;
case WM_HELP: // F1
{
HELPINFO *phi = (HELPINFO *)lParam;
//if the help is for one of the command buttons then call winhelp
if (phi->iCtrlId == IDC_COL_LVALL)
{
//Help is for the tree item so we need to do some special processing
int iItem;
// Is this help invoked throught F1 key
if (GetAsyncKeyState(VK_F1) < 0)
{
iItem = ListView_GetSelectionMark(_hwndLVAll);
}
else
{
LV_HITTESTINFO info;
info.pt = phi->MousePos;
ScreenToClient(_hwndLVAll, &info.pt);
iItem = ListView_HitTest(_hwndLVAll, &info);
}
if (iItem >= 0)
{
DWORD mapIDCToIDH[4] = {0};
TCHAR szFile[MAX_PATH];
mapIDCToIDH[0] = phi->iCtrlId;
mapIDCToIDH[1] = _HelpIDForItem(iItem, szFile, ARRAYSIZE(szFile));
WinHelp((HWND)((HELPINFO *)lParam)->hItemHandle, szFile[0] ? szFile : NULL,
HELP_WM_HELP, (DWORD_PTR)(LPSTR)mapIDCToIDH);
}
}
else
{
WinHelp((HWND)((HELPINFO *)lParam)->hItemHandle, TEXT(SHELL_HLP),
HELP_WM_HELP, (DWORD_PTR)(LPSTR)c_rgColumnDlgHelpIDs);
}
break;
}
case WM_CONTEXTMENU:
{
int iItem;
if ((LPARAM)-1 == lParam)
{
iItem = ListView_GetSelectionMark(_hwndLVAll);
}
else
{
LV_HITTESTINFO info;
info.pt.x = GET_X_LPARAM(lParam);
info.pt.y = GET_Y_LPARAM(lParam);
ScreenToClient(_hwndLVAll, &info.pt);
iItem = ListView_HitTest(_hwndLVAll, &info);
}
if (iItem >= 0)
{
DWORD mapIDCToIDH[4] = {0};
TCHAR szFile[MAX_PATH];
mapIDCToIDH[0] = IDC_COL_LVALL;
mapIDCToIDH[1] = _HelpIDForItem(iItem, szFile, ARRAYSIZE(szFile)); // IDH_ values
WinHelp((HWND)wParam, szFile[0] ? szFile : NULL, HELP_CONTEXTMENU, (DWORD_PTR)(LPSTR)mapIDCToIDH);
}
break;
}
default:
return FALSE;
}
return TRUE;
}