gusucode.com > VC++模仿QQ界面风格源码程序 > VC++模仿QQ界面风格/ResizeCtrl.cpp
// ResizeCtrl.cpp: implementation of the CResizeCtrl class. // // // Written by Herbert Menke (h.menke@gmx.de) // Copyright (c) 2000. // // This code may be used in compiled form in any way you desire. This // file may be redistributed unmodified by any means PROVIDING it is // not sold for profit without the authors written consent, and // providing that this notice and the authors name is included. If // the source code in this file is used in any commercial application // then acknowledgement must be made to the author of this file // (in whatever form you wish). // // This file is provided "as is" with no expressed or implied warranty. // // Expect bugs. // // Please use and enjoy. Please let me know of any bugs/mods/improvements // that you have found/implemented and I will fix/incorporate them into this // file. ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "ResizeCtrl.h" #ifndef __AFXTEMPL_H__ #include <afxtempl.H> #endif #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif // Specification struct struct RSRect { int left; // Specifies the percentage change in the position of the left edge // of the object relative to the total change in the parent form抯 width. int top; // Specifies the percentage change in the position of the top // of the object relative to the total change in the parent form抯 height. int width; // Specifies the percentage change in the width of the object // relative to the total change in the parent form抯 width. int height; // Specifies the percentage change in the height of the object // relative to the total change in the parent form抯 height. }; struct CRPItemState { HWND handle; // Handle of Control RSRect pending; // pending Resize pixels RSRect part; // resize specifications }; class CResizeArray : public CArray<CRPItemState,CRPItemState> { }; static const char m_szResizeProperty[] = "___CResizeCtrl___Class___"; ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CResizeCtrl::CResizeCtrl( ) : m_array ( NULL ) , m_hWndParent ( NULL ) , m_maxPart ( 100 ) , m_inResize ( FALSE ) , m_prevWndProc ( NULL ) , m_enabled ( FALSE ) , m_minTracking ( CSize( -1, -1 ) ) , m_maxTracking ( CSize( -1, -1 ) ) , m_hitCode ( 0 ) , m_inMouseMove ( FALSE ) , m_delta ( CSize( 0, 0 ) ) , m_windowRect ( CRect( 0, 0, 0, 0 ) ) , m_gripEnabled ( FALSE ) { } CResizeCtrl::CResizeCtrl( HWND hWndParent, BOOL enable, int maxPart ) : m_array( NULL ) , m_hWndParent ( NULL ) , m_maxPart ( 100 ) , m_inResize ( FALSE ) , m_prevWndProc ( NULL ) , m_enabled ( FALSE ) , m_minTracking ( CSize( -1, -1 ) ) , m_maxTracking ( CSize( -1, -1 ) ) , m_hitCode ( 0 ) , m_inMouseMove ( FALSE ) , m_delta ( CSize( 0, 0 ) ) , m_gripEnabled ( FALSE ) { Create( hWndParent, enable, maxPart ); } CResizeCtrl::CResizeCtrl( CWnd * wndParent, BOOL enable, int maxPart ) : m_array ( NULL ) , m_hWndParent ( NULL ) , m_maxPart ( 100 ) , m_inResize ( FALSE ) , m_prevWndProc ( NULL ) , m_enabled ( FALSE ) , m_minTracking ( CSize( -1, -1 ) ) , m_maxTracking ( CSize( -1, -1 ) ) , m_hitCode ( 0 ) , m_inMouseMove ( FALSE ) , m_delta ( CSize( 0, 0 ) ) , m_gripEnabled ( FALSE ) { Create( wndParent, enable, maxPart ); } CResizeCtrl::~CResizeCtrl() { SetEnabled( FALSE ); m_array->RemoveAll(); delete m_array; } BOOL CResizeCtrl::Create( CWnd * wndParent, BOOL enable, int maxPart ) { ASSERT( wndParent ); if( wndParent ) return Create( wndParent->GetSafeHwnd(), enable, maxPart ); return FALSE; } BOOL CResizeCtrl::Create( HWND hWndParent, BOOL enable, int maxPart ) { ASSERT( !m_array ); if( m_array ) return FALSE; m_array = new CResizeArray; ASSERT( m_array ); ASSERT( hWndParent ); ASSERT( maxPart > 0 ); m_hWndParent = hWndParent; m_maxPart = maxPart; m_inResize = FALSE; m_prevWndProc = NULL; m_enabled = FALSE; m_minTracking.cx = m_minTracking.cy = m_maxTracking.cx = m_maxTracking.cy = -1; m_hasResizingBorder = (::GetWindowLong( hWndParent, GWL_STYLE ) & WS_THICKFRAME ) == WS_THICKFRAME; if( enable ) SetEnabled( TRUE ); else ::GetWindowRect( hWndParent , &m_windowRect ); return TRUE; } // // Enabled Property // BOOL CResizeCtrl::SetEnabled( BOOL enable ) { ASSERT ( m_array ); if( m_enabled != enable ) { ASSERT( m_hWndParent ); ::GetWindowRect( m_hWndParent, &m_windowRect ); // remove subclassing if( FALSE == enable ) { ASSERT( m_prevWndProc ); ::SetWindowLong( m_hWndParent, GWL_WNDPROC, reinterpret_cast<LONG>( m_prevWndProc ) ); m_prevWndProc = NULL; ::RemoveProp( m_hWndParent, m_szResizeProperty ); if( m_hasResizingBorder == FALSE ) ChangeStyle( enable ); } else { m_hitCode = 0; m_delta.cx = m_delta.cy = 0; m_inResize = m_inMouseMove = FALSE; //m_hWndFocus = NULL; WNDPROC wndProc = CResizeCtrl::WndProc; if( m_hasResizingBorder == FALSE ) { ChangeStyle( enable ); } CRect rect; ::GetClientRect( m_hWndParent, &rect ); m_size.cx = rect.Width(); m_size.cy = rect.Height(); ::SetProp( m_hWndParent, m_szResizeProperty, reinterpret_cast<HANDLE>(this) ); m_prevWndProc = reinterpret_cast<WNDPROC>( ::GetWindowLong( m_hWndParent, GWL_WNDPROC ) ); ::SetWindowLong( m_hWndParent, GWL_WNDPROC, reinterpret_cast<LONG>( wndProc ) ); } m_enabled = enable; if( m_gripEnabled ) { GetGripRect( m_gripRect, TRUE ); } return TRUE; } return FALSE; } BOOL CResizeCtrl::GetEnabled() const { return m_enabled; } // // GripEnabled Property // BOOL CResizeCtrl::SetGripEnabled( BOOL showGrip ) { if( m_gripEnabled != showGrip ) { m_gripEnabled = showGrip; if( m_enabled ) GetGripRect( m_gripRect, TRUE ); } return FALSE; } BOOL CResizeCtrl::GetGripEnabled() const { return m_gripEnabled; } // resizeInfo is a null terminated array of CResizeInfo BOOL CResizeCtrl::Add( const CResizeInfo * resizeInfo ) { ASSERT ( m_array ); BOOL result = TRUE; while( result == TRUE && resizeInfo->ctlID > 0 ) { result &= Add( resizeInfo->ctlID, resizeInfo->left, resizeInfo->top, resizeInfo->width, resizeInfo->height ); resizeInfo++; } return result; } BOOL CResizeCtrl::Add( int ctlID, int left, int top, int width, int height ) { ASSERT ( m_array ); return Add( ::GetDlgItem( m_hWndParent, ctlID), left, top, width, height ); } BOOL CResizeCtrl::Add( CWnd * wndCtl, int left, int top, int width, int height ) { ASSERT ( m_array ); if( wndCtl ) return Add( wndCtl->GetSafeHwnd(), left, top, width, height ); return FALSE; } BOOL CResizeCtrl::Add(HWND hWndCtl, int left, int top, int width, int height) { ASSERT ( m_array ); if( left < 0 || left > m_maxPart ) { return FALSE; } if( top < 0 || top > m_maxPart ) { return FALSE; } if( width < 0 || width > m_maxPart ) { return FALSE; } if( height < 0 || height > m_maxPart ) { return FALSE; } if( ( left + width ) > m_maxPart ) { return FALSE; } if( ( top + height) > m_maxPart ) { return FALSE; } if( !::IsWindow( hWndCtl)) return FALSE; CRPItemState item; item.part.left = left; item.part.top = top; item.part.width = width; item.part.height = height; item.pending.left = item.pending.top = item.pending.width = item.pending.height = 0; item.handle = hWndCtl; return m_array->Add( item ) >= 0 ; } BOOL CResizeCtrl::Remove( int ctlID ) { ASSERT ( m_array ); return Remove( ::GetDlgItem( m_hWndParent, ctlID ) ); } BOOL CResizeCtrl::Remove( CWnd * wndCtl ) { ASSERT ( m_array ); if( wndCtl ) return Remove( wndCtl->GetSafeHwnd () ); return FALSE; } BOOL CResizeCtrl::Remove(HWND hWndCtl) { ASSERT ( m_array ); if( !::IsWindow( hWndCtl)) return FALSE; int upperBound = m_array->GetUpperBound (); for( int current = 0; current <= upperBound; current++ ) { if( m_array->GetAt( current ).handle == hWndCtl ) { m_array->RemoveAt( current ); return TRUE; } } return FALSE; } BOOL CResizeCtrl::GetWindowRect( RECT * rect ) { if( rect ) { *rect = m_windowRect; return TRUE; } else return FALSE; } BOOL CResizeCtrl::CalcValue(int delta, int part, int & pending, long &position, BOOL isSize) { if( part > 0 ) { int toAdd = ( delta * part ) + pending; if( toAdd != 0 ) { position += ( toAdd / m_maxPart ); pending = toAdd % m_maxPart ; // avoid negative width or height if( TRUE == isSize && position < 0 ) { pending += ( position * m_maxPart ); position = 0; } return TRUE; } } return FALSE; } void CResizeCtrl::Resize(int cx, int cy) { ASSERT ( m_array ); if( FALSE == m_inResize ) { m_inResize = TRUE; int upperBound = m_array->GetUpperBound(); if( upperBound >= 0 ) { int deltaX = cx - m_size.cx; int deltaY = cy - m_size.cy; if( deltaX != 0 || deltaY != 0 ) { CRPItemState * items = m_array->GetData(); HDWP hdwp = ::BeginDeferWindowPos( 0 ); for( int current = 0; current <= upperBound; current++, items++ ) { RECT rcItem; ::GetWindowRect( items->handle, & rcItem ); ::MapWindowPoints( HWND_DESKTOP, m_hWndParent, (LPPOINT)(RECT*)&rcItem, 2 ); rcItem.right -= rcItem.left; rcItem.bottom -= rcItem.top; BOOL changed = FALSE; changed |= CalcValue( deltaX, items->part.left, items->pending.left, rcItem.left, FALSE ); changed |= CalcValue( deltaX, items->part.width, items->pending.width, rcItem.right, TRUE ); changed |= CalcValue( deltaY, items->part.top, items->pending.top, rcItem.top, FALSE ); changed |= CalcValue( deltaY, items->part.height, items->pending.height, rcItem.bottom, TRUE ); if( changed ) { hdwp = ::DeferWindowPos( hdwp, items->handle, NULL, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom, SWP_NOZORDER ); } } ::EndDeferWindowPos( hdwp ); m_size.cx = cx; m_size.cy = cy; } } m_inResize = FALSE; } } LRESULT CALLBACK CResizeCtrl::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { LRESULT result = 0; CResizeCtrl * This = reinterpret_cast<CResizeCtrl *>(::GetProp( hWnd, m_szResizeProperty )); if( This ) { WNDPROC prevWndProc = This->m_prevWndProc; if( FALSE == This->ProcessMessage( msg, wParam, lParam, result ) ) result = ::CallWindowProc( prevWndProc, hWnd, msg, wParam, lParam ); } return result; } void CResizeCtrl::ChangeStyle(BOOL enable) { ASSERT( m_hWndParent ); ASSERT( m_hasResizingBorder == FALSE ); CRect rect; BOOL hasMenu = ::GetMenu( m_hWndParent ) != NULL; long style = ::GetWindowLong( m_hWndParent, GWL_STYLE ); ::GetWindowRect( m_hWndParent, &rect ); // retrieve client Rectangle RECT oldClientRect; ::GetClientRect( m_hWndParent, &oldClientRect ); RECT newClientRect = oldClientRect; // adjust rect with current style ::AdjustWindowRect( &oldClientRect, style, hasMenu ); if( enable ) { style |= WS_THICKFRAME; } else { style &= ~WS_THICKFRAME; } // adjust rect with new style ::AdjustWindowRect( &newClientRect, style, hasMenu ); // and adjust the windowrect, so that the clientrect remains equal rect.left += ( newClientRect.left - oldClientRect.left ); rect.right += ( newClientRect.right - oldClientRect.right ); rect.top += ( newClientRect.top - oldClientRect.top ); rect.bottom += ( newClientRect.bottom - oldClientRect.bottom ); ::SetWindowLong( m_hWndParent, GWL_STYLE, style ); ::SetWindowPos ( m_hWndParent, HWND_DESKTOP, rect.left, rect.top, rect.Width(), rect.Height(), SWP_NOZORDER | SWP_NOACTIVATE ); } BOOL CResizeCtrl::ProcessMessage(UINT message, WPARAM wParam, LPARAM lParam, LRESULT & result) { BOOL handled = FALSE; if( m_gripEnabled && WM_NCHITTEST == message ) { POINT pt = { (int)LOWORD(lParam), (int)HIWORD(lParam) }; ::ScreenToClient( m_hWndParent, &pt); if( ::PtInRect ( m_gripRect, pt ) ) { result = HTBOTTOMRIGHT; handled = TRUE; } } else if( m_gripEnabled && WM_PAINT == message ) { // First let the previous windowproc handle the WM_SIZE message result = ::CallWindowProc( m_prevWndProc, m_hWndParent, message, wParam, lParam ); HDC hDC = ::GetDC( m_hWndParent ); DrawFrameControl(hDC, &m_gripRect, DFC_SCROLL, DFCS_SCROLLSIZEGRIP); ::ReleaseDC( m_hWndParent, hDC ); // flag message handled handled = TRUE; } else if( WM_DESTROY == message ) { // Remove subclassing SetEnabled( FALSE ); } else if( WM_SIZE == message ) { // First let the previous windowproc handle the WM_SIZE message result = ::CallWindowProc( m_prevWndProc, m_hWndParent, message, wParam, lParam ); // resize all registered controls Resize( (int)LOWORD(lParam) , (int)HIWORD(lParam) ); if( m_gripEnabled ) { ::InvalidateRect( m_hWndParent, m_gripRect, TRUE ); GetGripRect( m_gripRect, TRUE ); } // flag message handled handled = TRUE; } else if( WM_GETMINMAXINFO == message ) { // First let the previous windowproc handle the WM_GETMINMAXINFO message result = ::CallWindowProc( m_prevWndProc, m_hWndParent, message, wParam, lParam ); MINMAXINFO * lpMMI = (MINMAXINFO * )lParam; if( m_minTracking.cx == -1 ) m_minTracking.cx = lpMMI->ptMinTrackSize.x; if( m_minTracking.cy == -1 ) m_minTracking.cy = lpMMI->ptMinTrackSize.y; if( m_maxTracking.cx == -1 ) m_maxTracking.cx = lpMMI->ptMaxTrackSize.x; if( m_maxTracking.cy == -1 ) m_maxTracking.cy = lpMMI->ptMaxTrackSize.y; lpMMI->ptMinTrackSize.x = m_minTracking.cx; lpMMI->ptMinTrackSize.y = m_minTracking.cy; lpMMI->ptMaxTrackSize.x = m_maxTracking.cx; lpMMI->ptMaxTrackSize.y = m_maxTracking.cy; // flag message handled handled = TRUE; } else { // if the original window had no resizing border // we must serve WM_MOUSE, WM_NCLBUTTONDOWN and // WM_LBUTTONUP to enable resizing if( !m_hasResizingBorder ) { POINT mousePostion; RECT currentRect; if( WM_MOUSEMOVE == message ) { if (m_hitCode && !m_inMouseMove ) { m_inMouseMove = TRUE; ::GetCursorPos( &mousePostion ); mousePostion.x += m_delta.cx; mousePostion.y += m_delta.cy; RECT m_previsionRect; ::GetWindowRect( m_hWndParent, ¤tRect ); m_previsionRect = currentRect; switch( m_hitCode ) { case HTTOPLEFT : currentRect.left = mousePostion.x; // fall through case HTTOP : currentRect.top = mousePostion.y; break; case HTBOTTOMRIGHT : currentRect.right = mousePostion.x; // fall through case HTBOTTOM : currentRect.bottom = mousePostion.y; break; case HTBOTTOMLEFT : currentRect.bottom = mousePostion.y; // fall through case HTLEFT : currentRect.left = mousePostion.x; break; case HTTOPRIGHT : currentRect.top = mousePostion.y; // fall through case HTRIGHT : currentRect.right = mousePostion.x; break; } if (!::EqualRect( ¤tRect, &m_previsionRect )) { int width = currentRect.right - currentRect.left; int height = currentRect.bottom - currentRect.top; ::SetWindowPos( m_hWndParent, HWND_DESKTOP, currentRect.left, currentRect.top, width, height, SWP_NOZORDER | SWP_NOACTIVATE ); } m_inMouseMove = FALSE; } } else if( WM_NCLBUTTONDOWN == message ) { ::GetCursorPos( &mousePostion ); ::GetWindowRect( m_hWndParent, ¤tRect ); m_hitCode = wParam; m_delta.cx = m_delta.cy = 0; switch( m_hitCode ) { case HTTOPLEFT : m_delta.cx = currentRect.left - mousePostion.x; // fall through case HTTOP : m_delta.cy = currentRect.top - mousePostion.y; break; case HTBOTTOMRIGHT : m_delta.cx = currentRect.right - mousePostion.x; // fall through case HTBOTTOM : m_delta.cy = currentRect.bottom - mousePostion.y; break; case HTBOTTOMLEFT : m_delta.cy = currentRect.bottom - mousePostion.y; // fall through case HTLEFT : m_delta.cx = currentRect.left - mousePostion.x; break; case HTTOPRIGHT : m_delta.cy = currentRect.top - mousePostion.y; // fall through case HTRIGHT : m_delta.cx = currentRect.right - mousePostion.x; break; default : m_hitCode = 0; break; } if (m_hitCode) { ::SetCapture( m_hWndParent ); } } else if( WM_LBUTTONUP == message ) { if (m_hitCode != 0) { ::ReleaseCapture(); m_hitCode = 0; } } } } return handled; } /////////////////////////////////////////////////////////////////////// // MinMaxInfo Support BOOL CResizeCtrl::SetMinimumTrackingSize( const CSize & size ) { m_minTracking = size; return TRUE; } BOOL CResizeCtrl::SetMinimumTrackingSize() { RECT rect; ::GetWindowRect( m_hWndParent, &rect ); return SetMinimumTrackingSize( CSize( rect.right - rect.left, rect.bottom - rect.top ) ); } CSize CResizeCtrl::GetMinimumTrackingSize( ) { return m_minTracking; } BOOL CResizeCtrl::SetMaximumTrackingSize( const CSize & size ) { m_maxTracking = size; return TRUE; } CSize CResizeCtrl::GetMaximumTrackingSize( ) { return m_maxTracking; } void CResizeCtrl::GetGripRect(RECT & rect, BOOL redraw) { GetClientRect( m_hWndParent, &rect ); rect.left = rect.right - ::GetSystemMetrics(SM_CXVSCROLL) ; rect.top = rect.bottom - ::GetSystemMetrics(SM_CYHSCROLL) ; if( redraw ) ::InvalidateRect( m_hWndParent, &rect, TRUE ); }