You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1814 lines
46 KiB
1814 lines
46 KiB
/* Copyright (c) 1995, Microsoft Corporation, all rights reserved
|
|
**
|
|
** ui.c
|
|
** UI helper routines
|
|
** Listed alphabetically
|
|
**
|
|
** 08/25/95 Steve Cobb
|
|
*/
|
|
|
|
|
|
#include <windows.h> // Win32 root
|
|
#include <windowsx.h> // Win32 macro extensions
|
|
#include <commctrl.h> // Win32 common controls
|
|
#include <debug.h> // Trace and assert
|
|
#include <uiutil.h> // Our public header
|
|
|
|
|
|
/*----------------------------------------------------------------------------
|
|
** Globals
|
|
**----------------------------------------------------------------------------
|
|
*/
|
|
|
|
/* See SetOffDesktop.
|
|
*/
|
|
static LPCWSTR g_SodContextId = NULL;
|
|
|
|
/* Set when running in a mode where WinHelp does not work. This is a
|
|
** workaround to the problem where WinHelp does not work correctly before a
|
|
** user is logged on. See AddContextHelpButton.
|
|
*/
|
|
BOOL g_fNoWinHelp = FALSE;
|
|
|
|
|
|
/*----------------------------------------------------------------------------
|
|
** Local datatypes
|
|
**----------------------------------------------------------------------------
|
|
*/
|
|
|
|
/* SetOffDesktop context.
|
|
*/
|
|
#define SODINFO struct tagSODINFO
|
|
SODINFO
|
|
{
|
|
RECT rectOrg;
|
|
BOOL fWeMadeInvisible;
|
|
};
|
|
|
|
/*----------------------------------------------------------------------------
|
|
** Local prototypes
|
|
**----------------------------------------------------------------------------
|
|
*/
|
|
|
|
BOOL CALLBACK
|
|
CancelOwnedWindowsEnumProc(
|
|
IN HWND hwnd,
|
|
IN LPARAM lparam );
|
|
|
|
BOOL CALLBACK
|
|
CloseOwnedWindowsEnumProc(
|
|
IN HWND hwnd,
|
|
IN LPARAM lparam );
|
|
|
|
|
|
/*----------------------------------------------------------------------------
|
|
** Utility routines
|
|
**----------------------------------------------------------------------------
|
|
*/
|
|
|
|
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 );
|
|
}
|
|
|
|
|
|
VOID
|
|
Button_MakeDefault(
|
|
IN HWND hwndDlg,
|
|
IN HWND hwndPb )
|
|
|
|
/* Make 'hwndPb' the default button on dialog 'hwndDlg'.
|
|
*/
|
|
{
|
|
DWORD dwResult;
|
|
HWND hwndPbOldDefault;
|
|
|
|
dwResult = (DWORD) SendMessage( hwndDlg, DM_GETDEFID, 0, 0 );
|
|
if (HIWORD( dwResult ) == DC_HASDEFID)
|
|
{
|
|
/* Un-default the current default button.
|
|
*/
|
|
hwndPbOldDefault = GetDlgItem( hwndDlg, LOWORD( dwResult ) );
|
|
Button_SetStyle( hwndPbOldDefault, BS_PUSHBUTTON, TRUE );
|
|
}
|
|
|
|
/* Set caller's button to the default.
|
|
*/
|
|
SendMessage( hwndDlg, DM_SETDEFID, GetDlgCtrlID( hwndPb ), 0 );
|
|
Button_SetStyle( hwndPb, BS_DEFPUSHBUTTON, TRUE );
|
|
}
|
|
|
|
|
|
HBITMAP
|
|
Button_CreateBitmap(
|
|
IN HWND hwndPb,
|
|
IN BITMAPSTYLE bitmapstyle )
|
|
|
|
/* Creates a bitmap of 'bitmapstyle' suitable for display on 'hwndPb. The
|
|
** 'hwndPb' must have been created with BS_BITMAP style.
|
|
**
|
|
** 'HwndPb' may be a checkbox with BS_PUSHLIKE style, in which case the
|
|
** button locks down when pressed like a toolbar button. This case
|
|
** requires that a color bitmap be created resulting in two extra
|
|
** restrictions. First, caller must handle WM_SYSCOLORCHANGE and rebuild
|
|
** the bitmaps with the new colors and second, the button cannot be
|
|
** disabled.
|
|
**
|
|
** Returns the handle to the bitmap. Caller can display it on the button
|
|
** as follows:
|
|
**
|
|
** SendMessage( hwndPb, BM_SETIMAGE, 0, (LPARAM )hbitmap );
|
|
**
|
|
** Caller is responsible for calling DeleteObject(hbitmap) when done using
|
|
** the bitmap, typically when the dialog is destroyed.
|
|
**
|
|
** (Adapted from a routine by Tony Romano)
|
|
*/
|
|
{
|
|
RECT rect;
|
|
HDC hdc;
|
|
HDC hdcMem;
|
|
HBITMAP hbitmap;
|
|
HFONT hfont;
|
|
HPEN hpen;
|
|
SIZE sizeText;
|
|
SIZE sizeBitmap;
|
|
INT x;
|
|
INT y;
|
|
TCHAR* psz;
|
|
TCHAR* pszText;
|
|
TCHAR* pszText2;
|
|
DWORD dxBitmap;
|
|
DWORD dxBetween;
|
|
BOOL fOnRight;
|
|
BOOL fPushLike;
|
|
|
|
hdc = NULL;
|
|
hdcMem = NULL;
|
|
hbitmap = NULL;
|
|
hpen = NULL;
|
|
pszText = NULL;
|
|
pszText2 = NULL;
|
|
|
|
switch (bitmapstyle)
|
|
{
|
|
case BMS_UpArrowOnLeft:
|
|
case BMS_DownArrowOnLeft:
|
|
case BMS_UpArrowOnRight:
|
|
case BMS_DownArrowOnRight:
|
|
dxBitmap = 5;
|
|
dxBetween = 4;
|
|
break;
|
|
|
|
case BMS_UpTriangleOnLeft:
|
|
case BMS_DownTriangleOnLeft:
|
|
case BMS_UpTriangleOnRight:
|
|
case BMS_DownTriangleOnRight:
|
|
dxBitmap = 7;
|
|
dxBetween = 6;
|
|
break;
|
|
|
|
default:
|
|
return NULL;
|
|
}
|
|
|
|
fOnRight = (bitmapstyle & BMS_OnRight);
|
|
fPushLike = (GetWindowLong( hwndPb, GWL_STYLE ) & BS_PUSHLIKE);
|
|
|
|
/* Get a memory DC compatible with the button window.
|
|
*/
|
|
hdc = GetDC( hwndPb );
|
|
if (!hdc)
|
|
return NULL;
|
|
hdcMem = CreateCompatibleDC( hdc );
|
|
if (!hdcMem)
|
|
goto BCB_Error;
|
|
|
|
/* Create a compatible bitmap covering the entire button in the memory DC.
|
|
**
|
|
** For a push button, the bitmap is created compatible with the memory DC,
|
|
** NOT the display DC. This causes the bitmap to be monochrome, the
|
|
** default for memory DCs. When GDI maps monochrome bitmaps into color,
|
|
** white is replaced with the background color and black is replaced with
|
|
** the text color, which is exactly what we want. With this technique, we
|
|
** are relieved from explicit handling of changes in system colors.
|
|
**
|
|
** For a push-like checkbox the bitmap is created compatible with the
|
|
** button itself, so the bitmap is typically color.
|
|
*/
|
|
GetClientRect( hwndPb, &rect );
|
|
hbitmap = CreateCompatibleBitmap(
|
|
(fPushLike) ? hdc : hdcMem, rect.right, rect.bottom );
|
|
if (!hbitmap)
|
|
goto BCB_Error;
|
|
ReleaseDC( hwndPb, hdc );
|
|
hdc = NULL;
|
|
SelectObject( hdcMem, hbitmap );
|
|
|
|
/* Select the font the button says it's using.
|
|
*/
|
|
hfont = (HFONT )SendMessage( hwndPb, WM_GETFONT, 0, 0 );
|
|
if (hfont)
|
|
SelectObject( hdcMem, hfont );
|
|
|
|
/* Set appropriate colors for regular and stuck-down states. Don't need
|
|
** to do anything for the monochrome case as the default black pen and
|
|
** white background are what we want.
|
|
*/
|
|
if (fPushLike)
|
|
{
|
|
INT nColor;
|
|
|
|
if (bitmapstyle == BMS_UpArrowOnLeft
|
|
|| bitmapstyle == BMS_UpArrowOnRight
|
|
|| bitmapstyle == BMS_UpTriangleOnLeft
|
|
|| bitmapstyle == BMS_UpTriangleOnRight)
|
|
{
|
|
nColor = COLOR_BTNHILIGHT;
|
|
}
|
|
else
|
|
{
|
|
nColor = COLOR_BTNFACE;
|
|
}
|
|
|
|
SetBkColor( hdcMem, GetSysColor( nColor ) );
|
|
hpen = CreatePen( PS_SOLID, 0, GetSysColor( COLOR_BTNTEXT ) );
|
|
if (hpen)
|
|
SelectObject( hdcMem, hpen );
|
|
}
|
|
|
|
/* The created bitmap is random, so we erase it to the background color.
|
|
** No text is written here.
|
|
*/
|
|
ExtTextOut( hdcMem, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL );
|
|
|
|
/* Get the button label and make a copy with the '&' accelerator-escape
|
|
** removed, which would otherwise mess up our width calculations.
|
|
*/
|
|
pszText = GetText( hwndPb );
|
|
pszText2 = StrDup( pszText );
|
|
if (!pszText || !pszText2)
|
|
goto BCB_Error;
|
|
|
|
for (psz = pszText2; *psz; psz = CharNext( psz ) )
|
|
{
|
|
if (*psz == TEXT('&'))
|
|
{
|
|
lstrcpy( psz, psz + 1 );
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Calculate the width of the button label text.
|
|
*/
|
|
sizeText.cx = 0;
|
|
sizeText.cy = 0;
|
|
GetTextExtentPoint32( hdcMem, pszText2, lstrlen( pszText2 ), &sizeText );
|
|
|
|
/* Draw the text off-center horizontally enough so it is centered with the
|
|
** bitmap symbol added.
|
|
*/
|
|
--rect.bottom;
|
|
sizeBitmap.cx = dxBitmap;
|
|
sizeBitmap.cy = 0;
|
|
|
|
rect.left +=
|
|
((rect.right - (sizeText.cx + sizeBitmap.cx) - dxBetween) / 2);
|
|
|
|
if (fOnRight)
|
|
{
|
|
DrawText( hdcMem, pszText, -1, &rect,
|
|
DT_VCENTER + DT_SINGLELINE + DT_EXTERNALLEADING );
|
|
rect.left += sizeText.cx + dxBetween;
|
|
}
|
|
else
|
|
{
|
|
rect.left += dxBitmap + dxBetween;
|
|
DrawText( hdcMem, pszText, -1, &rect,
|
|
DT_VCENTER + DT_SINGLELINE + DT_EXTERNALLEADING );
|
|
rect.left -= dxBitmap + dxBetween;
|
|
}
|
|
|
|
/* Eliminate the top and bottom 3 pixels of button from consideration for
|
|
** the bitmap symbol. This leaves the button control room to do the
|
|
** border and 3D edges.
|
|
*/
|
|
InflateRect( &rect, 0, -3 );
|
|
|
|
/* Draw the bitmap symbol. The rectangle is now 'dxBitmap' wide and
|
|
** centered vertically with variable height depending on the button size.
|
|
*/
|
|
switch (bitmapstyle)
|
|
{
|
|
case BMS_UpArrowOnLeft:
|
|
case BMS_UpArrowOnRight:
|
|
{
|
|
/* v-left
|
|
** ..... <-top
|
|
** .....
|
|
** .....
|
|
** ..*.. \
|
|
** .***. |
|
|
** ***** |
|
|
** ..*.. |
|
|
** ..*.. |
|
|
** ..*.. > varies depending on font height
|
|
** ..*.. |
|
|
** ..*.. |
|
|
** ..*.. |
|
|
** ..*.. |
|
|
** ..*.. /
|
|
** .....
|
|
** .....
|
|
** .....
|
|
** ..... <-bottom
|
|
*/
|
|
|
|
/* Draw the vertical line.
|
|
*/
|
|
x = rect.left + 2;
|
|
y = rect.top + 3;
|
|
MoveToEx( hdcMem, x, y, NULL );
|
|
LineTo( hdcMem, x, rect.bottom - 3 );
|
|
|
|
/* Draw the 2 crossbars.
|
|
*/
|
|
MoveToEx( hdcMem, x - 1, ++y, NULL );
|
|
LineTo( hdcMem, x + 2, y );
|
|
MoveToEx( hdcMem, x - 2, ++y, NULL );
|
|
LineTo( hdcMem, x + 3, y );
|
|
break;
|
|
}
|
|
|
|
case BMS_DownArrowOnLeft:
|
|
case BMS_DownArrowOnRight:
|
|
{
|
|
/* v-left
|
|
** ..... <-top
|
|
** .....
|
|
** .....
|
|
** ..*.. \
|
|
** ..*.. |
|
|
** ..*.. |
|
|
** ..*.. |
|
|
** ..*.. |
|
|
** ..*.. > varies depending on font height
|
|
** ..*.. |
|
|
** ..*.. |
|
|
** ***** |
|
|
** .***. |
|
|
** ..*.. /
|
|
** .....
|
|
** .....
|
|
** .....
|
|
** ..... <-bottom
|
|
*/
|
|
|
|
/* Draw the vertical line.
|
|
*/
|
|
x = rect.left + 2;
|
|
y = rect.top + 3;
|
|
MoveToEx( hdcMem, x, y, NULL );
|
|
LineTo( hdcMem, x, rect.bottom - 3 );
|
|
|
|
/* Draw the 2 crossbars.
|
|
*/
|
|
y = rect.bottom - 6;
|
|
MoveToEx( hdcMem, x - 2, y, NULL );
|
|
LineTo( hdcMem, x + 3, y );
|
|
MoveToEx( hdcMem, x - 1, ++y, NULL );
|
|
LineTo( hdcMem, x + 2, y );
|
|
break;
|
|
}
|
|
|
|
case BMS_UpTriangleOnLeft:
|
|
case BMS_UpTriangleOnRight:
|
|
{
|
|
/* v-left
|
|
** ....... <-top
|
|
** .......
|
|
** .......
|
|
** .......
|
|
** .......
|
|
** .......
|
|
** .......
|
|
** ...o... <- o indicates x,y origin
|
|
** ..***..
|
|
** .*****.
|
|
** *******
|
|
** .......
|
|
** .......
|
|
** .......
|
|
** .......
|
|
** .......
|
|
** .......
|
|
** ....... <-bottom
|
|
*/
|
|
x = rect.left + 3;
|
|
y = ((rect.bottom - rect.top) / 2) + 2;
|
|
MoveToEx( hdcMem, x, y, NULL );
|
|
LineTo( hdcMem, x + 1, y );
|
|
++y;
|
|
MoveToEx( hdcMem, x - 1, y, NULL );
|
|
LineTo( hdcMem, x + 2, y );
|
|
++y;
|
|
MoveToEx( hdcMem, x - 2, y, NULL );
|
|
LineTo( hdcMem, x + 3, y );
|
|
++y;
|
|
MoveToEx( hdcMem, x - 3, y, NULL );
|
|
LineTo( hdcMem, x + 4, y );
|
|
break;
|
|
}
|
|
|
|
case BMS_DownTriangleOnLeft:
|
|
case BMS_DownTriangleOnRight:
|
|
{
|
|
/* v-left
|
|
** ....... <-top
|
|
** .......
|
|
** .......
|
|
** .......
|
|
** .......
|
|
** .......
|
|
** .......
|
|
** ***o*** <- o indicates x,y origin
|
|
** .*****.
|
|
** ..***..
|
|
** ...*...
|
|
** .......
|
|
** .......
|
|
** .......
|
|
** .......
|
|
** .......
|
|
** .......
|
|
** ....... <-bottom
|
|
*/
|
|
x = rect.left + 3;
|
|
y = ((rect.bottom - rect.top) / 2) + 2;
|
|
MoveToEx( hdcMem, x - 3, y, NULL );
|
|
LineTo( hdcMem, x + 4, y );
|
|
++y;
|
|
MoveToEx( hdcMem, x - 2, y, NULL );
|
|
LineTo( hdcMem, x + 3, y );
|
|
++y;
|
|
MoveToEx( hdcMem, x - 1, y, NULL );
|
|
LineTo( hdcMem, x + 2, y );
|
|
++y;
|
|
MoveToEx( hdcMem, x, y, NULL );
|
|
LineTo( hdcMem, x + 1, y );
|
|
break;
|
|
}
|
|
}
|
|
|
|
BCB_Error:
|
|
|
|
Free0( pszText );
|
|
Free0( pszText2 );
|
|
if (hdc)
|
|
ReleaseDC( hwndPb, hdc );
|
|
if (hdcMem)
|
|
DeleteDC( hdcMem );
|
|
if (hpen)
|
|
DeleteObject( hpen );
|
|
return hbitmap;
|
|
}
|
|
|
|
|
|
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 );
|
|
}
|
|
|
|
|
|
//Add this function for whislter bug 320863 gangz
|
|
//
|
|
//Center expand the window horizontally in its parent window
|
|
// this expansion will remain the left margin between the child window
|
|
// and the parent window
|
|
// hwnd: Child Window
|
|
// hwndRef: Reference window
|
|
// bHoriz: TRUE, means expand horizontally, let the right margin equal the left margin
|
|
// bVert: TRUE, elongate the height proportial to the width;
|
|
// hwndVertBound: the window that our vertical expandasion cannot overlap with
|
|
//
|
|
VOID
|
|
CenterExpandWindowRemainLeftMargin(
|
|
IN HWND hwnd,
|
|
IN HWND hwndRef,
|
|
BOOL bHoriz,
|
|
BOOL bVert,
|
|
IN HWND hwndVertBottomBound)
|
|
{
|
|
RECT rectCur, rectRef, rectVbBound;
|
|
LONG dxCur, dyCur, dxRef;
|
|
POINT ptTemp;
|
|
double ratio;
|
|
BOOL bvbBound = FALSE;
|
|
|
|
if (hwnd)
|
|
{
|
|
GetWindowRect( hwnd, &rectCur );
|
|
|
|
if (hwndRef)
|
|
{
|
|
GetWindowRect( hwndRef, &rectRef );
|
|
|
|
if(hwndVertBottomBound)
|
|
{
|
|
GetWindowRect( hwndVertBottomBound, &rectVbBound );
|
|
|
|
//We only consider normal cases, if hwnd and hwndVertBound are already
|
|
//overlapped, that is the problem beyond this function.
|
|
//
|
|
if ( rectCur.top < rectVbBound.top )
|
|
{
|
|
bvbBound = TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
dxRef = rectRef.right - rectRef.left +1;
|
|
dxCur = rectCur.right - rectCur.left +1;
|
|
dyCur = rectCur.bottom - rectCur.top +1;
|
|
ratio = dyCur*1.0/dxCur;
|
|
|
|
if(bHoriz)
|
|
{
|
|
rectCur.right = rectRef.right - (rectCur.left - rectRef.left);
|
|
}
|
|
|
|
if(bVert)
|
|
{
|
|
rectCur.bottom = rectCur.top +
|
|
(LONG)( (rectCur.right - rectCur.left+1) * ratio );
|
|
}
|
|
|
|
if(bvbBound)
|
|
{
|
|
//if overlapping occurs w/o expansion, we need to fix it
|
|
//then we do the vertical centering,
|
|
// this bounding is basically for JPN bugs 329700 329715 which
|
|
// wont happen on English build
|
|
//
|
|
|
|
if(rectCur.bottom > rectVbBound.top )
|
|
{
|
|
LONG dxResult, dyResult, dyVRef;
|
|
|
|
dyResult = rectVbBound.top - rectCur.top;
|
|
dxResult = (LONG)(dyResult/ratio);
|
|
|
|
ptTemp.x = rectVbBound.left;
|
|
ptTemp.y = rectVbBound.top-1;
|
|
|
|
//For whistler bug 371914 gangz
|
|
//Cannot use ScreenToClient() here
|
|
//On RTL build, we must use MapWindowPoint() instead
|
|
//
|
|
MapWindowPoints(HWND_DESKTOP,
|
|
hwndRef,
|
|
&ptTemp,
|
|
1);
|
|
|
|
dyVRef = ptTemp.y + 1;
|
|
|
|
rectCur.left = rectRef.left + (dxRef - dxResult)/2;
|
|
rectCur.right = rectRef.right - (dxRef-dxResult)/2;
|
|
rectCur.bottom = rectVbBound.top - (dyVRef-dyResult)/2;
|
|
rectCur.top = rectCur.bottom - dyResult;
|
|
}
|
|
}
|
|
|
|
ptTemp.x = rectCur.left;
|
|
ptTemp.y = rectCur.top;
|
|
MapWindowPoints(HWND_DESKTOP,
|
|
hwndRef,
|
|
&ptTemp,
|
|
1);
|
|
|
|
rectCur.left = ptTemp.x;
|
|
rectCur.top = ptTemp.y;
|
|
|
|
ptTemp.x = rectCur.right;
|
|
ptTemp.y = rectCur.bottom;
|
|
MapWindowPoints(HWND_DESKTOP,
|
|
hwndRef,
|
|
&ptTemp,
|
|
1);
|
|
|
|
rectCur.right = ptTemp.x;
|
|
rectCur.bottom = ptTemp.y;
|
|
|
|
//For mirrored build
|
|
//
|
|
if ( rectCur.right < rectCur.left )
|
|
{
|
|
int tmp;
|
|
|
|
tmp = rectCur.right;
|
|
rectCur.right = rectCur.left;
|
|
rectCur.left = tmp;
|
|
}
|
|
|
|
SetWindowPos(
|
|
hwnd,
|
|
NULL,
|
|
rectCur.left,
|
|
rectCur.top,
|
|
rectCur.right - rectCur.left + 1,
|
|
rectCur.bottom - rectCur.top +1,
|
|
SWP_NOZORDER);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
CancelOwnedWindows(
|
|
IN HWND hwnd )
|
|
|
|
/* Sends WM_COMMAND(IDCANCEL) to all windows that are owned by 'hwnd' in
|
|
** the current thread.
|
|
*/
|
|
{
|
|
EnumThreadWindows( GetCurrentThreadId(),
|
|
CloseOwnedWindowsEnumProc, (LPARAM )hwnd );
|
|
}
|
|
|
|
|
|
BOOL CALLBACK
|
|
CancelOwnedWindowsEnumProc(
|
|
IN HWND hwnd,
|
|
IN LPARAM lparam )
|
|
|
|
/* Standard Win32 EnumThreadWindowsWndProc used by CancelOwnedWindows.
|
|
*/
|
|
{
|
|
HWND hwndThis;
|
|
|
|
for (hwndThis = GetParent( hwnd );
|
|
hwndThis;
|
|
hwndThis = GetParent( hwndThis ))
|
|
{
|
|
if (hwndThis == (HWND )lparam)
|
|
{
|
|
FORWARD_WM_COMMAND(
|
|
hwnd, IDCANCEL, NULL, 0, SendMessage );
|
|
break;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
CloseOwnedWindows(
|
|
IN HWND hwnd )
|
|
|
|
/* Sends WM_CLOSE to all windows that are owned by 'hwnd' in the current
|
|
** thread.
|
|
*/
|
|
{
|
|
EnumThreadWindows( GetCurrentThreadId(),
|
|
CloseOwnedWindowsEnumProc, (LPARAM )hwnd );
|
|
}
|
|
|
|
|
|
BOOL CALLBACK
|
|
CloseOwnedWindowsEnumProc(
|
|
IN HWND hwnd,
|
|
IN LPARAM lparam )
|
|
|
|
/* Standard Win32 EnumThreadWindowsWndProc used by CloseOwnedWindows.
|
|
*/
|
|
{
|
|
HWND hwndThis;
|
|
|
|
for (hwndThis = GetParent( hwnd );
|
|
hwndThis;
|
|
hwndThis = GetParent( hwndThis ))
|
|
{
|
|
if (hwndThis == (HWND )lparam)
|
|
{
|
|
SendMessage( hwnd, WM_CLOSE, 0, 0 );
|
|
break;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
INT
|
|
ComboBox_AddItem(
|
|
IN HWND hwndLb,
|
|
IN LPCTSTR pszText,
|
|
IN VOID* pItem )
|
|
|
|
/* Adds data item 'pItem' with displayed text 'pszText' to listbox
|
|
** 'hwndLb'. The item is added sorted if the listbox has LBS_SORT style,
|
|
** or to the end of the list otherwise. If the listbox has LB_HASSTRINGS
|
|
** style, 'pItem' is a null terminated string, otherwise it is any user
|
|
** defined data.
|
|
**
|
|
** Returns the index of the item in the list or negative if error.
|
|
*/
|
|
{
|
|
INT nIndex;
|
|
|
|
nIndex = ComboBox_AddString( hwndLb, pszText );
|
|
if (nIndex >= 0)
|
|
ComboBox_SetItemData( hwndLb, nIndex, pItem );
|
|
return nIndex;
|
|
}
|
|
|
|
|
|
INT
|
|
ComboBox_AddItemFromId(
|
|
IN HINSTANCE hinstance,
|
|
IN HWND hwndLb,
|
|
IN DWORD dwStringId,
|
|
IN VOID* pItem )
|
|
|
|
/* Adds data item 'pItem' to listbox 'hwndLb'. 'dwStringId' is the string
|
|
** ID of the item's displayed text. 'Hinstance' is the app or module
|
|
** instance handle.
|
|
**
|
|
** Returns the index of the item in the list or negative if error.
|
|
*/
|
|
{
|
|
INT i;
|
|
LPCTSTR psz;
|
|
|
|
psz = PszLoadString( hinstance, dwStringId );
|
|
|
|
if (psz)
|
|
{
|
|
i = ComboBox_AddItem( hwndLb, psz, pItem );
|
|
}
|
|
else
|
|
{
|
|
i = LB_ERRSPACE;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
|
|
INT
|
|
ComboBox_AddItemSorted(
|
|
IN HWND hwndLb,
|
|
IN LPCTSTR pszText,
|
|
IN VOID* pItem )
|
|
|
|
/* Adds data item 'pItem' with displayed text 'pszText' to listbox
|
|
** 'hwndLb' in order sorted by 'pszText'. It is assumed all items added
|
|
** to the list to this point are sorted. If the listbox has LB_HASSTRINGS
|
|
** style, 'pItem' is a null terminated string, otherwise it is any user
|
|
** defined data.
|
|
**
|
|
** Returns the index of the item in the list or negative if error.
|
|
*/
|
|
{
|
|
INT nIndex;
|
|
INT i;
|
|
INT c;
|
|
|
|
c = ComboBox_GetCount( hwndLb );
|
|
for (i = 0; i < c; ++i)
|
|
{
|
|
TCHAR* psz;
|
|
|
|
psz = ComboBox_GetPsz( hwndLb, i );
|
|
if (psz)
|
|
{
|
|
if (lstrcmp( pszText, psz ) < 0)
|
|
break;
|
|
Free( psz );
|
|
}
|
|
}
|
|
|
|
if (i >= c)
|
|
i = -1;
|
|
|
|
nIndex = ComboBox_InsertString( hwndLb, i, pszText );
|
|
if (nIndex >= 0)
|
|
ComboBox_SetItemData( hwndLb, nIndex, pItem );
|
|
|
|
return nIndex;
|
|
}
|
|
|
|
|
|
VOID
|
|
ComboBox_AutoSizeDroppedWidth(
|
|
IN HWND hwndLb )
|
|
|
|
/* Set the width of the drop-down list 'hwndLb' to the width of the
|
|
** longest item (or the width of the list box if that's wider).
|
|
*/
|
|
{
|
|
HDC hdc;
|
|
HFONT hfont;
|
|
TCHAR* psz;
|
|
SIZE size;
|
|
DWORD cch;
|
|
DWORD dxNew;
|
|
DWORD i;
|
|
|
|
hfont = (HFONT )SendMessage( hwndLb, WM_GETFONT, 0, 0 );
|
|
if (!hfont)
|
|
return;
|
|
|
|
hdc = GetDC( hwndLb );
|
|
if (!hdc)
|
|
return;
|
|
|
|
SelectObject( hdc, hfont );
|
|
|
|
dxNew = 0;
|
|
for (i = 0; psz = ComboBox_GetPsz( hwndLb, i ); ++i)
|
|
{
|
|
cch = lstrlen( psz );
|
|
if (GetTextExtentPoint32( hdc, psz, cch, &size ))
|
|
{
|
|
if (dxNew < (DWORD )size.cx)
|
|
dxNew = (DWORD )size.cx;
|
|
}
|
|
|
|
Free( psz );
|
|
}
|
|
|
|
ReleaseDC( hwndLb, hdc );
|
|
|
|
/* Allow for the spacing on left and right added by the control.
|
|
*/
|
|
dxNew += 6;
|
|
|
|
/* Figure out if the vertical scrollbar will be displayed and, if so,
|
|
** allow for it's width.
|
|
*/
|
|
{
|
|
RECT rectD;
|
|
RECT rectU;
|
|
DWORD dyItem;
|
|
DWORD cItemsInDrop;
|
|
DWORD cItemsInList;
|
|
|
|
GetWindowRect( hwndLb, &rectU );
|
|
SendMessage( hwndLb, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM )&rectD );
|
|
dyItem = (DWORD)SendMessage( hwndLb, CB_GETITEMHEIGHT, 0, 0 );
|
|
cItemsInDrop = (rectD.bottom - rectU.bottom) / dyItem;
|
|
cItemsInList = ComboBox_GetCount( hwndLb );
|
|
if (cItemsInDrop < cItemsInList)
|
|
dxNew += GetSystemMetrics( SM_CXVSCROLL );
|
|
}
|
|
|
|
SendMessage( hwndLb, CB_SETDROPPEDWIDTH, dxNew, 0 );
|
|
}
|
|
|
|
|
|
#if 0
|
|
VOID
|
|
ComboBox_FillFromPszList(
|
|
IN HWND hwndLb,
|
|
IN DTLLIST* pdtllistPsz )
|
|
|
|
/* Loads 'hwndLb' with an item form each node in the list strings,
|
|
** 'pdtllistPsz'.
|
|
*/
|
|
{
|
|
DTLNODE* pNode;
|
|
|
|
if (!pdtllistPsz)
|
|
return;
|
|
|
|
for (pNode = DtlGetFirstNode( pdtllistPsz );
|
|
pNode;
|
|
pNode = DtlGetNextNode( pNode ))
|
|
{
|
|
TCHAR* psz;
|
|
|
|
psz = (TCHAR* )DtlGetData( pNode );
|
|
ASSERT(psz);
|
|
ComboBox_AddString( hwndLb, psz );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
VOID*
|
|
ComboBox_GetItemDataPtr(
|
|
IN HWND hwndLb,
|
|
IN INT nIndex )
|
|
|
|
/* Returns the address of the 'nIndex'th item context in 'hwndLb' or NULL
|
|
** if none.
|
|
*/
|
|
{
|
|
LRESULT lResult;
|
|
|
|
if (nIndex < 0)
|
|
return NULL;
|
|
|
|
lResult = ComboBox_GetItemData( hwndLb, nIndex );
|
|
if (lResult < 0)
|
|
return NULL;
|
|
|
|
return (VOID* )lResult;
|
|
}
|
|
|
|
|
|
TCHAR*
|
|
ComboBox_GetPsz(
|
|
IN HWND hwnd,
|
|
IN INT nIndex )
|
|
|
|
/* Returns heap block containing the text contents of the 'nIndex'th item
|
|
** of combo box 'hwnd' or NULL. It is caller's responsibility to Free the
|
|
** returned string.
|
|
*/
|
|
{
|
|
INT cch;
|
|
TCHAR* psz;
|
|
|
|
cch = ComboBox_GetLBTextLen( hwnd, nIndex );
|
|
if (cch < 0)
|
|
return NULL;
|
|
|
|
psz = Malloc( (cch + 1) * sizeof(TCHAR) );
|
|
|
|
if (psz)
|
|
{
|
|
*psz = TEXT('\0');
|
|
ComboBox_GetLBText( hwnd, nIndex, psz );
|
|
}
|
|
|
|
return psz;
|
|
}
|
|
|
|
|
|
VOID
|
|
ComboBox_SetCurSelNotify(
|
|
IN HWND hwndLb,
|
|
IN INT nIndex )
|
|
|
|
/* Set selection in listbox 'hwndLb' to 'nIndex' and notify parent as if
|
|
** user had clicked the item which Windows doesn't do for some reason.
|
|
*/
|
|
{
|
|
ComboBox_SetCurSel( hwndLb, nIndex );
|
|
|
|
SendMessage(
|
|
GetParent( hwndLb ),
|
|
WM_COMMAND,
|
|
(WPARAM )MAKELONG(
|
|
(WORD )GetDlgCtrlID( hwndLb ), (WORD )CBN_SELCHANGE ),
|
|
(LPARAM )hwndLb );
|
|
}
|
|
|
|
|
|
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 = 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;
|
|
}
|
|
|
|
|
|
VOID
|
|
ExpandWindow(
|
|
IN HWND hwnd,
|
|
IN LONG dx,
|
|
IN LONG dy )
|
|
|
|
/* Expands window 'hwnd' 'dx' pels to the right and 'dy' pels down from
|
|
** it's current size.
|
|
*/
|
|
{
|
|
RECT rect;
|
|
|
|
GetWindowRect( hwnd, &rect );
|
|
|
|
SetWindowPos( hwnd, NULL,
|
|
0, 0, rect.right - rect.left + dx, rect.bottom - rect.top + dy,
|
|
SWP_NOMOVE + SWP_NOZORDER );
|
|
}
|
|
|
|
|
|
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 = Malloc( (cch + 1) * sizeof(TCHAR) );
|
|
|
|
if (psz)
|
|
{
|
|
*psz = TEXT('\0');
|
|
GetWindowText( hwnd, psz, cch + 1 );
|
|
}
|
|
|
|
return psz;
|
|
}
|
|
|
|
|
|
HWND
|
|
HwndFromCursorPos(
|
|
IN HINSTANCE hinstance,
|
|
IN POINT* ppt OPTIONAL )
|
|
|
|
/* Returns a "Static" control window created at the specified position
|
|
** (or at the cursor position if NULL is passed in).
|
|
** The window is moved off the desktop using SetOffDesktop()
|
|
** so that it can be specified as the owner window
|
|
** for a popup dialog shown using MsgDlgUtil.
|
|
** The window returned should be destroyed using DestroyWindow().
|
|
*/
|
|
{
|
|
|
|
HWND hwnd;
|
|
POINT pt;
|
|
|
|
if (ppt) { pt = *ppt; }
|
|
else { GetCursorPos(&pt); }
|
|
|
|
//
|
|
// create the window
|
|
//
|
|
|
|
hwnd = CreateWindowEx(
|
|
WS_EX_TOOLWINDOW, TEXT("Static"), NULL, WS_POPUP, pt.x, pt.y,
|
|
1, 1, NULL, NULL, hinstance, NULL
|
|
);
|
|
if (!hwnd) { return NULL; }
|
|
|
|
//
|
|
// move it off the desktop
|
|
//
|
|
|
|
SetOffDesktop(hwnd, SOD_MoveOff, NULL);
|
|
|
|
ShowWindow(hwnd, SW_SHOW);
|
|
|
|
return hwnd;
|
|
}
|
|
|
|
LPTSTR
|
|
IpGetAddressAsText(
|
|
HWND hwndIp )
|
|
{
|
|
if (SendMessage( hwndIp, IPM_ISBLANK, 0, 0 ))
|
|
{
|
|
return NULL;
|
|
}
|
|
else
|
|
{
|
|
DWORD dwIpAddrHost;
|
|
TCHAR szIpAddr [32];
|
|
|
|
SendMessage( hwndIp, IPM_GETADDRESS, 0, (LPARAM)&dwIpAddrHost );
|
|
IpHostAddrToPsz( dwIpAddrHost, szIpAddr );
|
|
return StrDup( szIpAddr );
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
IpSetAddressText(
|
|
HWND hwndIp,
|
|
LPCTSTR pszIpAddress )
|
|
{
|
|
if (pszIpAddress)
|
|
{
|
|
DWORD dwIpAddrHost = IpPszToHostAddr( pszIpAddress );
|
|
SendMessage( hwndIp, IPM_SETADDRESS, 0, dwIpAddrHost );
|
|
}
|
|
else
|
|
{
|
|
SendMessage( hwndIp, IPM_CLEARADDRESS, 0, 0 );
|
|
}
|
|
}
|
|
|
|
|
|
INT
|
|
ListBox_AddItem(
|
|
IN HWND hwndLb,
|
|
IN TCHAR* pszText,
|
|
IN VOID* pItem )
|
|
|
|
/* Adds data item 'pItem' with displayed text 'pszText' to listbox
|
|
** 'hwndLb'. The item is added sorted if the listbox has LBS_SORT style,
|
|
** or to the end of the list otherwise. If the listbox has LB_HASSTRINGS
|
|
** style, 'pItem' is a null terminated string, otherwise it is any user
|
|
** defined data.
|
|
**
|
|
** Returns the index of the item in the list or negative if error.
|
|
*/
|
|
{
|
|
INT nIndex;
|
|
|
|
nIndex = ListBox_AddString( hwndLb, pszText );
|
|
if (nIndex >= 0)
|
|
ListBox_SetItemData( hwndLb, nIndex, pItem );
|
|
return nIndex;
|
|
}
|
|
|
|
|
|
TCHAR*
|
|
ListBox_GetPsz(
|
|
IN HWND hwnd,
|
|
IN INT nIndex )
|
|
|
|
/* Returns heap block containing the text contents of the 'nIndex'th item
|
|
** of list box 'hwnd' or NULL. It is caller's responsibility to Free the
|
|
** returned string.
|
|
*/
|
|
{
|
|
INT cch;
|
|
TCHAR* psz;
|
|
|
|
cch = ListBox_GetTextLen( hwnd, nIndex );
|
|
psz = Malloc( (cch + 1) * sizeof(TCHAR) );
|
|
|
|
if (psz)
|
|
{
|
|
*psz = TEXT('\0');
|
|
ListBox_GetText( hwnd, nIndex, psz );
|
|
}
|
|
|
|
return psz;
|
|
}
|
|
|
|
|
|
INT
|
|
ListBox_IndexFromString(
|
|
IN HWND hwnd,
|
|
IN TCHAR* psz )
|
|
|
|
/* Returns the index of the item in string list 'hwnd' that matches 'psz'
|
|
** or -1 if not found. Unlike, ListBox_FindStringExact, this compare in
|
|
** case sensitive.
|
|
*/
|
|
{
|
|
INT i;
|
|
INT c;
|
|
|
|
c = ListBox_GetCount( hwnd );
|
|
|
|
for (i = 0; i < c; ++i)
|
|
{
|
|
TCHAR* pszThis;
|
|
|
|
pszThis = ListBox_GetPsz( hwnd, i );
|
|
if (pszThis)
|
|
{
|
|
BOOL f;
|
|
|
|
f = (lstrcmp( psz, pszThis ) == 0);
|
|
Free( pszThis );
|
|
if (f)
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
VOID
|
|
ListBox_SetCurSelNotify(
|
|
IN HWND hwndLb,
|
|
IN INT nIndex )
|
|
|
|
/* Set selection in listbox 'hwndLb' to 'nIndex' and notify parent as if
|
|
** user had clicked the item which Windows doesn't do for some reason.
|
|
*/
|
|
{
|
|
ListBox_SetCurSel( hwndLb, nIndex );
|
|
|
|
SendMessage(
|
|
GetParent( hwndLb ),
|
|
WM_COMMAND,
|
|
(WPARAM )MAKELONG(
|
|
(WORD )GetDlgCtrlID( hwndLb ), (WORD )LBN_SELCHANGE ),
|
|
(LPARAM )hwndLb );
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
VOID*
|
|
ListView_GetSelectedParamPtr(
|
|
IN HWND hwndLv )
|
|
|
|
/* Returns the lParam address of the first selected item in 'hwndLv' or
|
|
** NULL if none or error.
|
|
*/
|
|
{
|
|
INT iSel;
|
|
LV_ITEM item;
|
|
|
|
iSel = ListView_GetNextItem( hwndLv, -1, LVNI_SELECTED );
|
|
if (iSel < 0)
|
|
return NULL;
|
|
|
|
return ListView_GetParamPtr( hwndLv, iSel );
|
|
}
|
|
|
|
|
|
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 );
|
|
}
|
|
|
|
|
|
BOOL
|
|
ListView_SetParamPtr(
|
|
IN HWND hwndLv,
|
|
IN INT iItem,
|
|
IN VOID* pParam )
|
|
|
|
/* Set the lParam address of the 'iItem' item in 'hwndLv' to 'pParam'.
|
|
** Return true if successful, false otherwise.
|
|
*/
|
|
{
|
|
LV_ITEM item;
|
|
|
|
ZeroMemory( &item, sizeof(item) );
|
|
item.mask = LVIF_PARAM;
|
|
item.iItem = iItem;
|
|
item.lParam = (LPARAM )pParam;
|
|
|
|
return ListView_SetItem( hwndLv, &item );
|
|
}
|
|
|
|
|
|
VOID
|
|
Menu_CreateAccelProxies(
|
|
IN HINSTANCE hinst,
|
|
IN HWND hwndParent,
|
|
IN DWORD dwMid )
|
|
|
|
/* Causes accelerators on a popup menu to choose menu commands when the
|
|
** popup menu is not dropped down. 'Hinst' is the app/dll instance.
|
|
** 'HwndParent' is the window that receives the popup menu command
|
|
** messages. 'DwMid' is the menu ID of the menu bar containing the popup
|
|
** menu.
|
|
*/
|
|
{
|
|
#define MCF_cbBuf 512
|
|
|
|
HMENU hmenuBar;
|
|
HMENU hmenuPopup;
|
|
TCHAR szBuf[ MCF_cbBuf ];
|
|
MENUITEMINFO item;
|
|
INT i;
|
|
|
|
hmenuBar = LoadMenu( hinst, MAKEINTRESOURCE( dwMid ) );
|
|
ASSERT(hmenuBar);
|
|
hmenuPopup = GetSubMenu( hmenuBar, 0 );
|
|
ASSERT(hmenuPopup);
|
|
|
|
/* Loop thru menu items on the popup menu.
|
|
*/
|
|
for (i = 0; TRUE; ++i)
|
|
{
|
|
ZeroMemory( &item, sizeof(item) );
|
|
item.cbSize = sizeof(item);
|
|
item.fMask = MIIM_TYPE + MIIM_ID;
|
|
item.dwTypeData = szBuf;
|
|
item.cch = MCF_cbBuf;
|
|
|
|
if (!GetMenuItemInfo( hmenuPopup, i, TRUE, &item ))
|
|
break;
|
|
|
|
if (item.fType != MFT_STRING)
|
|
continue;
|
|
|
|
/* Create an off-screen button on the parent with the same ID and
|
|
** text as the menu item.
|
|
*/
|
|
CreateWindow( TEXT("button"), szBuf, WS_CHILD,
|
|
30000, 30000, 0, 0, hwndParent, (HMENU )UlongToPtr(item.wID), hinst, NULL );
|
|
}
|
|
|
|
DestroyMenu( hmenuBar );
|
|
}
|
|
|
|
|
|
VOID
|
|
ScreenToClientRect(
|
|
IN HWND hwnd,
|
|
IN OUT RECT* pRect )
|
|
|
|
/* Converts '*pRect' from screen to client coordinates of 'hwnd'.
|
|
*/
|
|
{
|
|
POINT xyUL;
|
|
POINT xyBR;
|
|
|
|
xyUL.x = pRect->left;
|
|
xyUL.y = pRect->top;
|
|
xyBR.x = pRect->right;
|
|
xyBR.y = pRect->bottom;
|
|
|
|
ScreenToClient( hwnd, &xyUL );
|
|
ScreenToClient( hwnd, &xyBR );
|
|
|
|
pRect->left = xyUL.x;
|
|
pRect->top = xyUL.y;
|
|
pRect->right = xyBR.x;
|
|
pRect->bottom = xyBR.y;
|
|
}
|
|
|
|
|
|
BOOL
|
|
SetOffDesktop(
|
|
IN HWND hwnd,
|
|
IN DWORD dwAction,
|
|
OUT RECT* prectOrg )
|
|
|
|
/* Move 'hwnd' back and forth from the visible desktop to the area
|
|
** off-screen. Use this when you want to "hide" your owner window without
|
|
** hiding yourself which Windows automatically does.
|
|
**
|
|
** 'dwAction' describes the action to take:
|
|
** SOD_Moveoff: Move 'hwnd' off the desktop.
|
|
** SOD_MoveBackFree: Undo SOD_MoveOff.
|
|
** SOD_GetOrgRect: Retrieves original 'hwnd' position.
|
|
** SOD_Free: Free SOD_MoveOff context without restoring.
|
|
** SOD_MoveBackHidden: Move window back, but hidden so you can call
|
|
** routines that internally query the position
|
|
** of the window. Undo with SOD_Moveoff.
|
|
**
|
|
** '*prectOrg' is set to the original window position when 'dwAction' is
|
|
** SOD_GetOrgRect. Otherwise, it is ignored, and may be NULL.
|
|
**
|
|
** Returns true if the window has a SODINFO context, false otherwise.
|
|
*/
|
|
{
|
|
SODINFO* pInfo;
|
|
|
|
TRACE2("SetOffDesktop(h=$%08x,a=%d)",hwnd,dwAction);
|
|
|
|
if (!g_SodContextId)
|
|
{
|
|
g_SodContextId = (LPCTSTR )GlobalAddAtom( TEXT("RASSETOFFDESKTOP") );
|
|
if (!g_SodContextId)
|
|
return FALSE;
|
|
}
|
|
|
|
pInfo = (SODINFO* )GetProp( hwnd, g_SodContextId );
|
|
|
|
if (dwAction == SOD_MoveOff)
|
|
{
|
|
if (pInfo)
|
|
{
|
|
/* Caller is undoing a SOD_MoveBackHidden.
|
|
*/
|
|
SetWindowPos( hwnd, NULL,
|
|
pInfo->rectOrg.left, GetSystemMetrics( SM_CYSCREEN ),
|
|
0, 0, SWP_NOSIZE + SWP_NOZORDER );
|
|
|
|
if (pInfo->fWeMadeInvisible)
|
|
{
|
|
ShowWindow( hwnd, SW_SHOW );
|
|
pInfo->fWeMadeInvisible = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BOOL f;
|
|
|
|
pInfo = (SODINFO* )Malloc( sizeof(SODINFO) );
|
|
if (!pInfo)
|
|
return FALSE;
|
|
|
|
f = IsWindowVisible( hwnd );
|
|
if (!f)
|
|
ShowWindow( hwnd, SW_HIDE );
|
|
|
|
GetWindowRect( hwnd, &pInfo->rectOrg );
|
|
SetWindowPos( hwnd, NULL, pInfo->rectOrg.left,
|
|
GetSystemMetrics( SM_CYSCREEN ),
|
|
0, 0, SWP_NOSIZE + SWP_NOZORDER );
|
|
|
|
if (!f)
|
|
ShowWindow( hwnd, SW_SHOW );
|
|
|
|
pInfo->fWeMadeInvisible = FALSE;
|
|
SetProp( hwnd, g_SodContextId, pInfo );
|
|
}
|
|
}
|
|
else if (dwAction == SOD_MoveBackFree || dwAction == SOD_Free)
|
|
{
|
|
if (pInfo)
|
|
{
|
|
if (dwAction == SOD_MoveBackFree)
|
|
{
|
|
SetWindowPos( hwnd, NULL,
|
|
pInfo->rectOrg.left, pInfo->rectOrg.top, 0, 0,
|
|
SWP_NOSIZE + SWP_NOZORDER );
|
|
}
|
|
|
|
Free( pInfo );
|
|
RemoveProp( hwnd, g_SodContextId );
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
else if (dwAction == SOD_GetOrgRect)
|
|
{
|
|
if (!pInfo)
|
|
return FALSE;
|
|
|
|
*prectOrg = pInfo->rectOrg;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(dwAction==SOD_MoveBackHidden);
|
|
|
|
if (!pInfo)
|
|
return FALSE;
|
|
|
|
if (IsWindowVisible( hwnd ))
|
|
{
|
|
ShowWindow( hwnd, SW_HIDE );
|
|
pInfo->fWeMadeInvisible = TRUE;
|
|
}
|
|
|
|
SetWindowPos( hwnd, NULL,
|
|
pInfo->rectOrg.left, pInfo->rectOrg.top, 0, 0,
|
|
SWP_NOSIZE + SWP_NOZORDER );
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
SetDlgItemNum(
|
|
IN HWND hwndDlg,
|
|
IN INT iDlgItem,
|
|
IN UINT uValue )
|
|
|
|
/* Similar to SetDlgItemInt, but this function uses commas (or the
|
|
** locale-specific separator) to delimit thousands
|
|
*/
|
|
{
|
|
|
|
DWORD dwSize;
|
|
TCHAR szNumber[32];
|
|
|
|
dwSize = 30;
|
|
GetNumberString(uValue, szNumber, &dwSize);
|
|
|
|
return SetDlgItemText(hwndDlg, iDlgItem, szNumber);
|
|
}
|
|
|
|
|
|
BOOL
|
|
SetEvenTabWidths(
|
|
IN HWND hwndDlg,
|
|
IN DWORD cPages )
|
|
|
|
/* Set the tabs on property sheet 'hwndDlg' to have even fixed width.
|
|
** 'cPages' is the number of pages on the property sheet.
|
|
**
|
|
** Returns true if successful, false if any of the tabs requires more than
|
|
** the fixed width in which case the call has no effect.
|
|
*/
|
|
{
|
|
HWND hwndTab;
|
|
LONG lStyle;
|
|
RECT rect;
|
|
INT dxFixed;
|
|
|
|
/* The tab control uses hard-coded 1-inch tabs when you set FIXEDWIDTH
|
|
** style while we want the tabs to fill the page, so we figure out the
|
|
** correct width ourselves. For some reason, without a fudge-factor (the
|
|
** -10) the expansion does not fit on a single line. The factor
|
|
** absolutely required varies between large and small fonts and the number
|
|
** of tabs does not seem to be a factor.
|
|
*/
|
|
hwndTab = PropSheet_GetTabControl( hwndDlg );
|
|
GetWindowRect( hwndTab, &rect );
|
|
dxFixed = (rect.right - rect.left - 10 ) / cPages;
|
|
|
|
while (cPages > 0)
|
|
{
|
|
RECT rectTab;
|
|
|
|
--cPages;
|
|
if (!TabCtrl_GetItemRect( hwndTab, cPages, &rectTab )
|
|
|| dxFixed < rectTab.right - rectTab.left)
|
|
{
|
|
/* This tab requires more than the fixed width. Since the fixed
|
|
** width is unworkable do nothing.
|
|
*/
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
TabCtrl_SetItemSize( hwndTab, dxFixed, 0 );
|
|
lStyle = GetWindowLong( hwndTab, GWL_STYLE );
|
|
SetWindowLong( hwndTab, GWL_STYLE, lStyle | TCS_FIXEDWIDTH );
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
HFONT
|
|
SetFont(
|
|
HWND hwndCtrl,
|
|
TCHAR* pszFaceName,
|
|
BYTE bfPitchAndFamily,
|
|
INT nPointSize,
|
|
BOOL fUnderline,
|
|
BOOL fStrikeout,
|
|
BOOL fItalic,
|
|
BOOL fBold )
|
|
|
|
/* Sets font of control 'hwndCtrl' to a font matching the specified
|
|
** attributes. See LOGFONT documentation.
|
|
**
|
|
** Returns the HFONT of the created font if successful or NULL. Caller
|
|
** should DeleteObject the returned HFONT when the control is destroyed.
|
|
*/
|
|
{
|
|
LOGFONT logfont;
|
|
INT nPelsPerInch;
|
|
HFONT hfont;
|
|
|
|
{
|
|
HDC hdc = GetDC( NULL );
|
|
if (hdc == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
nPelsPerInch = GetDeviceCaps( hdc, LOGPIXELSY );
|
|
ReleaseDC( NULL, hdc );
|
|
}
|
|
|
|
ZeroMemory( &logfont, sizeof(logfont) );
|
|
logfont.lfHeight = -MulDiv( nPointSize, nPelsPerInch, 72 );
|
|
|
|
{
|
|
DWORD cp;
|
|
CHARSETINFO csi;
|
|
|
|
cp = GetACP();
|
|
if (TranslateCharsetInfo( &cp, &csi, TCI_SRCCODEPAGE ))
|
|
logfont.lfCharSet = (UCHAR)csi.ciCharset;
|
|
else
|
|
logfont.lfCharSet = ANSI_CHARSET;
|
|
}
|
|
|
|
logfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
|
|
logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
|
|
logfont.lfQuality = DEFAULT_QUALITY;
|
|
logfont.lfPitchAndFamily = bfPitchAndFamily;
|
|
lstrcpy( logfont.lfFaceName, pszFaceName );
|
|
logfont.lfUnderline = (BYTE)fUnderline;
|
|
logfont.lfStrikeOut = (BYTE)fStrikeout;
|
|
logfont.lfItalic = (BYTE)fItalic;
|
|
logfont.lfWeight = (fBold) ? FW_BOLD : FW_NORMAL;
|
|
|
|
hfont = CreateFontIndirect( &logfont );
|
|
if (hfont)
|
|
{
|
|
SendMessage( hwndCtrl,
|
|
WM_SETFONT, (WPARAM )hfont, MAKELPARAM( TRUE, 0 ) );
|
|
}
|
|
|
|
return hfont;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
SlideWindow(
|
|
IN HWND hwnd,
|
|
IN HWND hwndParent,
|
|
IN LONG dx,
|
|
IN LONG dy )
|
|
|
|
/* Moves window 'hwnd' 'dx' pels right and 'dy' pels down from it's
|
|
** current position. 'HwndParent' is the handle of 'hwnd's parent or NULL
|
|
** if 'hwnd' is not a child window.
|
|
*/
|
|
{
|
|
RECT rect;
|
|
POINT xy;
|
|
|
|
GetWindowRect( hwnd, &rect );
|
|
xy.x = rect.left;
|
|
xy.y = rect.top;
|
|
|
|
if (GetParent( hwnd ))
|
|
{
|
|
/*
|
|
* For mirrored parents we us the right point not the left.
|
|
*/
|
|
if (GetWindowLongPtr(GetParent( hwnd ), GWL_EXSTYLE) & WS_EX_LAYOUTRTL) {
|
|
xy.x = rect.right;
|
|
}
|
|
ScreenToClient( hwndParent, &xy );
|
|
}
|
|
|
|
SetWindowPos( hwnd, NULL,
|
|
xy.x + dx, xy.y + dy, 0, 0,
|
|
SWP_NOSIZE + SWP_NOZORDER );
|
|
}
|
|
|
|
|
|
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 );
|
|
}
|