Win32 API: How to avoid flickering of basic window controls? - c++

I scroll the parent window by invalidating and redrawing all the content inside WM_PAINT according to current nPos value from scrollbar. I want to scroll without flickering so I handle WM_ERASEBKGND to avoid redrawing the background. I also do simple double buffering for my TextOut calls and for some bitmaps that are displayed. It works good except my child controls. They flicker badly especially when nPos == 0 and app handles SB_LINEUP or nPos == nMax and app handles SB_LINEDOWN or when dragging and dropping the scrollbar. I move them with MoveWindow(). I also have tried DeferWindowPos(). I've googled it for a solution for flickering but it doesn't work or I'm not using it correctly. How to eliminate flickering of child controls?
P.S. When I use WS_CLIPCHILDREN style for main window the controls aren't flickering but when scrolling some parts of my window get messed up, especially my static control on which I'm drawing some things by handling WM_DRAWITEM.
EDIT: (simplified code)
I double buffer inside WM_PAINT like this:
case WM_PAINT: {
hDC = BeginPaint( hwnd, &PS );
hDCMem = CreateCompatibleDC( hDC );
hBMMem = CreateCompatibleBitmap( hDC, wnd_x, wnd_y );
SelectObject( hDCMem, hBMMem );
Font = CreateFont( 130, 50, 0, 0, FW_SEMIBOLD, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_MODERN, "Arial" );
SelectObject( hDCMem, Font );
SetTextColor( hDCMem, RGB( 30, 144, 255 ) );
SetBkColor( hDCMem, RGB( 192, 192, 192 ) );
TextOut( hDCMem, 650, 69 - scr_pos, "ABC", 3 );
MoveWindow( GetDlgItem( hwnd, ID_BUTT_START ), 720, 500 - scr_pos, 160, 150, TRUE );
BitBlt( hDC, 0, 0, wnd_x, wnd_y, hDCMem, 0, 0, SRCCOPY );
DeleteObject( hBMMem );
DeleteDC( hDCMem );
EndPaint( hwnd, &PS );
}
scr_pos is current nPos value taken from scrollbar.
case WM_ERASEBKGND:
return 1;
break;
case WM_VSCROLL: {
SCROLLINFO sinfo;
ZeroMemory( &sinfo, sizeof( SCROLLINFO ) );
sinfo.cbSize = sizeof( SCROLLINFO );
sinfo.fMask = SIF_POS | SIF_PAGE | SIF_TRACKPOS;
GetScrollInfo( hwnd, SB_VERT, &sinfo );
scr_pos = sinfo.nPos;
switch( LOWORD( wParam ) ) {
case SB_TOP:
scr_pos = 0;
break;
case SB_BOTTOM:
scr_pos = 4000;
break;
case SB_LINEUP: {
scr_pos -= 200;
if ( scr_pos < 0 ) {
scr_pos = 0;
}
}
break;
case SB_LINEDOWN: {
scr_pos += 200;
if ( scr_pos > 4000 ) {
scr_pos = 4000;
}
}
break;
case SB_PAGEUP: {
scr_pos -= si.nPage;
if( scr_pos < 0 ) {
scr_pos = 0;
}
}
break;
case SB_PAGEDOWN: {
scr_pos += si.nPage;
if( scr_pos > 4000 ) {
scr_pos = 4000;
}
}
break;
case SB_THUMBPOSITION:
scr_pos = HIWORD(wParam);
break;
case SB_THUMBTRACK:
scr_pos = HIWORD(wParam);
break;
}
RedrawWindow( hwnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT );
// InvalidateRect ( hwnd, NULL, true );
// UpdateWindow( hwnd );
ZeroMemory( & sinfo, sizeof( SCROLLINFO ) );
sinfo.cbSize = sizeof( SCROLLINFO );
sinfo.fMask = SIF_POS;
snfo.nPos = scr_pos;
SetScrollInfo( hwnd, SB_VERT, & sinfo, TRUE );
}
}
break;
Nothing flickers except child controls...

WS_CLIPCHILDREN is indeed the solution. Go ahead and activate that, you'll have to track down the cause of "parts of my window get messed up", but you haven't provided sufficient detail about that.
You might also look into ScrollWindowEx. That documentation specifically describes how to correctly reposition child windows during scrolling.

Related

How to set header width equal to its list view control width when list view is resized?

