1658 lines
44 KiB
C++
1658 lines
44 KiB
C++
//#include "pch.h"
|
|
#pragma hdrstop
|
|
|
|
#include "sautil.h"
|
|
|
|
BOOL g_fNoWinHelp = FALSE;
|
|
|
|
VOID ContextHelp(
|
|
IN const DWORD* padwMap,
|
|
IN HWND hwndDlg,
|
|
IN UINT unMsg,
|
|
IN WPARAM wparam,
|
|
IN LPARAM lparam)
|
|
// Calls WinHelp to popup context sensitive help. 'PadwMap' is an array
|
|
// of control-ID help-ID pairs terminated with a 0,0 pair. 'UnMsg' is
|
|
// WM_HELP or WM_CONTEXTMENU indicating the message received requesting
|
|
// help. 'Wparam' and 'lparam' are the parameters of the message received
|
|
// requesting help.
|
|
//
|
|
{
|
|
HWND hwnd;
|
|
UINT unType;
|
|
TCHAR* pszHelpFile;
|
|
|
|
ASSERT( unMsg==WM_HELP || unMsg==WM_CONTEXTMENU );
|
|
|
|
// Don't try to do help if it won't work. See common\uiutil\ui.c.
|
|
//
|
|
{
|
|
extern BOOL g_fNoWinHelp;
|
|
if (g_fNoWinHelp)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (unMsg == WM_HELP)
|
|
{
|
|
LPHELPINFO p = (LPHELPINFO )lparam;;
|
|
|
|
TRACE3( "ContextHelp(WM_HELP,t=%d,id=%d,h=$%08x)",
|
|
p->iContextType, p->iCtrlId,p->hItemHandle );
|
|
|
|
if (p->iContextType != HELPINFO_WINDOW)
|
|
{
|
|
return;
|
|
}
|
|
|
|
hwnd = (HWND)p->hItemHandle;
|
|
ASSERT( hwnd );
|
|
unType = HELP_WM_HELP;
|
|
}
|
|
else
|
|
{
|
|
// Standard Win95 method that produces a one-item "What's This?" menu
|
|
// that user must click to get help.
|
|
//
|
|
TRACE1( "ContextHelp(WM_CONTEXTMENU,h=$%08x)", wparam );
|
|
|
|
hwnd = (HWND )wparam;
|
|
unType = HELP_CONTEXTMENU;
|
|
};
|
|
|
|
// if (fRouter)
|
|
// {
|
|
// pszHelpFile = g_pszRouterHelpFile;
|
|
// }
|
|
// else
|
|
// {
|
|
// pszHelpFile = g_pszHelpFile;
|
|
// }
|
|
pszHelpFile = PszFromId (g_hinstDll, SID_HelpFile );
|
|
|
|
TRACE1( "WinHelp(%s)", pszHelpFile );
|
|
WinHelp( hwnd, pszHelpFile, unType, (ULONG_PTR ) padwMap );
|
|
|
|
Free0 (pszHelpFile);
|
|
}
|
|
|
|
VOID
|
|
AddContextHelpButton(
|
|
IN HWND hwnd )
|
|
|
|
/* Turns on title bar context help button in 'hwnd'.
|
|
**
|
|
** Dlgedit.exe doesn't currently support adding this style at dialog
|
|
** resource edit time. When that's fixed set DS_CONTEXTHELP in the dialog
|
|
** definition and remove this routine.
|
|
*/
|
|
{
|
|
LONG lStyle;
|
|
|
|
if (g_fNoWinHelp)
|
|
return;
|
|
|
|
lStyle = GetWindowLong( hwnd, GWL_EXSTYLE );
|
|
|
|
if (lStyle)
|
|
SetWindowLong( hwnd, GWL_EXSTYLE, lStyle | WS_EX_CONTEXTHELP );
|
|
}
|
|
|
|
/* Extended arguments for the MsgDlgUtil routine. Designed so zeroed gives
|
|
** default behaviors.
|
|
*/
|
|
|
|
/*----------------------------------------------------------------------------
|
|
** Message popup
|
|
**----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
MsgDlgUtil(
|
|
IN HWND hwndOwner,
|
|
IN DWORD dwMsg,
|
|
IN OUT MSGARGS* pargs,
|
|
IN HINSTANCE hInstance,
|
|
IN DWORD dwTitle )
|
|
|
|
/* Pops up a message dialog centered on 'hwndOwner'. 'DwMsg' is the
|
|
** string resource ID of the message text. 'Pargs' is a extended
|
|
** formatting arguments or NULL if none. 'hInstance' is the
|
|
** application/module handle where string resources are located.
|
|
** 'DwTitle' is the string ID of the dialog title.
|
|
**
|
|
** Returns MessageBox-style code.
|
|
*/
|
|
{
|
|
TCHAR* pszUnformatted;
|
|
TCHAR* pszResult;
|
|
TCHAR* pszNotFound;
|
|
int nResult;
|
|
|
|
TRACE("MsgDlgUtil");
|
|
|
|
/* A placeholder for missing strings components.
|
|
*/
|
|
pszNotFound = TEXT("");
|
|
|
|
/* Build the message string.
|
|
*/
|
|
pszResult = pszNotFound;
|
|
|
|
if (pargs && pargs->pszString)
|
|
{
|
|
FormatMessage(
|
|
FORMAT_MESSAGE_FROM_STRING +
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER +
|
|
FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
pargs->pszString, 0, 0, (LPTSTR )&pszResult, 1,
|
|
(va_list* )pargs->apszArgs );
|
|
}
|
|
else
|
|
{
|
|
pszUnformatted = PszFromId( hInstance, dwMsg );
|
|
|
|
if (pszUnformatted)
|
|
{
|
|
FormatMessage(
|
|
FORMAT_MESSAGE_FROM_STRING +
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER +
|
|
FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
pszUnformatted, 0, 0, (LPTSTR )&pszResult, 1,
|
|
(va_list* )((pargs) ? pargs->apszArgs : NULL) );
|
|
|
|
Free( pszUnformatted );
|
|
}
|
|
}
|
|
|
|
if (!pargs || !pargs->fStringOutput)
|
|
{
|
|
TCHAR* pszTitle;
|
|
DWORD dwFlags;
|
|
HHOOK hhook;
|
|
|
|
if (pargs && pargs->dwFlags != 0)
|
|
dwFlags = pargs->dwFlags;
|
|
else
|
|
dwFlags = MB_ICONINFORMATION + MB_OK + MB_SETFOREGROUND;
|
|
|
|
pszTitle = PszFromId( hInstance, dwTitle );
|
|
|
|
if (hwndOwner)
|
|
{
|
|
/* Install hook that will get the message box centered on the
|
|
** owner window.
|
|
*/
|
|
hhook = SetWindowsHookEx( WH_CALLWNDPROC,
|
|
CenterDlgOnOwnerCallWndProc,
|
|
hInstance, GetCurrentThreadId() );
|
|
}
|
|
else
|
|
hhook = NULL;
|
|
|
|
if (pszResult)
|
|
{
|
|
nResult = MessageBox( hwndOwner, pszResult, pszTitle, dwFlags );
|
|
}
|
|
|
|
if (hhook)
|
|
UnhookWindowsHookEx( hhook );
|
|
|
|
Free0( pszTitle );
|
|
if (pszResult != pszNotFound)
|
|
LocalFree( pszResult );
|
|
}
|
|
else
|
|
{
|
|
/* Caller wants the string without doing the popup.
|
|
*/
|
|
pargs->pszOutput = (pszResult != pszNotFound) ? pszResult : NULL;
|
|
nResult = IDOK;
|
|
}
|
|
|
|
return nResult;
|
|
}
|
|
|
|
|
|
VOID
|
|
UnclipWindow(
|
|
IN HWND hwnd )
|
|
|
|
/* Moves window 'hwnd' so any clipped parts are again visible on the
|
|
** screen. The window is moved only as far as necessary to achieve this.
|
|
*/
|
|
{
|
|
RECT rect;
|
|
INT dxScreen = GetSystemMetrics( SM_CXSCREEN );
|
|
INT dyScreen = GetSystemMetrics( SM_CYSCREEN );
|
|
|
|
GetWindowRect( hwnd, &rect );
|
|
|
|
if (rect.right > dxScreen)
|
|
rect.left = dxScreen - (rect.right - rect.left);
|
|
|
|
if (rect.left < 0)
|
|
rect.left = 0;
|
|
|
|
if (rect.bottom > dyScreen)
|
|
rect.top = dyScreen - (rect.bottom - rect.top);
|
|
|
|
if (rect.top < 0)
|
|
rect.top = 0;
|
|
|
|
SetWindowPos(
|
|
hwnd, NULL,
|
|
rect.left, rect.top, 0, 0,
|
|
SWP_NOZORDER + SWP_NOSIZE );
|
|
}
|
|
VOID
|
|
CenterWindow(
|
|
IN HWND hwnd,
|
|
IN HWND hwndRef )
|
|
|
|
/* Center window 'hwnd' on window 'hwndRef' or if 'hwndRef' is NULL on
|
|
** screen. The window position is adjusted so that no parts are clipped
|
|
** by the edge of the screen, if necessary. If 'hwndRef' has been moved
|
|
** off-screen with SetOffDesktop, the original position is used.
|
|
*/
|
|
{
|
|
RECT rectCur;
|
|
LONG dxCur;
|
|
LONG dyCur;
|
|
RECT rectRef;
|
|
LONG dxRef;
|
|
LONG dyRef;
|
|
|
|
GetWindowRect( hwnd, &rectCur );
|
|
dxCur = rectCur.right - rectCur.left;
|
|
dyCur = rectCur.bottom - rectCur.top;
|
|
|
|
if (hwndRef)
|
|
{
|
|
// if (!SetOffDesktop( hwndRef, SOD_GetOrgRect, &rectRef ))
|
|
GetWindowRect( hwndRef, &rectRef );
|
|
}
|
|
else
|
|
{
|
|
rectRef.top = rectRef.left = 0;
|
|
rectRef.right = GetSystemMetrics( SM_CXSCREEN );
|
|
rectRef.bottom = GetSystemMetrics( SM_CYSCREEN );
|
|
}
|
|
|
|
dxRef = rectRef.right - rectRef.left;
|
|
dyRef = rectRef.bottom - rectRef.top;
|
|
|
|
rectCur.left = rectRef.left + ((dxRef - dxCur) / 2);
|
|
rectCur.top = rectRef.top + ((dyRef - dyCur) / 2);
|
|
|
|
SetWindowPos(
|
|
hwnd, NULL,
|
|
rectCur.left, rectCur.top, 0, 0,
|
|
SWP_NOZORDER + SWP_NOSIZE );
|
|
|
|
UnclipWindow( hwnd );
|
|
}
|
|
|
|
LRESULT CALLBACK
|
|
CenterDlgOnOwnerCallWndProc(
|
|
int code,
|
|
WPARAM wparam,
|
|
LPARAM lparam )
|
|
|
|
/* Standard Win32 CallWndProc hook callback that looks for the next dialog
|
|
** started and centers it on it's owner window.
|
|
*/
|
|
{
|
|
/* Arrive here when any window procedure associated with our thread is
|
|
** called.
|
|
*/
|
|
if (!wparam)
|
|
{
|
|
CWPSTRUCT* p = (CWPSTRUCT* )lparam;
|
|
|
|
/* The message is from outside our process. Look for the MessageBox
|
|
** dialog initialization message and take that opportunity to center
|
|
** the dialog on it's owner's window.
|
|
*/
|
|
if (p->message == WM_INITDIALOG)
|
|
CenterWindow( p->hwnd, GetParent( p->hwnd ) );
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
TCHAR*
|
|
PszFromId(
|
|
IN HINSTANCE hInstance,
|
|
IN DWORD dwStringId )
|
|
|
|
/* String resource message loader routine.
|
|
**
|
|
** Returns the address of a heap block containing the string corresponding
|
|
** to string resource 'dwStringId' or NULL if error. It is caller's
|
|
** responsibility to Free the returned string.
|
|
*/
|
|
{
|
|
HRSRC hrsrc;
|
|
TCHAR* pszBuf;
|
|
int cchBuf = 256;
|
|
int cchGot;
|
|
|
|
for (;;)
|
|
{
|
|
pszBuf = (TCHAR*)Malloc( cchBuf * sizeof(TCHAR) );
|
|
if (!pszBuf)
|
|
break;
|
|
|
|
/* LoadString wants to deal with character-counts rather than
|
|
** byte-counts...weird. Oh, and if you're thinking I could
|
|
** FindResource then SizeofResource to figure out the string size, be
|
|
** advised it doesn't work. From perusing the LoadString source, it
|
|
** appears the RT_STRING resource type requests a segment of 16
|
|
** strings not an individual string.
|
|
*/
|
|
cchGot = LoadString( hInstance, (UINT )dwStringId, pszBuf, cchBuf );
|
|
|
|
if (cchGot < cchBuf - 1)
|
|
{
|
|
TCHAR *pszTemp = pszBuf;
|
|
|
|
/* Good, got the whole string. Reduce heap block to actual size
|
|
** needed.
|
|
*/
|
|
pszBuf = (TCHAR*)Realloc( pszBuf, (cchGot + 1) * sizeof(TCHAR));
|
|
|
|
if(NULL == pszBuf)
|
|
{
|
|
Free(pszTemp);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
/* Uh oh, LoadStringW filled the buffer entirely which could mean the
|
|
** string was truncated. Try again with a larger buffer to be sure it
|
|
** wasn't.
|
|
*/
|
|
Free( pszBuf );
|
|
cchBuf += 256;
|
|
TRACE1("Grow string buf to %d",cchBuf);
|
|
}
|
|
|
|
return pszBuf;
|
|
}
|
|
|
|
TCHAR*
|
|
GetText(
|
|
IN HWND hwnd )
|
|
|
|
/* Returns heap block containing the text contents of the window 'hwnd' or
|
|
** NULL. It is caller's responsibility to Free the returned string.
|
|
*/
|
|
{
|
|
INT cch;
|
|
TCHAR* psz;
|
|
|
|
cch = GetWindowTextLength( hwnd );
|
|
psz = (TCHAR*)Malloc( (cch + 1) * sizeof(TCHAR) );
|
|
|
|
if (psz)
|
|
{
|
|
*psz = TEXT('\0');
|
|
GetWindowText( hwnd, psz, cch + 1 );
|
|
}
|
|
|
|
return psz;
|
|
}
|
|
|
|
BOOL
|
|
GetErrorText(
|
|
DWORD dwError,
|
|
TCHAR** ppszError )
|
|
|
|
/* Fill caller's '*ppszError' with the address of a LocalAlloc'ed heap
|
|
** block containing the error text associated with error 'dwError'. It is
|
|
** caller's responsibility to LocalFree the returned string.
|
|
**
|
|
** Returns true if successful, false otherwise.
|
|
*/
|
|
{
|
|
#define MAXRASERRORLEN 256
|
|
|
|
TCHAR szBuf[ MAXRASERRORLEN + 1 ];
|
|
DWORD dwFlags;
|
|
HANDLE hmodule;
|
|
DWORD cch;
|
|
|
|
/* Don't panic if the RAS API address is not loaded. Caller may be trying
|
|
** and get an error up during LoadRas.
|
|
*/
|
|
// if ((Rasapi32DllLoaded() || RasRpcDllLoaded())
|
|
// && g_pRasGetErrorString
|
|
// && g_pRasGetErrorString(
|
|
if (RasGetErrorString ((UINT)dwError, (LPTSTR)szBuf, MAXRASERRORLEN) == 0)
|
|
{
|
|
/* It's a RAS error.
|
|
*/
|
|
*ppszError = (TCHAR*)LocalAlloc( LPTR, (lstrlen( szBuf ) + 1) * sizeof(TCHAR) );
|
|
if (!*ppszError)
|
|
return FALSE;
|
|
|
|
lstrcpy( *ppszError, szBuf );
|
|
return TRUE;
|
|
}
|
|
|
|
/* The rest adapted from BLT's LoadSystem routine.
|
|
*/
|
|
dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER + FORMAT_MESSAGE_IGNORE_INSERTS;
|
|
|
|
if (dwError >= MIN_LANMAN_MESSAGE_ID && dwError <= MAX_LANMAN_MESSAGE_ID)
|
|
{
|
|
/* It's a net error.
|
|
*/
|
|
dwFlags += FORMAT_MESSAGE_FROM_HMODULE;
|
|
hmodule = GetModuleHandle( TEXT("NETMSG.DLL") );
|
|
}
|
|
else
|
|
{
|
|
/* It must be a system error.
|
|
*/
|
|
dwFlags += FORMAT_MESSAGE_FROM_SYSTEM;
|
|
hmodule = NULL;
|
|
}
|
|
|
|
cch = FormatMessage(
|
|
dwFlags, hmodule, dwError, 0, (LPTSTR )ppszError, 1, NULL );
|
|
return (cch > 0);
|
|
}
|
|
|
|
int
|
|
ErrorDlgUtil(
|
|
IN HWND hwndOwner,
|
|
IN DWORD dwOperation,
|
|
IN DWORD dwError,
|
|
IN OUT ERRORARGS* pargs,
|
|
IN HINSTANCE hInstance,
|
|
IN DWORD dwTitle,
|
|
IN DWORD dwFormat )
|
|
|
|
/* Pops up a modal error dialog centered on 'hwndOwner'. 'DwOperation' is
|
|
** the string resource ID of the string describing the operation underway
|
|
** when the error occurred. 'DwError' is the code of the system or RAS
|
|
** error that occurred. 'Pargs' is a extended formatting arguments or
|
|
** NULL if none. 'hInstance' is the application/module handle where
|
|
** string resources are located. 'DwTitle' is the string ID of the dialog
|
|
** title. 'DwFormat' is the string ID of the error format title.
|
|
**
|
|
** Returns MessageBox-style code.
|
|
*/
|
|
{
|
|
TCHAR* pszUnformatted;
|
|
TCHAR* pszOp;
|
|
TCHAR szErrorNum[ 50 ];
|
|
TCHAR* pszError;
|
|
TCHAR* pszResult;
|
|
TCHAR* pszNotFound;
|
|
int nResult;
|
|
|
|
TRACE("ErrorDlgUtil");
|
|
|
|
/* A placeholder for missing strings components.
|
|
*/
|
|
pszNotFound = TEXT("");
|
|
|
|
/* Build the error number string.
|
|
*/
|
|
if (dwError > 0x7FFFFFFF)
|
|
wsprintf( szErrorNum, TEXT("0x%X"), dwError );
|
|
else
|
|
wsprintf( szErrorNum, TEXT("%u"), dwError );
|
|
|
|
/* Build the error text string.
|
|
*/
|
|
if (!GetErrorText( dwError, &pszError ))
|
|
pszError = pszNotFound;
|
|
|
|
/* Build the operation string.
|
|
*/
|
|
pszUnformatted = PszFromId( hInstance, dwOperation );
|
|
pszOp = pszNotFound;
|
|
|
|
if (pszUnformatted)
|
|
{
|
|
FormatMessage(
|
|
FORMAT_MESSAGE_FROM_STRING +
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER +
|
|
FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
pszUnformatted, 0, 0, (LPTSTR )&pszOp, 1,
|
|
(va_list* )((pargs) ? pargs->apszOpArgs : NULL) );
|
|
|
|
Free( pszUnformatted );
|
|
}
|
|
|
|
/* Call MsgDlgUtil with the standard arguments plus any auxillary format
|
|
** arguments.
|
|
*/
|
|
pszUnformatted = PszFromId( hInstance, dwFormat );
|
|
pszResult = pszNotFound;
|
|
|
|
if (pszUnformatted)
|
|
{
|
|
MSGARGS msgargs;
|
|
|
|
ZeroMemory( &msgargs, sizeof(msgargs) );
|
|
msgargs.dwFlags = MB_ICONEXCLAMATION + MB_OK + MB_SETFOREGROUND;
|
|
msgargs.pszString = pszUnformatted;
|
|
msgargs.apszArgs[ 0 ] = pszOp;
|
|
msgargs.apszArgs[ 1 ] = szErrorNum;
|
|
msgargs.apszArgs[ 2 ] = pszError;
|
|
|
|
if (pargs)
|
|
{
|
|
msgargs.fStringOutput = pargs->fStringOutput;
|
|
|
|
CopyMemory( &msgargs.apszArgs[ 3 ], pargs->apszAuxFmtArgs,
|
|
3 * sizeof(TCHAR) );
|
|
}
|
|
|
|
nResult =
|
|
MsgDlgUtil(
|
|
hwndOwner, 0, &msgargs, hInstance, dwTitle );
|
|
|
|
Free( pszUnformatted );
|
|
|
|
if (pargs && pargs->fStringOutput)
|
|
pargs->pszOutput = msgargs.pszOutput;
|
|
}
|
|
|
|
if (pszOp != pszNotFound)
|
|
LocalFree( pszOp );
|
|
if (pszError != pszNotFound)
|
|
LocalFree( pszError );
|
|
|
|
return nResult;
|
|
}
|
|
int MsgDlgUtil(IN HWND hwndOwner, IN DWORD dwMsg, IN OUT MSGARGS* pargs, IN HINSTANCE hInstance, IN DWORD dwTitle);
|
|
#define MsgDlg(h,m,a) \
|
|
MsgDlgUtil(h,m,a,g_hinstDll,SID_PopupTitle)
|
|
|
|
#define ErrorDlg(h,o,e,a) \
|
|
ErrorDlgUtil(h,o,e,a,g_hinstDll,SID_PopupTitle,SID_FMT_ErrorMsg)
|
|
|
|
|
|
|
|
// LVX stuff (cut-n-paste'd from ...\net\rras\ras\ui\common\uiutil\lvx.c, etc.
|
|
|
|
|
|
static LPCTSTR g_lvxcbContextId = NULL;
|
|
|
|
BOOL
|
|
ListView_IsCheckDisabled (
|
|
IN HWND hwndLv,
|
|
IN INT iItem)
|
|
|
|
/* Returns true if the check box of item 'iItem' of listview of checkboxes
|
|
** 'hwndLv' is disabled, false otherwise.
|
|
*/
|
|
{
|
|
UINT unState;
|
|
unState = ListView_GetItemState( hwndLv, iItem, LVIS_STATEIMAGEMASK );
|
|
|
|
if ((unState == INDEXTOSTATEIMAGEMASK( SI_DisabledChecked )) ||
|
|
(unState == INDEXTOSTATEIMAGEMASK( SI_DisabledUnchecked )))
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
VOID
|
|
ListView_SetCheck(
|
|
IN HWND hwndLv,
|
|
IN INT iItem,
|
|
IN BOOL fCheck )
|
|
|
|
/* Sets the check mark on item 'iItem' of listview of checkboxes 'hwndLv'
|
|
** to checked if 'fCheck' is true or unchecked if false.
|
|
*/
|
|
{
|
|
NM_LISTVIEW nmlv;
|
|
|
|
if (ListView_IsCheckDisabled(hwndLv, iItem))
|
|
return;
|
|
|
|
ListView_SetItemState( hwndLv, iItem,
|
|
INDEXTOSTATEIMAGEMASK( (fCheck) ? SI_Checked : SI_Unchecked ),
|
|
LVIS_STATEIMAGEMASK );
|
|
|
|
nmlv.hdr.code = LVXN_SETCHECK;
|
|
nmlv.hdr.hwndFrom = hwndLv;
|
|
nmlv.iItem = iItem;
|
|
|
|
FORWARD_WM_NOTIFY(
|
|
GetParent(hwndLv), GetDlgCtrlID(hwndLv), &nmlv, SendMessage
|
|
);
|
|
}
|
|
|
|
VOID*
|
|
ListView_GetParamPtr(
|
|
IN HWND hwndLv,
|
|
IN INT iItem )
|
|
|
|
/* Returns the lParam address of the 'iItem' item in 'hwndLv' or NULL if
|
|
** none or error.
|
|
*/
|
|
{
|
|
LV_ITEM item;
|
|
|
|
ZeroMemory( &item, sizeof(item) );
|
|
item.mask = LVIF_PARAM;
|
|
item.iItem = iItem;
|
|
|
|
if (!ListView_GetItem( hwndLv, &item ))
|
|
return NULL;
|
|
|
|
return (VOID* )item.lParam;
|
|
}
|
|
|
|
BOOL
|
|
ListView_GetCheck(
|
|
IN HWND hwndLv,
|
|
IN INT iItem )
|
|
|
|
/* Returns true if the check box of item 'iItem' of listview of checkboxes
|
|
** 'hwndLv' is checked, false otherwise. This function works on disabled
|
|
** check boxes as well as enabled ones.
|
|
*/
|
|
{
|
|
UINT unState;
|
|
|
|
unState = ListView_GetItemState( hwndLv, iItem, LVIS_STATEIMAGEMASK );
|
|
return !!((unState == INDEXTOSTATEIMAGEMASK( SI_Checked )) ||
|
|
(unState == INDEXTOSTATEIMAGEMASK( SI_DisabledChecked )));
|
|
}
|
|
|
|
LRESULT APIENTRY
|
|
LvxcbProc(
|
|
IN HWND hwnd,
|
|
IN UINT unMsg,
|
|
IN WPARAM wparam,
|
|
IN LPARAM lparam )
|
|
|
|
/* List view subclass window procedure to trap toggle-check events.
|
|
*/
|
|
{
|
|
WNDPROC pOldProc;
|
|
INT iItem;
|
|
BOOL fSet;
|
|
BOOL fClear;
|
|
BOOL fToggle;
|
|
|
|
iItem = -1;
|
|
fSet = fClear = fToggle = FALSE;
|
|
|
|
if (unMsg == WM_LBUTTONDOWN)
|
|
{
|
|
LV_HITTESTINFO info;
|
|
|
|
/* Left mouse button pressed over checkbox icon toggles state.
|
|
** Normally, we'd use LVHT_ONITEMSTATEICON and be done with it, but we
|
|
** want to work with our cool owner-drawn list view extensions in
|
|
** which case the control doesn't know where the icon is on the item,
|
|
** so it returns a hit anywhere on the item anyway.
|
|
*/
|
|
ZeroMemory( &info, sizeof(info) );
|
|
info.pt.x = LOWORD( lparam );
|
|
info.pt.y = HIWORD( lparam );
|
|
info.flags = LVHT_ONITEM;
|
|
iItem = ListView_HitTest( hwnd, &info );
|
|
|
|
if (iItem >= 0)
|
|
{
|
|
/* OK, it's over item 'iItem'. Now figure out if it's over the
|
|
** checkbox. Note this currently doesn't account for use of the
|
|
** "indent" feature on an owner-drawn item.
|
|
*/
|
|
if ((INT )(LOWORD( lparam )) >= GetSystemMetrics( SM_CXSMICON ))
|
|
iItem = -1;
|
|
else
|
|
fToggle = TRUE;
|
|
}
|
|
}
|
|
else if (unMsg == WM_LBUTTONDBLCLK)
|
|
{
|
|
LV_HITTESTINFO info;
|
|
|
|
/* Left mouse button double clicked over any area toggles state.
|
|
** Normally, we'd use LVHT_ONITEMSTATEICON and be done with it, but we
|
|
** want to work with our cool owner-drawn list view extensions in
|
|
** which case the control doesn't know where the icon is on the item,
|
|
** so it returns a hit anywhere on the item anyway.
|
|
*/
|
|
ZeroMemory( &info, sizeof(info) );
|
|
info.pt.x = LOWORD( lparam );
|
|
info.pt.y = HIWORD( lparam );
|
|
info.flags = LVHT_ONITEM;
|
|
iItem = ListView_HitTest( hwnd, &info );
|
|
|
|
if (iItem >= 0)
|
|
{
|
|
/* OK, it's over item 'iItem'. If the click does not occur
|
|
* over a checkbox, inform the parent of the double click.
|
|
*/
|
|
if ((INT )(LOWORD( lparam )) >= GetSystemMetrics( SM_CXSMICON )) {
|
|
NM_LISTVIEW nmlv;
|
|
nmlv.hdr.code = LVXN_DBLCLK;
|
|
nmlv.hdr.hwndFrom = hwnd;
|
|
nmlv.iItem = iItem;
|
|
|
|
FORWARD_WM_NOTIFY(
|
|
GetParent(hwnd), GetDlgCtrlID(hwnd), &nmlv, SendMessage);
|
|
|
|
iItem = -1;
|
|
}
|
|
|
|
/*
|
|
* Otherwise, toggle the state.
|
|
*/
|
|
else
|
|
fToggle = TRUE;
|
|
}
|
|
}
|
|
else if (unMsg == WM_CHAR)
|
|
{
|
|
/* Space bar pressed with item selected toggles check.
|
|
** Plus or Equals keys set check.
|
|
** Minus key clears check.
|
|
*/
|
|
switch (wparam)
|
|
{
|
|
case TEXT(' '):
|
|
fToggle = TRUE;
|
|
break;
|
|
|
|
case TEXT('+'):
|
|
case TEXT('='):
|
|
fSet = TRUE;
|
|
break;
|
|
|
|
case TEXT('-'):
|
|
fClear = TRUE;
|
|
break;
|
|
}
|
|
|
|
if (fToggle || fSet || fClear)
|
|
iItem = ListView_GetNextItem( hwnd, -1, LVNI_SELECTED );
|
|
}
|
|
else if (unMsg == WM_KEYDOWN)
|
|
{
|
|
/* Left arrow becomes up arrow and right arrow becomes down arrow so
|
|
** the list of checkboxes behaves just like a static group of
|
|
** checkboxes.
|
|
*/
|
|
if (wparam == VK_LEFT)
|
|
wparam = VK_UP;
|
|
else if (wparam == VK_RIGHT)
|
|
wparam = VK_DOWN;
|
|
}
|
|
|
|
if (iItem >= 0)
|
|
{
|
|
|
|
/* If we are handling the spacebar, plus, minus, or equals,
|
|
** the change we make applies to all the selected items;
|
|
** hence the do {} while(WM_CHAR).
|
|
*/
|
|
|
|
do {
|
|
|
|
if (fToggle)
|
|
{
|
|
UINT unOldState;
|
|
BOOL fCheck;
|
|
|
|
fCheck = ListView_GetCheck( hwnd, iItem );
|
|
ListView_SetCheck( hwnd, iItem, !fCheck );
|
|
}
|
|
else if (fSet)
|
|
{
|
|
if (!ListView_GetCheck( hwnd, iItem ))
|
|
ListView_SetCheck( hwnd, iItem, TRUE );
|
|
}
|
|
else if (fClear)
|
|
{
|
|
if (ListView_GetCheck( hwnd, iItem ))
|
|
ListView_SetCheck( hwnd, iItem, FALSE );
|
|
}
|
|
|
|
iItem = ListView_GetNextItem(hwnd, iItem, LVNI_SELECTED);
|
|
|
|
} while(iItem >= 0 && unMsg == WM_CHAR);
|
|
|
|
if (fSet || fClear) {
|
|
|
|
/* Don't pass to listview to avoid beep.
|
|
*/
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
pOldProc = (WNDPROC )GetProp( hwnd, g_lvxcbContextId );
|
|
if (pOldProc)
|
|
return CallWindowProc( pOldProc, hwnd, unMsg, wparam, lparam );
|
|
|
|
return 0;
|
|
}
|
|
|
|
BOOL
|
|
ListView_InstallChecks(
|
|
IN HWND hwndLv,
|
|
IN HINSTANCE hinst )
|
|
|
|
/* Initialize "list of checkbox" handling for listview 'hwndLv'. 'Hinst'
|
|
** is the module instance containing the two checkbox icons. See LVX.RC.
|
|
**
|
|
** Returns true if successful, false otherwise. Caller must eventually
|
|
** call 'ListView_UninstallChecks', typically in WM_DESTROY processing.
|
|
*/
|
|
{
|
|
HICON hIcon;
|
|
HIMAGELIST himl;
|
|
WNDPROC pOldProc;
|
|
|
|
// pmay: 397395
|
|
//
|
|
// Prevent endless loops resulting from accidentally calling this
|
|
// api twice.
|
|
//
|
|
pOldProc = (WNDPROC)GetWindowLongPtr(hwndLv, GWLP_WNDPROC);
|
|
if (pOldProc == LvxcbProc)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
/* Build checkbox image lists.
|
|
*/
|
|
himl = ImageList_Create(
|
|
GetSystemMetrics( SM_CXSMICON ),
|
|
GetSystemMetrics( SM_CYSMICON ),
|
|
ILC_MASK | ILC_MIRROR, 2, 2 );
|
|
|
|
/* The order these are added is significant since it implicitly
|
|
** establishes the state indices matching SI_Unchecked and SI_Checked.
|
|
*/
|
|
hIcon = LoadIcon( hinst, MAKEINTRESOURCE( IID_Unchecked ) );
|
|
if ( NULL != hIcon )
|
|
{
|
|
ImageList_AddIcon( himl, hIcon );
|
|
DeleteObject( hIcon );
|
|
}
|
|
|
|
hIcon = LoadIcon( hinst, MAKEINTRESOURCE( IID_Checked ) );
|
|
if ( NULL != hIcon )
|
|
{
|
|
ImageList_AddIcon( himl, hIcon );
|
|
DeleteObject( hIcon );
|
|
}
|
|
|
|
hIcon = LoadIcon( hinst, MAKEINTRESOURCE( IID_DisabledUnchecked ) );
|
|
if ( NULL != hIcon )
|
|
{
|
|
ImageList_AddIcon( himl, hIcon );
|
|
DeleteObject( hIcon );
|
|
}
|
|
|
|
hIcon = LoadIcon( hinst, MAKEINTRESOURCE( IID_DisabledChecked ) );
|
|
if ( NULL != hIcon )
|
|
{
|
|
ImageList_AddIcon( himl, hIcon );
|
|
DeleteObject( hIcon );
|
|
}
|
|
|
|
ListView_SetImageList( hwndLv, himl, LVSIL_STATE );
|
|
|
|
/* Register atom for use in the Windows XxxProp calls which are used to
|
|
** associate the old WNDPROC with the listview window handle.
|
|
*/
|
|
if (!g_lvxcbContextId)
|
|
g_lvxcbContextId = (LPCTSTR )GlobalAddAtom( _T("RASLVXCB") );
|
|
if (!g_lvxcbContextId)
|
|
return FALSE;
|
|
|
|
/* Subclass the current window procedure.
|
|
*/
|
|
pOldProc = (WNDPROC)SetWindowLongPtr(
|
|
hwndLv, GWLP_WNDPROC, (ULONG_PTR)LvxcbProc );
|
|
|
|
return SetProp( hwndLv, g_lvxcbContextId, (HANDLE )pOldProc );
|
|
}
|
|
|
|
VOID
|
|
ListView_InsertSingleAutoWidthColumn(
|
|
HWND hwndLv )
|
|
|
|
// Insert a single auto-sized column into listview 'hwndLv', e.g. for a
|
|
// list of checkboxes with no visible column header.
|
|
//
|
|
{
|
|
LV_COLUMN col;
|
|
|
|
ZeroMemory( &col, sizeof(col) );
|
|
col.mask = LVCF_FMT;
|
|
col.fmt = LVCFMT_LEFT;
|
|
ListView_InsertColumn( hwndLv, 0, &col );
|
|
ListView_SetColumnWidth( hwndLv, 0, LVSCW_AUTOSIZE );
|
|
}
|
|
|
|
TCHAR*
|
|
Ellipsisize(
|
|
IN HDC hdc,
|
|
IN TCHAR* psz,
|
|
IN INT dxColumn,
|
|
IN INT dxColText OPTIONAL )
|
|
|
|
/* Returns a heap string containing the 'psz' shortened to fit in the
|
|
** given width, if necessary, by truncating and adding "...". 'Hdc' is the
|
|
** device context with the appropiate font selected. 'DxColumn' is the
|
|
** width of the column. It is caller's responsibility to Free the
|
|
** returned string.
|
|
*/
|
|
{
|
|
const TCHAR szDots[] = TEXT("...");
|
|
|
|
SIZE size;
|
|
TCHAR* pszResult;
|
|
TCHAR* pszResultLast;
|
|
TCHAR* pszResult2nd;
|
|
DWORD cch;
|
|
|
|
cch = lstrlen( psz );
|
|
pszResult = (TCHAR*)Malloc( (cch * sizeof(TCHAR)) + sizeof(szDots) );
|
|
if (!pszResult)
|
|
return NULL;
|
|
lstrcpy( pszResult, psz );
|
|
|
|
dxColumn -= dxColText;
|
|
if (dxColumn <= 0)
|
|
{
|
|
/* None of the column text will be visible so bag the calculations and
|
|
** just return the original string.
|
|
*/
|
|
return pszResult;
|
|
}
|
|
|
|
if (!GetTextExtentPoint32( hdc, pszResult, cch, &size ))
|
|
{
|
|
Free( pszResult );
|
|
return NULL;
|
|
}
|
|
|
|
pszResult2nd = CharNext( pszResult );
|
|
pszResultLast = pszResult + cch;
|
|
|
|
while (size.cx > dxColumn && pszResultLast > pszResult2nd)
|
|
{
|
|
/* Doesn't fit. Lop off a character, add the ellipsis, and try again.
|
|
** The minimum result is "..." for empty original or "x..." for
|
|
** non-empty original.
|
|
*/
|
|
pszResultLast = CharPrev( pszResult2nd, pszResultLast );
|
|
lstrcpy( pszResultLast, szDots );
|
|
|
|
if (!GetTextExtentPoint( hdc, pszResult, lstrlen( pszResult ), &size ))
|
|
{
|
|
Free( pszResult );
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return pszResult;
|
|
}
|
|
|
|
BOOL
|
|
LvxDrawItem(
|
|
IN DRAWITEMSTRUCT* pdis,
|
|
IN PLVXCALLBACK pLvxCallback )
|
|
|
|
/* Respond to WM_DRAWITEM by drawing the list view item. 'Pdis' is the
|
|
** information sent by the system. 'PLvxCallback' is caller's callback to
|
|
** get information about drawing the control.
|
|
**
|
|
** Returns true is processed the message, false otherwise.
|
|
*/
|
|
{
|
|
LV_ITEM item;
|
|
INT i;
|
|
INT dxState;
|
|
INT dyState;
|
|
INT dxSmall;
|
|
INT dySmall;
|
|
INT dxIndent;
|
|
UINT uiStyleState;
|
|
UINT uiStyleSmall;
|
|
HIMAGELIST himlState;
|
|
HIMAGELIST himlSmall;
|
|
LVXDRAWINFO* pDrawInfo;
|
|
RECT rc;
|
|
RECT rcClient;
|
|
BOOL fEnabled;
|
|
BOOL fSelected;
|
|
HDC hdc;
|
|
HFONT hfont;
|
|
|
|
|
|
TRACE3("LvxDrawItem,i=%d,a=$%X,s=$%X",
|
|
pdis->itemID,pdis->itemAction,pdis->itemState);
|
|
|
|
/* Make sure this is something we want to handle.
|
|
*/
|
|
if (pdis->CtlType != ODT_LISTVIEW)
|
|
return FALSE;
|
|
|
|
if (pdis->itemAction != ODA_DRAWENTIRE
|
|
&& pdis->itemAction != ODA_SELECT
|
|
&& pdis->itemAction != ODA_FOCUS)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
/* Get item information from the list view.
|
|
*/
|
|
ZeroMemory( &item, sizeof(item) );
|
|
item.mask = LVIF_IMAGE + LVIF_STATE;
|
|
item.iItem = pdis->itemID;
|
|
item.stateMask = LVIS_STATEIMAGEMASK;
|
|
if (!ListView_GetItem( pdis->hwndItem, &item ))
|
|
{
|
|
TRACE("LvxDrawItem GetItem failed");
|
|
return TRUE;
|
|
}
|
|
|
|
/* Stash some useful stuff for reference later.
|
|
*/
|
|
fEnabled = IsWindowEnabled( pdis->hwndItem )
|
|
&& !(pdis->itemState & ODS_DISABLED);
|
|
fSelected = (pdis->itemState & ODS_SELECTED);
|
|
GetClientRect( pdis->hwndItem, &rcClient );
|
|
|
|
/* Callback owner to get drawing information.
|
|
*/
|
|
ASSERT(pLvxCallback);
|
|
pDrawInfo = pLvxCallback( pdis->hwndItem, pdis->itemID );
|
|
ASSERT(pDrawInfo);
|
|
|
|
/* Get image list icon sizes now, though we draw them last because their
|
|
** background is set up during first column text output.
|
|
*/
|
|
dxState = dyState = 0;
|
|
himlState = ListView_GetImageList( pdis->hwndItem, LVSIL_STATE );
|
|
if (himlState)
|
|
ImageList_GetIconSize( himlState, &dxState, &dyState );
|
|
|
|
dxSmall = dySmall = 0;
|
|
himlSmall = ListView_GetImageList( pdis->hwndItem, LVSIL_SMALL );
|
|
if (himlSmall)
|
|
ImageList_GetIconSize( himlSmall, &dxSmall, &dySmall );
|
|
|
|
uiStyleState = uiStyleSmall = ILD_TRANSPARENT;
|
|
|
|
/* Figure out the number of pixels to indent the item, if any.
|
|
*/
|
|
if (pDrawInfo->dxIndent >= 0)
|
|
dxIndent = pDrawInfo->dxIndent;
|
|
else
|
|
{
|
|
if (dxSmall > 0)
|
|
dxIndent = dxSmall;
|
|
else
|
|
dxIndent = GetSystemMetrics( SM_CXSMICON );
|
|
}
|
|
|
|
/* Get a device context for the window and set it up with the font the
|
|
** control says it's using. (Can't use the one that comes in the
|
|
** DRAWITEMSTRUCT because sometimes it has the wrong rectangle, see bug
|
|
** 13106)
|
|
*/
|
|
hdc = GetDC( pdis->hwndItem );
|
|
|
|
if(NULL == hdc)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
hfont = (HFONT )SendMessage( pdis->hwndItem, WM_GETFONT, 0, 0 );
|
|
if (hfont)
|
|
SelectObject( hdc, hfont );
|
|
|
|
/* Set things up as if we'd just got done processing a column that ends
|
|
** after the icons, then loop thru each column from left to right.
|
|
*/
|
|
rc.right = pdis->rcItem.left + dxIndent + dxState + dxSmall;
|
|
rc.top = pdis->rcItem.top;
|
|
rc.bottom = pdis->rcItem.bottom;
|
|
|
|
for (i = 0; i < pDrawInfo->cCols; ++i)
|
|
{
|
|
TCHAR szText[ LVX_MaxColTchars + 1 ];
|
|
TCHAR* pszText;
|
|
INT dxCol;
|
|
|
|
/* Get the column width, adding any index and icon width to the first
|
|
** column.
|
|
*/
|
|
dxCol = ListView_GetColumnWidth( pdis->hwndItem, i );
|
|
if (i == 0)
|
|
dxCol -= dxIndent + dxState + dxSmall;
|
|
|
|
szText[ 0 ] = TEXT('\0');
|
|
ListView_GetItemText( pdis->hwndItem, pdis->itemID, i, szText,
|
|
LVX_MaxColTchars + 1 );
|
|
|
|
/* Update rectangle to enclose just this one item's column 'i'.
|
|
*/
|
|
rc.left = rc.right;
|
|
rc.right = rc.left + dxCol;
|
|
|
|
if ((pDrawInfo->dwFlags & LVXDI_DxFill)
|
|
&& i == pDrawInfo->cCols - 1)
|
|
{
|
|
INT dxWnd = pdis->rcItem.left + rcClient.right;
|
|
|
|
if (rc.right < dxWnd)
|
|
{
|
|
/* When the last column does not fill out a full controls
|
|
** width of space, extend it to the right so it does. Note
|
|
** this does not mean the user can't scroll off to the right
|
|
** if they want.
|
|
** (Abolade-Gbadegesin 03-27-96)
|
|
** Don't subtrace rc.left when there is only one column;
|
|
** this accounts for the space needed for icons.
|
|
*/
|
|
rc.right = pdis->rcItem.right = dxWnd;
|
|
if (i == 0) {
|
|
ListView_SetColumnWidth(pdis->hwndItem, i, rc.right);
|
|
}
|
|
else {
|
|
ListView_SetColumnWidth(
|
|
pdis->hwndItem, i, rc.right - rc.left );
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Lop the text and append "..." if it won't fit in the column.
|
|
*/
|
|
pszText = Ellipsisize( hdc, szText, rc.right - rc.left, LVX_dxColText );
|
|
if (!pszText)
|
|
continue;
|
|
|
|
/* Figure out the appropriate text and background colors for the
|
|
** current item state.
|
|
*/
|
|
if (fEnabled)
|
|
{
|
|
if (fSelected)
|
|
{
|
|
SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
|
|
SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
|
|
if (pDrawInfo->dwFlags & LVXDI_Blend50Sel)
|
|
uiStyleSmall |= ILD_BLEND50;
|
|
}
|
|
else
|
|
{
|
|
if (pDrawInfo->adwFlags[ i ] & LVXDIA_3dFace)
|
|
{
|
|
SetTextColor( hdc, GetSysColor( COLOR_WINDOWTEXT ) );
|
|
SetBkColor( hdc, GetSysColor( COLOR_3DFACE ) );
|
|
}
|
|
else
|
|
{
|
|
SetTextColor( hdc, GetSysColor( COLOR_WINDOWTEXT ) );
|
|
SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pDrawInfo->adwFlags[ i ] & LVXDIA_Static)
|
|
{
|
|
SetTextColor( hdc, GetSysColor( COLOR_WINDOWTEXT ) );
|
|
SetBkColor( hdc, GetSysColor( COLOR_3DFACE ) );
|
|
}
|
|
else
|
|
{
|
|
SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
|
|
SetBkColor( hdc, GetSysColor( COLOR_3DFACE ) );
|
|
}
|
|
|
|
if (pDrawInfo->dwFlags & LVXDI_Blend50Dis)
|
|
uiStyleSmall |= ILD_BLEND50;
|
|
}
|
|
|
|
/* Draw the column text. In the first column the background of any
|
|
** indent and icons is erased to the text background color.
|
|
*/
|
|
{
|
|
RECT rcBg = rc;
|
|
|
|
if (i == 0)
|
|
rcBg.left -= dxIndent + dxState + dxSmall;
|
|
|
|
ExtTextOut( hdc, rc.left + LVX_dxColText,
|
|
rc.top + LVX_dyColText, ETO_CLIPPED + ETO_OPAQUE,
|
|
&rcBg, pszText, lstrlen( pszText ), NULL );
|
|
}
|
|
|
|
Free( pszText );
|
|
}
|
|
|
|
/* Finally, draw the icons, if caller specified any.
|
|
*/
|
|
if (himlState)
|
|
{
|
|
ImageList_Draw( himlState, (item.state >> 12) - 1, hdc,
|
|
pdis->rcItem.left + dxIndent, pdis->rcItem.top, uiStyleState );
|
|
}
|
|
|
|
if (himlSmall)
|
|
{
|
|
ImageList_Draw( himlSmall, item.iImage, hdc,
|
|
pdis->rcItem.left + dxIndent + dxState,
|
|
pdis->rcItem.top, uiStyleSmall );
|
|
}
|
|
|
|
/* Draw the dotted focus rectangle around the whole item, if indicated.
|
|
*/
|
|
//comment for bug 52688 whistler
|
|
// if ((pdis->itemState & ODS_FOCUS) && GetFocus() == pdis->hwndItem)
|
|
// DrawFocusRect( hdc, &pdis->rcItem );
|
|
//
|
|
|
|
ReleaseDC( pdis->hwndItem, hdc );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
LvxMeasureItem(
|
|
IN HWND hwnd,
|
|
IN OUT MEASUREITEMSTRUCT* pmis )
|
|
|
|
/* Respond to WM_MEASUREITEM message, i.e. fill in the height of an item
|
|
** in the ListView. 'Hwnd' is the owner window. 'Pmis' is the structure
|
|
** provided from Windows.
|
|
**
|
|
** Returns true is processed the message, false otherwise.
|
|
*/
|
|
{
|
|
HDC hdc;
|
|
HWND hwndLv;
|
|
HFONT hfont;
|
|
TEXTMETRIC tm;
|
|
UINT dySmIcon;
|
|
RECT rc;
|
|
|
|
TRACE("LvxMeasureItem");
|
|
|
|
if (pmis->CtlType != ODT_LISTVIEW)
|
|
return FALSE;
|
|
|
|
hwndLv = GetDlgItem( hwnd, pmis->CtlID );
|
|
ASSERT(hwndLv);
|
|
|
|
/* Get a device context for the list view control and set up the font the
|
|
** control says it's using. MSDN claims the final font may not be
|
|
** available at this point, but it sure seems to be.
|
|
*/
|
|
hdc = GetDC( hwndLv );
|
|
hfont = (HFONT )SendMessage( hwndLv, WM_GETFONT, 0, 0 );
|
|
if (hfont)
|
|
SelectObject( hdc, hfont );
|
|
|
|
if (GetTextMetrics( hdc, &tm ))
|
|
pmis->itemHeight = tm.tmHeight + 1;
|
|
else
|
|
pmis->itemHeight = 0;
|
|
|
|
/* Make sure it's tall enough for a standard small icon.
|
|
*/
|
|
dySmIcon = (UINT )GetSystemMetrics( SM_CYSMICON );
|
|
if (pmis->itemHeight < dySmIcon + LVX_dyIconSpacing)
|
|
pmis->itemHeight = dySmIcon + LVX_dyIconSpacing;
|
|
|
|
/* Set the width since the docs say to, though I don't think it's used by
|
|
** list view.
|
|
*/
|
|
GetClientRect( hwndLv, &rc );
|
|
pmis->itemWidth = rc.right - rc.left - 1;
|
|
|
|
ReleaseDC( hwndLv, hdc );
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
ListView_OwnerHandler(
|
|
IN HWND hwnd,
|
|
IN UINT unMsg,
|
|
IN WPARAM wparam,
|
|
IN LPARAM lparam,
|
|
IN PLVXCALLBACK pLvxCallback )
|
|
|
|
/* Handler that, when installed, turns a regular report-view-only list
|
|
** view (but with style LVS_OWNERDRAWFIXED) into an enhanced list view
|
|
** with full width selection bar and other custom column display options.
|
|
** It should appear in list view owner's dialog proc as follows:
|
|
**
|
|
** BOOL
|
|
** MyDlgProc(
|
|
** IN HWND hwnd,
|
|
** IN UINT unMsg,
|
|
** IN WPARAM wparam,
|
|
** IN LPARAM lparam )
|
|
** {
|
|
** if (ListView_OwnerHandler(
|
|
** hwnd, unMsg, wParam, lParam, MyLvxCallback ))
|
|
** return TRUE;
|
|
**
|
|
** <the rest of your stuff here>
|
|
** }
|
|
**
|
|
** 'PLvxCallback' is caller's callback routine that provides information
|
|
** about drawing columns and other options.
|
|
**
|
|
** Returns true if processed message, false otherwise.
|
|
*/
|
|
{
|
|
/* This routine executes on EVERY message thru the dialog so keep it
|
|
** efficient, please.
|
|
*/
|
|
switch (unMsg)
|
|
{
|
|
case WM_DRAWITEM:
|
|
return LvxDrawItem( (DRAWITEMSTRUCT* )lparam, pLvxCallback );
|
|
|
|
case WM_MEASUREITEM:
|
|
return LvxMeasureItem( hwnd, (MEASUREITEMSTRUCT* )lparam );
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// StrDup* functions
|
|
TCHAR* _StrDup(LPCTSTR psz ) // my local version...
|
|
|
|
/* Returns heap block containing a copy of 0-terminated string 'psz' or
|
|
** NULL on error or is 'psz' is NULL. It is caller's responsibility to
|
|
** 'Free' the returned string.
|
|
*/
|
|
{
|
|
TCHAR* pszNew = NULL;
|
|
|
|
if (psz)
|
|
{
|
|
pszNew = (TCHAR*)Malloc( (lstrlen( psz ) + 1) * sizeof(TCHAR) );
|
|
if (!pszNew)
|
|
{
|
|
TRACE("StrDup Malloc failed");
|
|
return NULL;
|
|
}
|
|
|
|
lstrcpy( pszNew, psz );
|
|
}
|
|
|
|
return pszNew;
|
|
}
|
|
|
|
TCHAR*
|
|
StrDupTFromW(
|
|
LPCWSTR psz )
|
|
|
|
/* Returns heap block containing a copy of 0-terminated string 'psz' or
|
|
** NULL on error or is 'psz' is NULL. The output string is converted to
|
|
** UNICODE. It is caller's responsibility to Free the returned string.
|
|
*/
|
|
{
|
|
#ifdef UNICODE
|
|
|
|
return _StrDup ( psz );
|
|
|
|
#else // !UNICODE
|
|
|
|
CHAR* pszNew = NULL;
|
|
|
|
if (psz)
|
|
{
|
|
DWORD cb;
|
|
|
|
cb = WideCharToMultiByte( CP_ACP, 0, psz, -1, NULL, 0, NULL, NULL );
|
|
ASSERT(cb);
|
|
|
|
pszNew = (CHAR* )Malloc( cb + 1 );
|
|
if (!pszNew)
|
|
{
|
|
TRACE("StrDupTFromW Malloc failed");
|
|
return NULL;
|
|
}
|
|
|
|
cb = WideCharToMultiByte( CP_ACP, 0, psz, -1, pszNew, cb, NULL, NULL );
|
|
if (cb == 0)
|
|
{
|
|
Free( pszNew );
|
|
TRACE("StrDupTFromW conversion failed");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return pszNew;
|
|
|
|
#endif
|
|
}
|
|
|
|
WCHAR*
|
|
StrDupWFromT(
|
|
LPCTSTR psz )
|
|
|
|
/* Returns heap block containing a copy of 0-terminated string 'psz' or
|
|
** NULL on error or if 'psz' is NULL. The output string is converted to
|
|
** UNICODE. It is caller's responsibility to Free the returned string.
|
|
*/
|
|
{
|
|
#ifdef UNICODE
|
|
|
|
return _StrDup ( psz );
|
|
|
|
#else // !UNICODE
|
|
|
|
WCHAR* pszNew = NULL;
|
|
|
|
if (psz)
|
|
{
|
|
DWORD cb;
|
|
|
|
cb = MultiByteToWideChar( CP_ACP, 0, psz, -1, NULL, 0 );
|
|
ASSERT(cb);
|
|
|
|
pszNew = (WCHAR*)Malloc( (cb + 1) * sizeof(WCHAR) );
|
|
if (!pszNew)
|
|
{
|
|
TRACE("StrDupWFromT Malloc failed");
|
|
return NULL;
|
|
}
|
|
|
|
cb = MultiByteToWideChar( CP_ACP, 0, psz, -1, pszNew, cb );
|
|
if (cb == 0)
|
|
{
|
|
Free( pszNew );
|
|
TRACE("StrDupWFromT conversion failed");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return pszNew;
|
|
#endif
|
|
}
|
|
|
|
void
|
|
IpHostAddrToPsz(
|
|
IN DWORD dwAddr,
|
|
OUT LPTSTR pszBuffer )
|
|
|
|
// Converts an IP address in host byte order to its
|
|
// string representation.
|
|
// pszBuffer should be allocated by the caller and be
|
|
// at least 16 characters long.
|
|
//
|
|
{
|
|
BYTE* pb = (BYTE*)&dwAddr;
|
|
static const TCHAR c_szIpAddr [] = TEXT("%d.%d.%d.%d");
|
|
wsprintf (pszBuffer, c_szIpAddr, pb[3], pb[2], pb[1], pb[0]);
|
|
}
|
|
#ifdef DOWNLEVEL_CLIENT
|
|
DWORD
|
|
IpPszToHostAddr(
|
|
IN LPCTSTR cp )
|
|
|
|
// Converts an IP address represented as a string to
|
|
// host byte order.
|
|
//
|
|
{
|
|
DWORD val, base, n;
|
|
TCHAR c;
|
|
DWORD parts[4], *pp = parts;
|
|
|
|
again:
|
|
// Collect number up to ``.''.
|
|
// Values are specified as for C:
|
|
// 0x=hex, 0=octal, other=decimal.
|
|
//
|
|
val = 0; base = 10;
|
|
if (*cp == TEXT('0'))
|
|
base = 8, cp++;
|
|
if (*cp == TEXT('x') || *cp == TEXT('X'))
|
|
base = 16, cp++;
|
|
while (c = *cp)
|
|
{
|
|
if ((c >= TEXT('0')) && (c <= TEXT('9')))
|
|
{
|
|
val = (val * base) + (c - TEXT('0'));
|
|
cp++;
|
|
continue;
|
|
}
|
|
if ((base == 16) &&
|
|
( ((c >= TEXT('0')) && (c <= TEXT('9'))) ||
|
|
((c >= TEXT('A')) && (c <= TEXT('F'))) ||
|
|
((c >= TEXT('a')) && (c <= TEXT('f'))) ))
|
|
{
|
|
val = (val << 4) + (c + 10 - (
|
|
((c >= TEXT('a')) && (c <= TEXT('f')))
|
|
? TEXT('a')
|
|
: TEXT('A') ) );
|
|
cp++;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
if (*cp == TEXT('.'))
|
|
{
|
|
// Internet format:
|
|
// a.b.c.d
|
|
// a.b.c (with c treated as 16-bits)
|
|
// a.b (with b treated as 24 bits)
|
|
//
|
|
if (pp >= parts + 3)
|
|
return (DWORD) -1;
|
|
*pp++ = val, cp++;
|
|
goto again;
|
|
}
|
|
|
|
// Check for trailing characters.
|
|
//
|
|
if (*cp && (*cp != TEXT(' ')))
|
|
return 0xffffffff;
|
|
|
|
*pp++ = val;
|
|
|
|
// Concoct the address according to
|
|
// the number of parts specified.
|
|
//
|
|
n = (DWORD) (pp - parts);
|
|
switch (n)
|
|
{
|
|
case 1: // a -- 32 bits
|
|
val = parts[0];
|
|
break;
|
|
|
|
case 2: // a.b -- 8.24 bits
|
|
val = (parts[0] << 24) | (parts[1] & 0xffffff);
|
|
break;
|
|
|
|
case 3: // a.b.c -- 8.8.16 bits
|
|
val = (parts[0] << 24) | ((parts[1] & 0xff) << 16) |
|
|
(parts[2] & 0xffff);
|
|
break;
|
|
|
|
case 4: // a.b.c.d -- 8.8.8.8 bits
|
|
val = (parts[0] << 24) | ((parts[1] & 0xff) << 16) |
|
|
((parts[2] & 0xff) << 8) | (parts[3] & 0xff);
|
|
break;
|
|
|
|
default:
|
|
return 0xffffffff;
|
|
}
|
|
|
|
return val;
|
|
}
|
|
#endif
|
|
VOID*
|
|
Free0(
|
|
VOID* p )
|
|
|
|
/* Like Free, but deals with NULL 'p'.
|
|
*/
|
|
{
|
|
if (!p)
|
|
return NULL;
|
|
|
|
return Free( p );
|
|
}
|
|
|
|
HRESULT ActivateLuna(HANDLE* phActivationContext, ULONG_PTR* pulCookie)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
|
|
TCHAR szPath[MAX_PATH];
|
|
if(0 != GetModuleFileName(_Module.GetResourceInstance(), szPath, sizeof(szPath) / sizeof(TCHAR)))
|
|
{
|
|
ACTCTX ActivationContext;
|
|
ZeroMemory(&ActivationContext, sizeof(ActivationContext));
|
|
ActivationContext.cbSize = sizeof(ActivationContext);
|
|
ActivationContext.lpSource = szPath;
|
|
ActivationContext.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID;
|
|
ActivationContext.lpResourceName = MAKEINTRESOURCE(123);
|
|
|
|
ULONG_PTR ulCookie;
|
|
HANDLE hActivationContext = CreateActCtx(&ActivationContext);
|
|
if(NULL != hActivationContext)
|
|
{
|
|
if(TRUE == ActivateActCtx(hActivationContext, &ulCookie))
|
|
{
|
|
*phActivationContext = hActivationContext;
|
|
*pulCookie = ulCookie;
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
ReleaseActCtx(hActivationContext);
|
|
}
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT DeactivateLuna(HANDLE hActivationContext, ULONG_PTR ulCookie)
|
|
{
|
|
DeactivateActCtx(0, ulCookie);
|
|
ReleaseActCtx(hActivationContext);
|
|
return S_OK;
|
|
}
|