//--------------------------------------------------------------------------- // DrawHelp.cpp - flat drawing helper routines //--------------------------------------------------------------------------- #include "stdafx.h" #include "DrawHelp.h" #include "rgn.h" #define cxRESIZE (ClassicGetSystemMetrics(SM_CXEDGE)+ClassicGetSystemMetrics( SM_CXSIZEFRAME )) #define cyRESIZE (ClassicGetSystemMetrics(SM_CYEDGE)+ClassicGetSystemMetrics( SM_CYSIZEFRAME )) #define cxRESIZEPAD ClassicGetSystemMetrics(SM_CXVSCROLL) #define cyRESIZEPAD ClassicGetSystemMetrics(SM_CYHSCROLL) //--------------------------------------------------------------------------- typedef WORD (* HITTESTRECTPROC)(LPCRECT, int, int, const POINT&, WORD); WORD _HitTestRectCorner( HITTESTRECTPROC, HITTESTRECTPROC, LPCRECT, int, int, int, int, const POINT&, WORD, WORD, WORD, WORD ); //--------------------------------------------------------------------------- WORD _HitTestRectLeft( LPCRECT prc, int cxMargin, int cyMargin, const POINT& pt, WORD wMiss ) { return ((WORD)((pt.x <= (prc->left + cxMargin)) ? HTLEFT : wMiss)); } //--------------------------------------------------------------------------- WORD _HitTestRectTop( LPCRECT prc, int cxMargin, int cyMargin, const POINT& pt, WORD wMiss ) { return ((WORD)((pt.y <= (prc->top + cyMargin)) ? HTTOP : wMiss)); } //--------------------------------------------------------------------------- WORD _HitTestRectRight( LPCRECT prc, int cxMargin, int cyMargin, const POINT& pt, WORD wMiss ) { return ((WORD)((pt.x >= (prc->right - cxMargin)) ? HTRIGHT : wMiss)); } //--------------------------------------------------------------------------- WORD _HitTestRectBottom( LPCRECT prc, int cxMargin, int cyMargin, const POINT& pt, WORD wMiss ) { return ((WORD)((pt.y >= (prc->bottom - cyMargin)) ? HTBOTTOM : wMiss)); } //--------------------------------------------------------------------------- WORD _HitTestRectTopLeft( LPCRECT prc, int cxMargin, int cyMargin, const POINT& pt, WORD wMiss ) { return _HitTestRectCorner( _HitTestRectLeft, _HitTestRectTop, prc, cxMargin, cyMargin, cxRESIZEPAD, cyRESIZEPAD, pt, HTTOPLEFT, HTLEFT, HTTOP, wMiss ); } //--------------------------------------------------------------------------- WORD _HitTestRectTopRight( LPCRECT prc, int cxMargin, int cyMargin, const POINT& pt, WORD wMiss ) { return _HitTestRectCorner( _HitTestRectRight, _HitTestRectTop, prc, cxMargin, cyMargin, cxRESIZEPAD, cyRESIZEPAD, pt, HTTOPRIGHT, HTRIGHT, HTTOP, wMiss ); } //--------------------------------------------------------------------------- WORD _HitTestRectBottomLeft( LPCRECT prc, int cxMargin, int cyMargin, const POINT& pt, WORD wMiss ) { return _HitTestRectCorner( _HitTestRectLeft, _HitTestRectBottom, prc, cxMargin, cyMargin, cxRESIZEPAD, cyRESIZEPAD, pt, HTBOTTOMLEFT, HTLEFT, HTBOTTOM, wMiss ); } //--------------------------------------------------------------------------- WORD _HitTestRectBottomRight( LPCRECT prc, int cxMargin, int cyMargin, const POINT& pt, WORD wMiss ) { return _HitTestRectCorner( _HitTestRectRight, _HitTestRectBottom, prc, cxMargin, cyMargin, cxRESIZEPAD, cyRESIZEPAD, pt, HTBOTTOMRIGHT, HTRIGHT, HTBOTTOM, wMiss ); } //--------------------------------------------------------------------------- WORD _HitTestRectCorner( HITTESTRECTPROC pfnX, HITTESTRECTPROC pfnY, LPCRECT prc, // target rect int cxMargin, int cyMargin, // width, height of resizing borders int cxMargin2, int cyMargin2, // width, height of scrollbars const POINT& pt, // test point WORD wHitC, WORD wHitX, WORD wHitY, // winning hittest codes WORD wMiss ) // losing hittest code { WORD wRetX = pfnX( prc, cxMargin, cyMargin, pt, wMiss ); WORD wRetY = pfnY( prc, cxMargin, cyMargin, pt, wMiss ); if( wMiss != wRetX && wMiss != wRetY ) return wHitC; if( wMiss != wRetX ) { wMiss = wHitX; if( wMiss != pfnY( prc, cxMargin2, cyMargin2, pt, wMiss ) ) return wHitC; } else if( wMiss != wRetY ) { wMiss = wHitY; if( wMiss != pfnX( prc, cxMargin2, cyMargin2, pt, wMiss ) ) return wHitC; } return wMiss; } //--------------------------------------------------------------------------- WORD HitTest9Grid( LPCRECT prc, const MARGINS& margins, const POINT& pt ) { ASSERT(PtInRect(prc,pt)); WORD wHit = HTCLIENT; // test left side if( HTLEFT == _HitTestRectLeft( prc, margins.cxLeftWidth, 0, pt, wHit ) ) { if( HTTOP == _HitTestRectTop( prc, 0, margins.cyTopHeight, pt, wHit ) ) return HTTOPLEFT; if( HTBOTTOM == _HitTestRectBottom( prc, 0, margins.cyBottomHeight, pt, wHit ) ) return HTBOTTOMLEFT; wHit = HTLEFT; } else // test right side if( HTRIGHT == _HitTestRectRight( prc, margins.cxRightWidth, 0, pt, wHit ) ) { if( HTTOP == _HitTestRectTop( prc, 0, margins.cyTopHeight, pt, wHit ) ) return HTTOPRIGHT; if( HTBOTTOM == _HitTestRectBottom( prc, 0, margins.cyBottomHeight, pt, wHit ) ) return HTBOTTOMRIGHT; wHit = HTRIGHT; } else // test top if( HTTOP == _HitTestRectTop( prc, 0, margins.cyTopHeight, pt, wHit ) ) { return HTTOP; } else // test bottom if( HTBOTTOM == _HitTestRectBottom( prc, 0, margins.cyBottomHeight, pt, wHit ) ) { return HTBOTTOM; } return wHit; } //--------------------------------------------------------------------------- WORD _HitTestResizingRect( DWORD dwHTFlags, LPCRECT prc, const POINT& pt, WORD w9GridHit, WORD wMiss ) { WORD wHit = wMiss; BOOL fTestLeft = TESTFLAG(dwHTFlags, HTTB_RESIZINGBORDER_LEFT); BOOL fTestTop = TESTFLAG(dwHTFlags, HTTB_RESIZINGBORDER_TOP); BOOL fTestRight = TESTFLAG(dwHTFlags, HTTB_RESIZINGBORDER_RIGHT); BOOL fTestBottom = TESTFLAG(dwHTFlags, HTTB_RESIZINGBORDER_BOTTOM); BOOL fTestCaption = TESTFLAG( dwHTFlags, HTTB_CAPTION ); switch( w9GridHit ) { case HTLEFT: if( fTestLeft ) { // first test for a hit in the corner resizing areas, respecting caller's option flags. if( (fTestTop && (wHit = _HitTestRectTopLeft( prc, cxRESIZE, cyRESIZE, pt, wMiss )) != HTLEFT) || (fTestBottom && (wHit = _HitTestRectBottomLeft( prc, cxRESIZE, cyRESIZE, pt, wMiss )) != HTLEFT) ) break; // failed corners, just test the resizing margin within the specified 9-grid hit seg. wHit = _HitTestRectLeft( prc, cxRESIZE, cyRESIZE, pt, wMiss ); } break; case HTTOP: if( fTestCaption ) wHit = wMiss = HTCAPTION; if( fTestTop ) { // first test for a hit in the corner resizing areas, respecting caller's option flags. if( (fTestLeft && (wHit = _HitTestRectTopLeft( prc, cxRESIZE, cyRESIZE, pt, wMiss )) != HTTOP) || (fTestRight && (wHit = _HitTestRectTopRight( prc, cxRESIZE, cyRESIZE, pt, wMiss )) != HTTOP) ) break; // failed corners, just test the resizing margin within the specified 9-grid hit seg. wHit = _HitTestRectTop( prc, cxRESIZE, cyRESIZE, pt, wMiss ); } break; case HTRIGHT: if( fTestRight ) { // first test for a hit in the corner resizing areas, respecting caller's option flags. if( (fTestTop && (wHit = _HitTestRectTopRight( prc, cxRESIZE, cyRESIZE, pt, wMiss )) != HTRIGHT) || (fTestBottom && (wHit = _HitTestRectBottomRight( prc, cxRESIZE, cyRESIZE, pt, wMiss )) != HTRIGHT) ) break; // failed corners, just test the resizing margin within the specified 9-grid hit seg. wHit = _HitTestRectRight( prc, cxRESIZE, cyRESIZE, pt, wMiss ); break; } case HTBOTTOM: if( fTestBottom ) { // first test for a hit in the corner resizing areas, respecting caller's option flags. if( (fTestLeft && (wHit = _HitTestRectBottomLeft( prc, cxRESIZE, cyRESIZE, pt, wMiss )) != HTBOTTOM) || (fTestRight && (wHit = _HitTestRectBottomRight( prc, cxRESIZE, cyRESIZE, pt, wMiss )) != HTBOTTOM) ) break; // failed corners, just test the resizing margin within the specified 9-grid hit seg. wHit = _HitTestRectBottom( prc, cxRESIZE, cyRESIZE, pt, wMiss ); } break; case HTTOPLEFT: if( fTestCaption ) wHit = wMiss = HTCAPTION; // first test for a resizing hit in the corner, and failing that, test the // resizing margin on either side. if( fTestTop && fTestLeft ) wHit = _HitTestRectTopLeft( prc, cxRESIZE, cyRESIZE, pt, wMiss ); else if( fTestLeft ) wHit = _HitTestRectLeft( prc, cxRESIZE, cyRESIZE, pt, wMiss ); else if( fTestTop ) wHit = _HitTestRectTop( prc, cxRESIZE, cyRESIZE, pt, wMiss ); break; case HTTOPRIGHT: if( fTestCaption ) wHit = wMiss = HTCAPTION; // first test for a resizing hit in the corner, and failing that, test the // resizing margin on either side. if( fTestTop && fTestRight ) wHit = _HitTestRectTopRight( prc, cxRESIZE, cyRESIZE, pt, wMiss ); else if( fTestRight ) wHit = _HitTestRectRight( prc, cxRESIZE, cyRESIZE, pt, wMiss ); else if( fTestTop ) wHit = _HitTestRectTop( prc, cxRESIZE, cyRESIZE, pt, wMiss ); break; case HTBOTTOMLEFT: // first test for a resizing hit in the corner, and failing that, test the // resizing margin on either side. if( fTestBottom && fTestLeft ) wHit = _HitTestRectBottomLeft( prc, cxRESIZE, cyRESIZE, pt, wMiss ); else if( fTestLeft ) wHit = _HitTestRectLeft( prc, cxRESIZE, cyRESIZE, pt, wMiss ); else if( fTestBottom ) wHit = _HitTestRectBottom( prc, cxRESIZE, cyRESIZE, pt, wMiss ); break; case HTBOTTOMRIGHT: // first test for a resizing hit in the corner, and failing that, test the // resizing margin on either side. if( fTestBottom && fTestRight ) wHit = _HitTestRectBottomRight( prc, cxRESIZE, cyRESIZE, pt, wMiss ); else if( fTestRight ) wHit = _HitTestRectRight( prc, cxRESIZE, cyRESIZE, pt, wMiss ); else if( fTestBottom ) wHit = _HitTestRectBottom( prc, cxRESIZE, cyRESIZE, pt, wMiss ); break; } return wHit; } //--------------------------------------------------------------------------- WORD HitTestRect(DWORD dwHTFlags, LPCRECT prc, const MARGINS& margins, const POINT& pt ) { WORD wHit = HTNOWHERE; if( PtInRect( prc, pt ) ) { wHit = HitTest9Grid( prc, margins, pt ); if( HTCLIENT != wHit ) { if( TESTFLAG(dwHTFlags, HTTB_RESIZINGBORDER) ) { WORD wMiss = HTBORDER; wHit = _HitTestResizingRect( dwHTFlags, prc, pt, wHit, wMiss ); } else if( TESTFLAG(dwHTFlags, HTTB_CAPTION|HTTB_FIXEDBORDER) ) { switch( wHit ) { case HTTOP: case HTTOPLEFT: case HTTOPRIGHT: wHit = (WORD)(TESTFLAG(dwHTFlags, HTTB_CAPTION) ? HTCAPTION : HTBORDER); break; default: wHit = HTBORDER; } } } // !HTCLIENT } // PtInRect return wHit; } //--------------------------------------------------------------------------- WORD _HitTestResizingTemplate( DWORD dwHTFlags, HRGN hrgn, const POINT& pt, WORD w9GridHit, WORD wMiss ) { WORD wHit = wMiss; BOOL fTestLeft = TESTFLAG(dwHTFlags, HTTB_RESIZINGBORDER_LEFT); BOOL fTestTop = TESTFLAG(dwHTFlags, HTTB_RESIZINGBORDER_TOP); BOOL fTestRight = TESTFLAG(dwHTFlags, HTTB_RESIZINGBORDER_RIGHT); BOOL fTestBottom = TESTFLAG(dwHTFlags, HTTB_RESIZINGBORDER_BOTTOM); BOOL fTestCaption = TESTFLAG( dwHTFlags, HTTB_CAPTION ); BOOL fInsideRgn; switch( w9GridHit ) { case HTLEFT: if( !fTestLeft ) { return wMiss; } break; case HTTOP: if( fTestCaption ) wMiss = HTCAPTION; if( !fTestTop ) { return wMiss; } break; case HTRIGHT: if( !fTestRight ) { return wMiss; } break; case HTBOTTOM: if( !fTestBottom ) { return wMiss; } break; case HTTOPLEFT: if( fTestCaption ) wMiss = HTCAPTION; if( !fTestTop || !fTestLeft ) { return wMiss; } break; case HTTOPRIGHT: if( fTestCaption ) wMiss = HTCAPTION; if( !fTestTop || !fTestRight ) { return wMiss; } break; case HTBOTTOMLEFT: if( !fTestBottom || !fTestLeft ) { return wMiss; } break; case HTBOTTOMRIGHT: if( !fTestBottom || !fTestRight ) { return wMiss; } break; } fInsideRgn = PtInRegion(hrgn, pt.x, pt.y); if( fInsideRgn ) { wHit = w9GridHit; } return wHit; } //--------------------------------------------------------------------------- WORD HitTestTemplate(DWORD dwHTFlags, LPCRECT prc, HRGN hrgn, const MARGINS& margins, const POINT& pt ) { WORD wHit = HTNOWHERE; if( PtInRect( prc, pt ) ) { wHit = HitTest9Grid( prc, margins, pt ); if( HTCLIENT != wHit ) { if( TESTFLAG(dwHTFlags, HTTB_RESIZINGBORDER) ) { WORD wMiss = HTBORDER; wHit = _HitTestResizingTemplate( dwHTFlags, hrgn, pt, wHit, wMiss ); } else if( TESTFLAG(dwHTFlags, HTTB_CAPTION|HTTB_FIXEDBORDER) ) { switch( wHit ) { case HTTOP: case HTTOPLEFT: case HTTOPRIGHT: wHit = (WORD)(TESTFLAG(dwHTFlags, HTTB_CAPTION) ? HTCAPTION : HTBORDER); break; default: wHit = HTBORDER; } } } // !HTCLIENT } return wHit; } // -------------------------------------------------------------------------- // FillRectClr // // History: 2000-12-06 lmouton borrowed from comctl32\v6\cutils.c //--------------------------------------------------------------------------- void FillRectClr(HDC hdc, LPRECT prc, COLORREF clr) { COLORREF clrSave = SetBkColor(hdc, clr); ExtTextOut(hdc,0,0,ETO_OPAQUE,prc,NULL,0,NULL); SetBkColor(hdc, clrSave); } //--------------------------------------------------------------------------- // _DrawEdge // // Classic values are: // clrLight = 192 192 192 // clrHighlight = 255 255 255 // clrShadow = 128 128 128 // clrDkShadow = 0 0 0 // clrFill = 192 192 192 // // History: 2000-12-06 lmouton borrowed from comctl32\v6\cutils.c, modified colors //--------------------------------------------------------------------------- HRESULT _DrawEdge(HDC hdc, const RECT *pDestRect, UINT uEdge, UINT uFlags, COLORREF clrLight, COLORREF clrHighlight, COLORREF clrShadow, COLORREF clrDkShadow, COLORREF clrFill, OUT RECT *pContentRect) { if (hdc == NULL || pDestRect == NULL) return E_INVALIDARG; HRESULT hr = S_OK; RECT rc, rcD; UINT bdrType; COLORREF clrTL = 0; COLORREF clrBR = 0; // This is were we would adjust for high DPI if the new "BF_DPISCALE" flag is specified in uFlags. int cxBorder = GetSystemMetrics(SM_CXBORDER); int cyBorder = GetSystemMetrics(SM_CYBORDER); // // Enforce monochromicity and flatness // // if (oemInfo.BitCount == 1) // uFlags |= BF_MONO; if (uFlags & BF_MONO) uFlags |= BF_FLAT; CopyRect(&rc, pDestRect); // // Draw the border segment(s), and calculate the remaining space as we // go. // bdrType = (uEdge & BDR_OUTER); if (bdrType) { DrawBorder: // // Get colors. Note the symmetry between raised outer, sunken inner and // sunken outer, raised inner. // if (uFlags & BF_FLAT) { if (uFlags & BF_MONO) clrBR = (bdrType & BDR_OUTER) ? clrDkShadow : clrHighlight; else clrBR = (bdrType & BDR_OUTER) ? clrShadow: clrFill; clrTL = clrBR; } else { // 5 == HILIGHT // 4 == LIGHT // 3 == FACE // 2 == SHADOW // 1 == DKSHADOW switch (bdrType) { // +2 above surface case BDR_RAISEDOUTER: // 5 : 4 clrTL = ((uFlags & BF_SOFT) ? clrHighlight : clrLight); clrBR = clrDkShadow; // 1 break; // +1 above surface case BDR_RAISEDINNER: // 4 : 5 clrTL = ((uFlags & BF_SOFT) ? clrLight : clrHighlight); clrBR = clrShadow; // 2 break; // -1 below surface case BDR_SUNKENOUTER: // 1 : 2 clrTL = ((uFlags & BF_SOFT) ? clrDkShadow : clrShadow); clrBR = clrHighlight; // 5 break; // -2 below surface case BDR_SUNKENINNER: // 2 : 1 clrTL = ((uFlags & BF_SOFT) ? clrShadow : clrDkShadow); clrBR = clrLight; // 4 break; default: hr = E_INVALIDARG; } } if FAILED(hr) { return hr; } // // Draw the sides of the border. NOTE THAT THE ALGORITHM FAVORS THE // BOTTOM AND RIGHT SIDES, since the light source is assumed to be top // left. If we ever decide to let the user set the light source to a // particular corner, then change this algorithm. // // Bottom Right edges if (uFlags & (BF_RIGHT | BF_BOTTOM)) { // Right if (uFlags & BF_RIGHT) { rc.right -= cxBorder; // PatBlt(hdc, rc.right, rc.top, g_cxBorder, rc.bottom - rc.top, PATCOPY); rcD.left = rc.right; rcD.right = rc.right + cxBorder; rcD.top = rc.top; rcD.bottom = rc.bottom; FillRectClr(hdc, &rcD, clrBR); } // Bottom if (uFlags & BF_BOTTOM) { rc.bottom -= cyBorder; // PatBlt(hdc, rc.left, rc.bottom, rc.right - rc.left, g_cyBorder, PATCOPY); rcD.left = rc.left; rcD.right = rc.right; rcD.top = rc.bottom; rcD.bottom = rc.bottom + cyBorder; FillRectClr(hdc, &rcD, clrBR); } } // Top Left edges if (uFlags & (BF_TOP | BF_LEFT)) { // Left if (uFlags & BF_LEFT) { // PatBlt(hdc, rc.left, rc.top, g_cxBorder, rc.bottom - rc.top, PATCOPY); rc.left += cxBorder; rcD.left = rc.left - cxBorder; rcD.right = rc.left; rcD.top = rc.top; rcD.bottom = rc.bottom; FillRectClr(hdc, &rcD, clrTL); } // Top if (uFlags & BF_TOP) { // PatBlt(hdc, rc.left, rc.top, rc.right - rc.left, g_cyBorder, PATCOPY); rc.top += cyBorder; rcD.left = rc.left; rcD.right = rc.right; rcD.top = rc.top - cyBorder; rcD.bottom = rc.top; FillRectClr(hdc, &rcD, clrTL); } } } bdrType = (uEdge & BDR_INNER); if (bdrType) { // // Strip this so the next time through, bdrType will be 0. // Otherwise, we'll loop forever. // uEdge &= ~BDR_INNER; goto DrawBorder; } // // Fill the middle & clean up if asked // if (uFlags & BF_MIDDLE) FillRectClr(hdc, &rc, (uFlags & BF_MONO) ? clrHighlight : clrFill); if ((uFlags & BF_ADJUST) && (pContentRect != NULL)) CopyRect(pContentRect, &rc); return hr; }