I want to set width of header in a list view to width of its list view control when it is resized using win32 api.So i use ListView_SetColumnWidth() to set its width equal to its control's width but it does not work.
This is The code inside WinMain():
InitCommonControls();
hwndList1 = CreateWindow(WC_LISTVIEW , L"" , WS_VISIBLE | WS_CHILD | LVS_REPORT | WS_BORDER | WS_VSCROLL, 10 , 10 , width , height, hwnd, NULL, GetModuleHandle(NULL), 0);
//Sub classing the list control
SetWindowSubclass(hwndList1 ,ListProc,0 ,NULL);
SendMessage(hwndList1,LVM_SETEXTENDEDLISTVIEWSTYLE,LVS_EX_FULLROWSELECT,LVS_EX_FULLROWSELECT);
hHeader1=ListView_GetHeader(hwndList1);
GetClientRect(hwndList1 , &rect1);
CreateColumn(hwndList1 , 0 , (char*)L"MASTER" , rect1.right );
//enable arrows
EnableScrollBar(hwndList1 , SB_VERT , ESB_ENABLE_BOTH);
//scroll down
SendMessage(hwndList1, WM_VSCROLL, SB_BOTTOM, 0L);
This is ListProc():
//the list proc
LRESULT CALLBACK ListProc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp,UINT_PTR, DWORD_PTR ){
switch(msg)
{
case WM_NOTIFY :
if (((LPNMHDR) lp)->code == NM_CUSTOMDRAW)
{
LPNMCUSTOMDRAW lpcd = (LPNMCUSTOMDRAW)lp;
switch(lpcd->dwDrawStage)
{
case CDDS_PREPAINT :
return CDRF_NOTIFYITEMDRAW;
case CDDS_ITEMPREPAINT:
{
SetBkColor(lpcd->hdc, RGB(0, 135, 234));
SetTextColor(lpcd->hdc, RGB(255, 255, 245));
return CDRF_NEWFONT;
}
break;
}
}
break;
case WM_NCPAINT:
{
RECT rc;
GetWindowRect(hwnd, &rc);
OffsetRect(&rc, -rc.left, -rc.top);
auto hdc = GetWindowDC(hwnd);
auto hpen = CreatePen(PS_SOLID, 1, RGB(201, 201, 201));
auto oldpen = SelectObject(hdc, hpen);
SelectObject(hdc, GetStockObject(NULL_BRUSH));
Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom);
SelectObject(hdc, oldpen);
DeleteObject(oldpen);
ReleaseDC(hwnd, hdc);
return 0;
}
case WM_NCDESTROY:
RemoveWindowSubclass(hwnd, ListProc, 0);
break;
}
return DefSubclassProc(hwnd, msg, wp, lp);
}
And the following code is WndProc() for parent window procedure:
//The window procedure
LRESULT CALLBACK WndProc( HWND hwnd , UINT msg , WPARAM wParam , LPARAM lParam){
switch(msg){
case WM_SIZE:{
int nHeight , nWidth;
width =(int)((nWidth /2) * 0.8);
height =(int)((nHeight/2) * 0.7);
if( wParam == SIZE_RESTORED ){
SetWindowPos(hwndList1, 0 , 10, 10 , width, height,SWP_NOZORDER|SWP_NOMOVE);
RECT Rc;
GetClientRect(hwndList1, &Rc);
ListView_SetColumnWidth(hwndList1, 0, Rc.right - Rc.left);
}
else if ( wParam == SIZE_MAXIMIZED )
{
SetWindowPos(hwndList1, 0 , 20, 20, width, height,0);
RECT Rc;
GetClientRect(hwndList1, &Rc);
ListView_SetColumnWidth(hwndList1, 0, Rc.right - Rc.left);//
}
}
break;
case WM_NOTIFY:
if(((LPNMHDR)lParam)->code == NM_CUSTOMDRAW) {
LPNMLVCUSTOMDRAW lplvcd = (LPNMLVCUSTOMDRAW)lParam;
switch(lplvcd->nmcd.dwDrawStage) {
case CDDS_PREPAINT:
return CDRF_NOTIFYITEMDRAW;
case CDDS_ITEMPREPAINT:
if (((int)lplvcd->nmcd.dwItemSpec%2)==0) {
lplvcd->clrText = RGB(0,0,0);
lplvcd->clrTextBk = RGB(255, 255, 255);
} else {
lplvcd->clrText = RGB(0,0,0);
lplvcd->clrTextBk = RGB(255,255,255);
}
return CDRF_NEWFONT;
}
}
return TRUE;
case WM_COMMAND:
switch(LOWORD(wParam)){
case ID_FILE_EXIT:
PostMessage(hwnd, WM_CLOSE , 0 , 0);
break;
case ID_ABOUT:
{
int ret=DialogBox( GetModuleHandle(NULL) , MAKEINTRESOURCE(ID_ABOUT) , hwnd , AboutDlgProc );
}
break;
}
break;
case WM_CLOSE:
DestroyWindow( hwnd );
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc( hwnd , msg , wParam , lParam );
}
return 0;
}
What am i missing? Is there another way to do it?
Thanks!
Use GetClientRect to find the inner rectangle for the control. Example:
case WM_SIZE:
{
//resize the listview control first
//calculate width/height
SetWindowPos(hwndList1, NULL, 0, 0, width, height, SWP_NOZORDER|SWP_NOMOVE);
RECT rc;
GetClientRect(hwndList1, &rc);
ListView_SetColumnWidth(hwndList1, 0, rc.right - rc.left);//rc.left is zero
break;
}
The client rectangle can be a bit smaller than the window rectangle.
You can use width/height to set the size of listview control using SetWindowPos or MoveWindow. This will correspond to GetWindowRect. But you want the client rectangle for column width.
You can also subclass the listview control and respond to WM_SIZE in listview_proc.
WM_SIZE is sent when the user resizes the main window. It is not triggered by default when the window is first opened. You may have to call ListView_SetColumnWidth when the window is first initialized.
Also note, you cannot handle custom draw inside ListProc. You must remove WM_NOTIFY section from ListProc, add it to WndProc only.
Suggested edit:
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_SIZE:
{
int nWidth = LOWORD(lParam);
int nHeight = HIWORD(lParam);
int width = (int)((nWidth / 2) * 0.8);
int height = (int)((nHeight / 2) * 0.7);
SetWindowPos(hwndList1, 0, 20, 20, width, height, SWP_NOZORDER);
RECT rc;
GetClientRect(hwndList1, &rc);
ListView_SetColumnWidth(hwndList1, 0, rc.right - rc.left);
}
break;
case WM_NOTIFY:
if(((LPNMHDR)lParam)->code == NM_CUSTOMDRAW)
{
LPNMLVCUSTOMDRAW lplvcd = (LPNMLVCUSTOMDRAW)lParam;
switch(lplvcd->nmcd.dwDrawStage)
{
case CDDS_PREPAINT:
return CDRF_NOTIFYITEMDRAW;
case CDDS_ITEMPREPAINT:
lplvcd->clrText = RGB(255, 0, 0);
lplvcd->clrTextBk = RGB(255, 255, 0);
return CDRF_NEWFONT;
}
}
break;
...
}

