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