532 lines
13 KiB
C++
532 lines
13 KiB
C++
/*
|
|
* misc - misc stuff
|
|
*/
|
|
|
|
#include "tweakui.h"
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Eschew the C runtime
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void * __cdecl operator new(size_t nSize)
|
|
{
|
|
// Zero init just to save some headaches
|
|
return((LPVOID)LocalAlloc(LPTR, nSize));
|
|
}
|
|
|
|
|
|
void __cdecl operator delete(void *pv)
|
|
{
|
|
LocalFree((HLOCAL)pv);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Misc_NextPx
|
|
*
|
|
* Allocate another slot in the growable X array, returning its index.
|
|
* The slot is not eaten, however.
|
|
*
|
|
* The array may move as a result of this call; the caller promises
|
|
* not to use the resulting pointer after the next alloc call.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
PV PASCAL
|
|
Misc_AllocPx(PGXA pgxa)
|
|
{
|
|
if (pgxa->cx >= pgxa->cxAlloc) {
|
|
PV pv = lReAlloc(pgxa->pv, pgxa->cbx * (pgxa->cxAlloc + 5));
|
|
if (pv) {
|
|
pgxa->pv = pv;
|
|
pgxa->cxAlloc += 5;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
return (PBYTE)pgxa->pv + pgxa->cbx * pgxa->cx;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Misc_InitPgxa
|
|
*
|
|
*****************************************************************************/
|
|
|
|
BOOL PASCAL
|
|
Misc_InitPgxa(PGXA pgxa, int cbx)
|
|
{
|
|
pgxa->cbx = cbx;
|
|
pgxa->cxAlloc = 5;
|
|
pgxa->cx = 0;
|
|
pgxa->pv = lAlloc(pgxa->cbx * pgxa->cxAlloc);
|
|
return pgxa->pv ? TRUE : FALSE;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Misc_FreePgxa
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void PASCAL
|
|
Misc_FreePgxa(PGXA pgxa)
|
|
{
|
|
if (pgxa->pv) {
|
|
lFree(pgxa->pv);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Misc_EnableMenuFromHdlgId
|
|
*
|
|
* Enable or disable a menu item based on a dialog item.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void PASCAL
|
|
Misc_EnableMenuFromHdlgId(HMENU hmenu, HWND hdlg, UINT idc)
|
|
{
|
|
EnableMenuItem(hmenu, idc, MF_BYCOMMAND | (
|
|
IsWindowEnabled(GetDlgItem(hdlg, idc)) ? 0 : MFS_GRAYED));
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Misc_LV_GetCurSel
|
|
*
|
|
* Return the selection index.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
|
|
int PASCAL
|
|
Misc_LV_GetCurSel(HWND hwnd)
|
|
{
|
|
return ListView_GetNextItem(hwnd, -1, LVNI_FOCUSED);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Misc_LV_SetCurSel
|
|
*
|
|
* Set the selection index.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void PASCAL
|
|
Misc_LV_SetCurSel(HWND hwnd, int iIndex)
|
|
{
|
|
ListView_SetItemState(hwnd, iIndex, LVIS_FOCUSED | LVIS_SELECTED,
|
|
LVIS_FOCUSED | LVIS_SELECTED);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Misc_LV_EnsureSel
|
|
*
|
|
* Make sure *something* is selected. Try to make it iItem.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void PASCAL
|
|
Misc_LV_EnsureSel(HWND hwnd, int iItem)
|
|
{
|
|
int iItemMax = ListView_GetItemCount(hwnd) - 1;
|
|
if (iItem > iItemMax) {
|
|
iItem = iItemMax;
|
|
}
|
|
Misc_LV_SetCurSel(hwnd, iItem);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Misc_LV_GetItemInfo
|
|
*
|
|
* Grab some info out of an item.
|
|
*
|
|
* The incoming LV_ITEM should have any fields initialized which are
|
|
* required by the mask. (E.g., the state mask.)
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void PASCAL
|
|
Misc_LV_GetItemInfo(HWND hwnd, LV_ITEM FAR *plvi, int iItem, UINT mask)
|
|
{
|
|
plvi->iItem = iItem;
|
|
plvi->iSubItem = 0;
|
|
plvi->mask = mask;
|
|
ListView_GetItem(hwnd, plvi);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Misc_LV_GetParam
|
|
*
|
|
* Convert an iItem into the associated parameter.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
LPARAM PASCAL
|
|
Misc_LV_GetParam(HWND hwnd, int iItem)
|
|
{
|
|
LV_ITEM lvi;
|
|
Misc_LV_GetItemInfo(hwnd, &lvi, iItem, LVIF_PARAM);
|
|
return lvi.lParam;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Misc_LV_HitTest
|
|
*
|
|
* Perform a hit-test on the listview. The LPARAM parameter is
|
|
* the screen coordinates of the message.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void PASCAL
|
|
Misc_LV_HitTest(HWND hwnd, LV_HITTESTINFO FAR *phti, LPARAM lpPos)
|
|
{
|
|
phti->pt.x = (signed short)LOWORD(lpPos);
|
|
phti->pt.y = (signed short)HIWORD(lpPos);
|
|
|
|
MapWindowPoints(HWND_DESKTOP, hwnd, &phti->pt, 1);
|
|
|
|
ListView_HitTest(hwnd, phti);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Misc_Combo_GetCurItemData
|
|
*
|
|
* Get the current item data from a combo box.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
LRESULT PASCAL
|
|
Misc_Combo_GetCurItemData(HWND hwnd)
|
|
{
|
|
int iItem = ComboBox_GetCurSel(hwnd);
|
|
return ComboBox_GetItemData(hwnd, iItem);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Misc_Trim
|
|
*
|
|
* Trim leading and trailing spaces from a string. Returns a pointer
|
|
* to the first non-space character, and slaps a '\0' on top of the
|
|
* last trailing space.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
LPTSTR PASCAL
|
|
Misc_Trim(LPTSTR ptsz)
|
|
{
|
|
int ctch;
|
|
|
|
for ( ; (unsigned)(*ptsz - 1) <= ' '; ptsz++);
|
|
for (ctch = lstrlen(ptsz); ctch && (unsigned)ptsz[ctch-1] <= ' '; ctch--);
|
|
if (ctch) {
|
|
ptsz[ctch] = '\0';
|
|
}
|
|
return ptsz;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Misc_SetDrbSize
|
|
*
|
|
* Set the size of the buffer in a DRB.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
typedef struct DRB { /* dynamically resizable buffer */
|
|
PV pv; /* Actual buffer */
|
|
UINT cb; /* Size of buffer in bytes */
|
|
} DRB, *PDRB;
|
|
|
|
void PASCAL
|
|
Misc_SetDrbSize(PDRB pdrb, UINT cb)
|
|
{
|
|
if (pdrb->pv) {
|
|
LocalFree(pdrb->pv);
|
|
pdrb->pv = 0;
|
|
pdrb->cb = 0;
|
|
}
|
|
if (cb) {
|
|
pdrb->pv = LocalAlloc(LMEM_FIXED, cb);
|
|
if (pdrb->pv) {
|
|
pdrb->cb = cb;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Misc_EnsureDrb
|
|
*
|
|
* Make sure that the DRB can handle at least cb bytes.
|
|
*
|
|
* Round up to 4K boundary for convenience.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
BOOL PASCAL
|
|
Misc_EnsureDrb(PDRB pdrb, UINT cb)
|
|
{
|
|
if (cb <= pdrb->cb) {
|
|
} else {
|
|
Misc_SetDrbSize(pdrb, (cb + 1023) & ~1023);
|
|
}
|
|
return pdrb->pv ? TRUE : FALSE;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Misc_CopyRegWorker
|
|
*
|
|
* Copy a registry tree from one point to another.
|
|
*
|
|
* Since this is a recursive routine, don't allocate lots of goo off the
|
|
* stack.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
typedef struct RCI { /* registry copy info */
|
|
DRB drb; /* Data being copied */
|
|
TCH tszKey[ctchKeyMax]; /* Key name buffer */
|
|
} RCI, *PRCI;
|
|
|
|
BOOL PASCAL
|
|
Misc_CopyRegWorker(PRCI prci, HKEY hkSrcRoot, PCTSTR ptszSrc,
|
|
HKEY hkDstRoot, PCTSTR ptszDst)
|
|
{
|
|
HKEY hkSrc, hkDst;
|
|
BOOL fRc;
|
|
if (_RegOpenKey(hkSrcRoot, ptszSrc, &hkSrc) == 0) {
|
|
if (RegCreateKey(hkDstRoot, ptszDst, &hkDst) == 0) {
|
|
int i;
|
|
|
|
/*
|
|
* The first loop copies the values.
|
|
*/
|
|
for (i = 0; ; i++) {
|
|
DWORD ctch = cA(prci->tszKey), cb = 0, dwType;
|
|
switch (RegEnumValue(hkSrc, i, prci->tszKey, &ctch, 0,
|
|
&dwType, 0, &cb)) {
|
|
case ERROR_NO_MORE_ITEMS: goto valuesdone;
|
|
|
|
/*
|
|
* We can get an ERROR_SUCCESS if the value length is 0.
|
|
*/
|
|
case ERROR_SUCCESS:
|
|
case ERROR_MORE_DATA:
|
|
if (Misc_EnsureDrb(&prci->drb, cb)) {
|
|
DWORD dwType;
|
|
if (RegQueryValueEx(hkSrc, prci->tszKey, 0, &dwType,
|
|
(LPBYTE)prci->drb.pv, &cb) == 0 &&
|
|
RegSetValueEx(hkDst, prci->tszKey, 0, dwType,
|
|
(LPBYTE)prci->drb.pv, cb) == 0) {
|
|
} else {
|
|
fRc = 0; goto stopkey;
|
|
}
|
|
} else {
|
|
fRc = 0; goto stopkey;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
fRc = 0; goto stopkey;
|
|
}
|
|
}
|
|
valuesdone:;
|
|
|
|
/*
|
|
* The second loop recurses on each subkey.
|
|
*/
|
|
for (i = 0; ; i++) {
|
|
switch (RegEnumKey(hkSrc, i, prci->tszKey, cA(prci->tszKey))) {
|
|
case ERROR_NO_MORE_ITEMS: goto keysdone;
|
|
|
|
/*
|
|
* We can get an ERROR_SUCCESS if the subkey length is 0.
|
|
* (Thought that might be illegal for other reasons, but
|
|
* we'll go along with it anyway.)
|
|
*/
|
|
case ERROR_SUCCESS:
|
|
case ERROR_MORE_DATA:
|
|
if (Misc_CopyRegWorker(prci, hkSrc, prci->tszKey,
|
|
hkDst, prci->tszKey)) {
|
|
} else {
|
|
fRc = 0; goto stopkey;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
fRc = 0; goto stopkey;
|
|
}
|
|
}
|
|
keysdone:;
|
|
fRc = TRUE;
|
|
|
|
stopkey:;
|
|
RegCloseKey(hkDst);
|
|
} else {
|
|
fRc = 0;
|
|
}
|
|
RegCloseKey(hkSrc);
|
|
} else {
|
|
fRc = 0;
|
|
}
|
|
return fRc;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Misc_CopyReg
|
|
*
|
|
* Copy a registry tree from one point to another.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
BOOL PASCAL
|
|
Misc_CopyReg(HKEY hkSrcRoot, PCTSTR ptszSrc, HKEY hkDstRoot, PCTSTR ptszDst)
|
|
{
|
|
BOOL fRc;
|
|
RCI rci;
|
|
rci.drb.pv = 0;
|
|
rci.drb.cb = 0;
|
|
|
|
fRc = Misc_CopyRegWorker(&rci, hkSrcRoot, ptszSrc, hkDstRoot, ptszDst);
|
|
|
|
Misc_SetDrbSize(&rci.drb, 0);
|
|
|
|
if (fRc) {
|
|
} else {
|
|
/* Clean up partial copy */
|
|
RegDeleteTree(hkDstRoot, ptszDst);
|
|
}
|
|
|
|
return fRc;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Misc_RenameReg
|
|
*
|
|
* Rename a registry key by copying it and deleting the original.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
BOOL PASCAL
|
|
Misc_RenameReg(HKEY hkRoot, PCTSTR ptszKey, PCTSTR ptszSrc, PCTSTR ptszDst)
|
|
{
|
|
BOOL fRc;
|
|
HKEY hk;
|
|
|
|
if (_RegOpenKey(hkRoot, ptszKey, &hk) == 0) {
|
|
if (Misc_CopyReg(hk, ptszSrc, hk, ptszDst)) {
|
|
RegDeleteTree(hk, ptszSrc);
|
|
fRc = TRUE;
|
|
} else {
|
|
fRc = FALSE;
|
|
}
|
|
RegCloseKey(hk);
|
|
} else {
|
|
fRc = FALSE;
|
|
}
|
|
|
|
return fRc;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* lstrcatnBs
|
|
*
|
|
* Like lstrcatn, but with a backslash stuck in between.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void PASCAL
|
|
lstrcatnBs(PTSTR ptszDst, PCTSTR ptszSrc, int ctch)
|
|
{
|
|
int ctchDst = lstrlen(ptszDst);
|
|
ptszDst += ctchDst;
|
|
ctch -= ctchDst;
|
|
if (ctch > 1) {
|
|
ptszDst[0] = TEXT('\\');
|
|
lstrcpyn(ptszDst + 1, ptszSrc, ctch - 1);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Misc_GetShellIconSize
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#pragma BEGIN_CONST_DATA
|
|
|
|
KL const c_klShellIconSize = { &c_hkCU, c_tszMetrics, c_tszShellIconSize };
|
|
|
|
#pragma END_CONST_DATA
|
|
|
|
|
|
UINT PASCAL
|
|
Misc_GetShellIconSize(void)
|
|
{
|
|
return GetIntPkl(GetSystemMetrics(SM_CXICON), &c_klShellIconSize);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Misc_SetShellIconSize
|
|
*
|
|
* Dork the shell icon size and rebuild.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void PASCAL
|
|
Misc_SetShellIconSize(UINT ui)
|
|
{
|
|
SetIntPkl(ui, &c_klShellIconSize);
|
|
SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, SPI_SETNONCLIENTMETRICS, 0L);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Misc_RebuildIcoCache
|
|
*
|
|
* Force the shell to rebuild its icon cache.
|
|
*
|
|
* Due to the way the shell works, we have to do this by changing
|
|
* the icon sizes, then changing it back.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void PASCAL
|
|
Misc_RebuildIcoCache(void)
|
|
{
|
|
UINT cxIcon = Misc_GetShellIconSize();
|
|
Misc_SetShellIconSize(cxIcon-1);
|
|
Misc_SetShellIconSize(cxIcon);
|
|
Explorer_HackPtui();
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* SetWaitCursor
|
|
*
|
|
*****************************************************************************/
|
|
|
|
HCURSOR SetWaitCursor(void)
|
|
{
|
|
return SetCursor(LoadCursor(0, IDC_WAIT));
|
|
}
|