How to properly handle a win32 GUI message loop?

I'm making a simple text editor win32 application for fun. I'm having a peculiar problem with my program. It seems that my program is not returning zero when it exits. Instead, it is returning 1385929. When my main GUI window is destroyed, I use PostQuitMessage( 0 ), but it seems that is not what is being returned in my main function's message.wParam. Here is my code thus far,
#define WIDTH 500
#define HEIGHT 400
#define EDIT_ID 10
LRESULT CALLBACK windowProc( HWND window, UINT message, WPARAM wParam, LPARAM lParam )
{
static HDC deviceContext = INVALID_HANDLE_VALUE;
static HWND editControl = INVALID_HANDLE_VALUE;
switch ( message )
{
case WM_CREATE :
deviceContext = GetDC( window );
if ( !deviceContext )
{
showWindowsError( "Creating Device Context", FALSE );
DestroyWindow( window );
}
editControl = CreateWindow(
"EDIT",
NULL,
WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_LEFT |
ES_MULTILINE | ES_AUTOVSCROLL | ES_NOHIDESEL,
0,
0,
0,
0,
window,
( HMENU )EDIT_ID,
( HINSTANCE )GetWindowLong( window, GWL_HINSTANCE ),
NULL
);
if ( !editControl )
{
showWindowsError( "Creating Edit Control", TRUE );
DestroyWindow( window );
}
return 0;
break;
case WM_COMMAND :
switch ( wParam )
{
case WM_UNDO :
SendMessage( editControl, WM_UNDO, 0, 0 );
break;
case WM_CUT :
SendMessage( editControl, WM_CUT, 0, 0 );
break;
case WM_COPY :
SendMessage( editControl, WM_COPY, 0, 0 );
break;
case WM_PASTE :
SendMessage( editControl, WM_PASTE, 0, 0 );
break;
case WM_CLEAR :
SendMessage( editControl, WM_CLEAR, 0, 0 );
break;
default:
return DefWindowProc( window, message, wParam, lParam );
}
case WM_SIZE :
MoveWindow( editControl, 0, 0, LOWORD( lParam ), HIWORD( lParam ), TRUE );
return 0;
break;
case WM_DESTROY :
ReleaseDC( window, deviceContext );
DestroyWindow( editControl );
PostQuitMessage( 0 );
return 0;
break;
}
return DefWindowProc( window, message, wParam, lParam );
}
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR CmdArgs, int nCmdShow )
{
WNDCLASSEX windowClass = { 0 };
HWND window = INVALID_HANDLE_VALUE;
MSG message = { 0 };
HBRUSH windowColor = CreateSolidBrush( GetSysColor( COLOR_WINDOW ) );
windowClass.cbSize = sizeof( windowClass );
windowClass.style = CS_HREDRAW | CS_VREDRAW;
windowClass.lpfnWndProc = windowProc;
windowClass.hInstance = hInstance;
windowClass.hCursor = LoadCursor( NULL, IDC_ARROW );
windowClass.hIcon = LoadIcon( NULL, IDI_APPLICATION );
windowClass.hbrBackground = windowColor;
windowClass.lpszClassName = "TextEditorWindow";
if ( !RegisterClassEx( &windowClass ) )
{
DeleteObject( windowColor );
showWindowsError( "Registering Windows Class", TRUE );
}
window = CreateWindow(
"TextEditorWindow",
"Text Editor",
WS_OVERLAPPED | WS_SYSMENU | WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,
CW_USEDEFAULT,
CW_USEDEFAULT,
WIDTH,
HEIGHT,
NULL,
NULL,
hInstance,
NULL
);
if ( !window )
{
DeleteObject( windowColor );
showWindowsError( "Creating GUI", TRUE );
}
ShowWindow( window, SW_SHOW );
UpdateWindow( window );
do
{
TranslateMessage( &message );
DispatchMessage( &message );
} while ( GetMessage( &message, window, 0, 0 ) > 0 );
DeleteObject( windowColor );
return message.wParam;
}
Your call to GetMessage() has the 2nd parameter set to your window's HWND, which limits the messages received only to those sent to that window - see the documentation of the API function.
WM_QUIT is, on the other hand, a message sent to the thread running the message pump, without any particular window. Because of that filter, you don't receive it and message.wParam is never set to that 0.
But why does the loop end and the program shuts down anyway? Because that HWND in window gets invalid after the window is closed, therefore GetMessage() ends with an error and returns -1.
Additionally, you are currently calling TranslateMessage() and DispatchMessage() with message before it is filled with any correct data, on the first iteration. The loop should rather be like this:
while ( GetMessage( &message, NULL, 0, 0 ) > 0 )
{
TranslateMessage( &message );
DispatchMessage( &message );
}

Win32 Toolbar and TBSTYLE_WRAPABLE

I have a toolbar which doesn't wrap on WM_SIZE message to its parent. Here is create code for toolbar:
// create toolbar
g_hTool = CreateWindowEx( 0, TOOLBARCLASSNAME, NULL,
WS_CHILD | WS_VISIBLE | TBSTYLE_WRAPABLE, 0, 0, 0, 0,
hWnd, ( HMENU )IDC_TEX_TOOL, GetModuleHandle( NULL ), NULL );
// Send the TB_BUTTONSTRUCTSIZE message, which is required for backward compatibility.
SendMessage( g_hTool, TB_BUTTONSTRUCTSIZE, ( WPARAM )sizeof( TBBUTTON ), 0 );
SendMessage( g_hTool, TB_SETBUTTONSIZE, 0, MAKELPARAM( 32, 32 ) );
SendMessage( g_hTool, TB_SETBITMAPSIZE, 0, MAKELPARAM( 32, 32 ) );
SendMessage( g_hTool, TB_SETEXTENDEDSTYLE, 0, ( LPARAM )TBSTYLE_EX_DRAWDDARROWS );
here is WM_SIZE( handled by parent window ):
switch( uMsg ):
{
...
case WM_SIZE:
{
// get auxiliary viewport toolbar window handle and autosize
SendMessage( g_hTool, TB_AUTOSIZE, 0, 0 );
RECT cr;
GetWindowRect( g_hTool, &cr );
nTexToolHeight = cr.bottom - cr.top;
// get status bar window handle and autosize
SendMessage( g_hStatus, WM_SIZE, 0, 0 );
// get status bar height
GetWindowRect( g_hStatus, &cr );
nTexStatusHeight = cr.bottom - cr.top;
break;
}
...
}
any idea why the toolbar won't wrap? The buttons extend beyond the extents of the parent window. The buttons that extend beyond the window are clipped but the toolbar isn't resized and the buttons remain unseen...

Transparent STATIC text changing

I have this STATIC control displayed over a window that has an image as its background. When I initiate the control it displays a text. If I want to change the text inside a WM_TIMER message it is displayed over the initial text (it is not removed) I have tried UpdateWindow and InvalidateRect but id doesn't work.
This is my code
WM_CREATE:
HWND control = CreateWindowEx(
WS_EX_TRANSPARENT,
L"STATIC",
L"FirstText",
WS_CHILD|WS_VISIBLE|ES_LEFT,
0,
0,
200,
20,
hWnd,
HMENU(LABEL1),
Instance,
NULL
);
break;
case WM_TIMER:
SetWindowText(GetDlgItem(hWnd, LABEL1), L"SecondText");
KillTimer(hWnd, MYTIMER);
// Here I tried UpdateWindow and InvalidateRect but no result
break;
So, the second text is drawn over the first one. It looks like the STATIC content is not updated after changing it. What could be going wrong? Thanks!
I hard coded some numbers in there for testing (like the window ID) - but it's just an example to show you.
case WM_CREATE:
control = CreateWindowEx(
WS_EX_TRANSPARENT,
L"STATIC",
L"FirstText",
WS_CHILD|WS_VISIBLE|ES_LEFT,
0,
0,
200,
20,
hWnd,
HMENU(99),
hInst,
NULL
);
//Create a timer
SetTimer(hWnd,23, 5000,NULL);
break;
case WM_CTLCOLORSTATIC:
if ( GetDlgItem(hWnd, 99) == (HWND)lParam)
{
SetBkMode( (HDC)wParam, TRANSPARENT);
return (LRESULT) GetStockObject(HOLLOW_BRUSH);
}
break;
case WM_TIMER:
{
SetWindowText(GetDlgItem(hWnd, 99), L"Second Text");
KillTimer(hWnd, 23);
RECT rect = {0,0, 200,20};
InvalidateRect(hWnd, &rect, TRUE);
UpdateWindow(hWnd);
}
break;
case WM_PAINT:
{
hdc = BeginPaint(hWnd, &ps);
//Load my test bitmap from resources
HBITMAP hb = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP1));
BITMAP bm;
GetObject(hb, sizeof(BITMAP), &bm);
HDC memDC= CreateCompatibleDC(hdc);
SelectObject(memDC,hb);
// ========================
//Put the bitmap on the main window to act as a backdrop
BitBlt(hdc, 0,0, bm.bmWidth,bm.bmHeight,memDC,0,0, SRCCOPY);
DeleteDC(memDC);
EndPaint(hWnd, &ps);
}
break;

RichEdit Vertical Text Aligment

How can i align the text in a TRichEdit vertically centered. There is a property for aligning paragaph by horizontal but there isn't any property for vertical alignment. I use C++ builder.
TRichEdit is just a wrapper around MS RichEdit component, so you can get a handle and use WinAPI to work with it directly: http://msdn.microsoft.com/en-us/library/bb787873(VS.85).aspx
The Richedit interface does not have a method for this. However it is fairly straightforward to do it using existing messages. Use EM_POSFROMCHAR to get the vertical position of the first and last character in the control, adding the height of the last character. Then calculate the offset from the top of the control needed to centre the text vertically. Then use an EM_SETRECT message to adjust the position that the txt is drawn in the control.
If you want to do this dynamically as the text is modified you will need to subclass the control and handle the appropriate messages. See the code snippets below.
// in WM_CREATE of parent window or when control is created
hwndTextbox = CreateWindowEx (0, MSFTEDIT_CLASS, (WCHAR*)yourtext,
ES_MULTILINE | WS_VISIBLE | WS_SIZEBOX | WS_CHILD | WS_CLIPSIBLINGS,
left, top, width, height,
hwnd, NULL, hInst, NULL);
SetProp (hwndTextbox, L"oldproc", (HANDLE)(ULONG_PTR)GetWindowLong (hwndTextbox, GWL_WNDPROC));
SetWindowLong (hwndTextbox, GWL_WNDPROC, (DWORD)(LRESULT)RicheditWndProc);
// after control has been drawn eg in WM_SIZE of parent window
{
RECT rclBox;
SendMessage(hwndTextbox, EM_GETRECT, 0, (LPARAM)&rclBox);
rclBox.top = vertoffset; // previously saved vertical offset for control either globally or as window property of control
SendMessage(hwndTextbox, EM_SETRECT, 0, (LPARAM)&rclBox);
InvalidateRect(hwndTextbox, NULL, TRUE);
}
LRESULT WINAPI RicheditWndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
static BOOL fVcentre; // will need to be stored as a window property if more than one control
WNDPROC lpOldProc;
CHARFORMAT cf;
lpOldProc = (WNDPROC)GetProp( hwnd, L"oldproc" );
switch( msg )
{
case WM_TIMER:
// use timer to ensure text is redrawn before positioning
switch(wParam)
{
case 1:
KillTimer(hwnd, 1);
{
POINTL pt, pt1;
RECT rcl;
RECT rclParent;
int last, height=0, offset, current;
SendMessage (hwnd, EM_GETSEL, (WPARAM)&current, 0);
SendMessage(hwnd, EM_GETRECT, 0, (LPARAM)&rcl);
GetClientRect(GetParent(hwnd), &rclParent);
SendMessage (hwnd, EM_SETSEL, 0, -1);
SendMessage (hwnd, EM_GETSEL, 0, (LPARAM)&last);
SendMessage(hwnd, EM_POSFROMCHAR, (WPARAM)&pt, 0);
SendMessage(hwnd, EM_POSFROMCHAR, (WPARAM)&pt1, last);
// get height of last character
SendMessage (hwnd, EM_SETSEL, last-1, last);
memset(&cf, 0, sizeof cf);
cf.cbSize = sizeof cf;
SendMessage (hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
if(cf.dwMask & CFM_SIZE)
height = cf.yHeight/15;
height = height + pt1.y - pt.y;
vertoffset = (rcl.bottom - height)/2;
ifvertoffset > 0) // vertoffset can be < 0 if needed
{
rcl.top = vertoffset;
SendMessage(hwnd, EM_SETRECT, 0, (LPARAM)&rcl);
InvalidateRect(hwnd, NULL, TRUE);
}
SendMessage (hwnd, EM_SETSEL, current, current);
}
break;
}
break;
case WM_DESTROY: // Put back old window proc and
SetWindowLong( hwnd, GWL_WNDPROC, (DWORD)(LRESULT)lpOldProc );
RemoveProp( hwnd, L"oldproc" ); // remove window property
break;
case WM_KEYUP:
case WM_CHAR:
case WM_PASTE:
case WM_CUT:
case EM_UNDO:
case EM_REDO:
// any message that modifies the text
if(fVcentre)
SetTimer(hwnd, 1, 100, NULL);
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case ID_TFVCENTRE: // id to centre text
{
POINTL pt, pt1;
RECT rcl;
RECT rclParent;
int last, height=0, offset, current;
fVcentre = !fVcentre;
SendMessage(hwnd, EM_GETRECT, 0, (LPARAM)&rcl);
if(fVcentre)
{
GetClientRect(GetParent(hwnd), &rclParent);
SendMessage (hwnd, EM_GETSEL, (WPARAM)&current, 0);
SendMessage (hwnd, EM_SETSEL, 0, -1);
SendMessage (hwnd, EM_GETSEL, 0, (LPARAM)&last);
SendMessage(hwnd, EM_POSFROMCHAR, (WPARAM)&pt, 0);
SendMessage(hwnd, EM_POSFROMCHAR, (WPARAM)&pt1, last);
// get height of last character
SendMessage (hwnd, EM_SETSEL, last-1, last);
memset(&cf, 0, sizeof cf);
cf.cbSize = sizeof cf;
SendMessage (hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
if(cf.dwMask & CFM_SIZE)
height = cf.yHeight/15;
height = height + pt1.y - pt.y;
vertoffset = (rcl.bottom - height)/2;
ifvertoffset > 0)
{
rcl.top = vertoffset
SendMessage(hwnd, EM_SETRECT, 0, (LPARAM)&rcl);
InvalidateRect(hwnd, NULL, TRUE);
}
SendMessage (hwnd, EM_SETSEL, current, current);
}
else
{
rcl.top = 0;
vertoffset = 0;
SendMessage(hwnd, EM_SETRECT, 0, (LPARAM)&rcl);
InvalidateRect(hwnd, NULL, TRUE);
}
}
break;
}
break;
} // Pass all non-custom messages to old window proc
return( CallWindowProc( lpOldProc, hwnd, msg, wParam, lParam ) );
}