본문 바로가기

Windows/MFC

CDialogSkin - 다이얼로그 스킨 입히기

[code]// DialogSkin.cpp : implementation file
//
// DATE        : June 27, 2006
// CODER    : aucd29 (aucd29@gmail.com)
//
//
// ----------------------------------------------------------
// NOTE :August 5, 2006
// ----------------------------------------------------------
// * 다이얼로그에 입힐 스킨을 지정해주도록 하자!
//
// ----------------------------------------------------------
// NOTE : August 9, 2006
// ----------------------------------------------------------
// * 1차적으로 완성...
// * g_bWindowVersion <- 전역 변수로 윈도우xp 인지 아닌지 구분
// 및 테마 사용 유무를 알려주도록 하자.

#include "stdafx.h"
#include "SMS_WIZARD.h"
#include "DialogSkin.h"

// CDialogSkin dialog

IMPLEMENT_DYNAMIC(CDialogSkin, CDialog)

CDialogSkin::CDialogSkin(CWnd* pParent /*=NULL*/)
{

}

CDialogSkin::CDialogSkin(UINT uResourceID, CWnd* pParent)
: CDialog(uResourceID, pParent)
, m_bWindowPos(false)
, m_szWindowText(_T(""))
, m_x(0)
, m_y(0)
, m_bBtnActive(false)
, m_bActive(true)
, m_bBtnClose(false)
, nBorderHeight(0)
, nBorderGap(0)
, nBorderLeftGap(0)
, skinCen(0)
{

}

CDialogSkin::~CDialogSkin()
{
}

void CDialogSkin::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CDialogSkin, CDialog)
    ON_WM_PAINT()
    ON_WM_ACTIVATE()
    ON_WM_NCMOUSEMOVE()
    ON_WM_NCLBUTTONDOWN()
    ON_WM_ERASEBKGND()
    ON_WM_NCPAINT()
    ON_WM_NCACTIVATE()
END_MESSAGE_MAP()

// CDialogSkin message handlers
void CDialogSkin::OnPaint()
{
    CPaintDC dc(this); // device context for painting

    CDC* pDC = GetWindowDC();
    
    if (!m_bWindowPos)
    {
        GetWindowRect(&m_rect);
        SetLoadImage();
    }

    CDC bitmapDC;
    bitmapDC.CreateCompatibleDC(pDC);

    CMemDC mDC(pDC, &m_rtTop);
    CMemDC mLeftDC(pDC, &m_rtLeft);
    CMemDC mRightDC(pDC, &m_rtRight);
    CMemDC mBottomDC(pDC, &m_rtBottom);

    m_pmDC = &mDC;

    // Active
    if (m_bActive)
    {
        // 상단.. 타이틀
        bitmapDC.SelectObject(&frmLeft);
        mDC.BitBlt(0, m_rtTop.top, bmLeft.bmWidth, bmLeft.bmHeight, &bitmapDC, 0, 0, SRCCOPY);

        bitmapDC.SelectObject(&frmCen);
        mDC.StretchBlt(bmLeft.bmWidth, m_rtTop.top, skinCen, bmRight.bmHeight, &bitmapDC,
            0, 0, bmCen.bmWidth, bmCen.bmHeight, SRCCOPY);

        bitmapDC.SelectObject(&frmRight);
        mDC.BitBlt(skinCen + bmLeft.bmWidth, m_rtTop.top, bmRight.bmWidth, bmRight.bmHeight, &bitmapDC, 0, 0, SRCCOPY);

        // 왼쪽 볼더
        bitmapDC.SelectObject(&frmLeftBorder);
        mLeftDC.StretchBlt(0, 0, 3, m_rtLeft.bottom, &bitmapDC, 0, 0, 3, 1, SRCCOPY);

        // 오른쪽 볼더
        mRightDC.StretchBlt(m_rtTop.right - 3, bmCen.bmHeight-1, 3, m_rtRight.bottom,
            &bitmapDC, 0, 0, 3, 1, SRCCOPY);

        bitmapDC.SelectObject(&frmBottomLeft);
        mBottomDC.BitBlt(0, nBorderHeight, 3, 3, &bitmapDC, 0, 0, SRCCOPY);

        bitmapDC.SelectObject(&frmBottomCen);
        mBottomDC.StretchBlt(3, nBorderHeight, m_rtTop.right-6, 3, &bitmapDC, 0, 0, 1, 3, SRCCOPY);

        bitmapDC.SelectObject(&frmBottomRight);
        mBottomDC.BitBlt(nBorderLeftGap, nBorderHeight, 3, 3, &bitmapDC, 0, 0, SRCCOPY);
    }
    else
    {
        // 상단.. 타이틀
        bitmapDC.SelectObject(&frmGrayLeft);
        mDC.BitBlt(0, m_rtTop.top, bmLeft.bmWidth, bmLeft.bmHeight, &bitmapDC, 0, 0, SRCCOPY);

        bitmapDC.SelectObject(&frmGrayCen);
        mDC.StretchBlt(bmLeft.bmWidth, m_rtTop.top, skinCen, bmRight.bmHeight, &bitmapDC,
            0, 0, bmCen.bmWidth, bmCen.bmHeight, SRCCOPY);

        bitmapDC.SelectObject(&frmGrayRight);
        mDC.BitBlt(skinCen + bmLeft.bmWidth, m_rtTop.top, bmRight.bmWidth, bmRight.bmHeight, &bitmapDC, 0, 0, SRCCOPY);

        // 왼쪽 볼더
        bitmapDC.SelectObject(&frmGrayLeftVertical);
        mLeftDC.StretchBlt(0, 0, 3, m_rtLeft.bottom, &bitmapDC, 0, 0, 3, 1, SRCCOPY);

        // 오른쪽 볼더
        mRightDC.StretchBlt(m_rtTop.right - 3, bmCen.bmHeight-1, 3, m_rtRight.bottom,
            &bitmapDC, 0, 0, 3, 1, SRCCOPY);

        bitmapDC.SelectObject(&frmGrayBottomLeft);
        mBottomDC.BitBlt(0, nBorderHeight, 3, 3, &bitmapDC, 0, 0, SRCCOPY);

        bitmapDC.SelectObject(&frmGrayBottomLeft);
        mBottomDC.StretchBlt(3, nBorderHeight, m_rtTop.right-6, 3, &bitmapDC, 0, 0, 1, 3, SRCCOPY);

        bitmapDC.SelectObject(&frmGrayBottomLeft);
        mBottomDC.BitBlt(nBorderLeftGap, nBorderHeight, 3, 3, &bitmapDC, 0, 0, SRCCOPY);
    }

    CFont font;
    LOGFONT lf;
    memset(&lf, 0, sizeof(LOGFONT));
    int nPlusPos = 0;

    if (g_bWindowVersion == 1)
    {
        lf.lfHeight = 16;
        nPlusPos    += 3;
    }
    else
    {
        lf.lfHeight = 13;
    }

    lf.lfWeight = FW_BOLD;
    wsprintf(lf.lfFaceName, L"Tahoma");
    font.CreateFontIndirect(&lf);
    CFont* def_font = mDC.SelectObject(&font);
    mDC.SetBkMode(TRANSPARENT);
    mDC.SetTextColor(RGB(0, 0, 0));
    mDC.TextOut(15, 5 + nPlusPos, m_szWindowText);    // 검정
    mDC.SetTextColor(RGB(255, 255, 255));
    mDC.TextOut(14, 4 + nPlusPos, m_szWindowText);    // 흰색
    mDC.SelectObject(def_font);
    font.DeleteObject();

    DrawCloseButton(true);
}

BOOL CDialogSkin::OnInitDialog()
{
    CDialog::OnInitDialog();

    // 닫기 버튼을 비 활성화 시킨다.
    CMenu* pMenu = this->GetSystemMenu(FALSE);
    pMenu->ModifyMenu(SC_CLOSE, MF_BYCOMMAND | MF_GRAYED);
    pMenu->EnableMenuItem(SC_CLOSE, MF_BYCOMMAND | MF_GRAYED);

    // 나날히 늘어가는 잔머리..
    // 안깜박이게 하려고... 윈도우 타이틀을 메모리에 넣었다가
    // 현재 뿌리고 있는 건 지우고 Paint에서 다시 적게 했다.
    GetWindowText(m_szWindowText);
    SetWindowText(L"");

    return TRUE; // return TRUE unless you set the focus to a control
    // EXCEPTION: OCX Property Pages should return FALSE
}

void CDialogSkin::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized)
{
    CDialog::OnActivate(nState, pWndOther, bMinimized);

    switch( nState )
    {
    case WA_INACTIVE:
        m_bActive = false;
        OnPaint();
        break;
    case WA_ACTIVE:
    case WA_CLICKACTIVE:
        m_bActive = true;
        OnPaint();
        break;
    }
}

void CDialogSkin::SetLoadImage(void)
{
    // 타이틀 바 위치 설정
    m_rtTop         = m_rect;
    int nPlusPos = 0;

    // 타이틀 바 이미지 로드
    // XP 테마 때문에 이면 이렇게.
    if (g_bWindowVersion)
    {
        frmLeft.LoadBitmap(IDB_FRM_TOP_LEFT);
        frmCen.LoadBitmap(IDB_FRM_TOP_CEN);
        frmRight.LoadBitmap(IDB_FRM_TOP_RIGHT);

        frmGrayLeft.LoadBitmap(IDB_FRM_GRAY_TOP_LEFT);
        frmGrayCen.LoadBitmap(IDB_FRM_GRAY_TOP_CEN);
        frmGrayRight.LoadBitmap(IDB_FRM_GRAY_TOP_RIGHT);
        
        nPlusPos += 3;
    }
    else
    {
        frmLeft.LoadBitmap(IDB_OLDFRM_LEFT);
        frmCen.LoadBitmap(IDB_OLDFRM_CEN);
        frmRight.LoadBitmap(IDB_OLDFRM_RIGHT);

        frmGrayLeft.LoadBitmap(IDB_FRM_GRAY_OLDTOP_LEFT);
        frmGrayCen.LoadBitmap(IDB_FRM_GRAY_OLDTOP_CEN);
        frmGrayRight.LoadBitmap(IDB_FRM_GRAY_OLDTOP_RIGHT);
    }

    frmLeftBorder.LoadBitmap(IDB_FRM_RIGHT_BORDER);
    frmRightBorder.LoadBitmap(IDB_FRM_RIGHT_BORDER);
    frmGrayLeftVertical.LoadBitmap(IDB_FRM_GRAY_VERTICAL);

    frmBottomLeft.LoadBitmap(IDB_FRM_BOTTOM_LEFT);
    frmBottomCen.LoadBitmap(IDB_FRM_BOTTOM_RIGHT);
    frmBottomRight.LoadBitmap(IDB_FRM_BOTTOM_RIGHT);

    frmLeft.GetObject(sizeof(BITMAP), &bmLeft);
    frmCen.GetObject(sizeof(BITMAP), &bmCen);
    frmRight.GetObject(sizeof(BITMAP), &bmRight);

    frmGrayBottomLeft.LoadBitmap(IDB_FRM_GRAY_BOTTOM_LEFT);
    frmGrayBottomCen.LoadBitmap(IDB_FRM_GRAY_BOTTOM_CEN);
    frmGrayBottomRight.LoadBitmap(IDB_FRM_GRAY_BOTTOM_RIGHT);

    m_rtTop.top         = 0;
    m_rtTop.left     = 0;
    m_rtTop.bottom     = bmLeft.bmHeight+m_rtTop.top;
    m_rtTop.right    -= m_rect.left;

    m_bWindowPos     = true;

    // 버튼들
    m_btnClose.LoadBitmap(IDB_FRM_BTN_CLOSE);
    m_btnCloseActive.LoadBitmap(IDB_FRM_BTN_CLOSE_ACTIVE);
    m_btnCloseOver.LoadBitmap(IDB_FRM_BTN_CLOSE_OVER);
    m_btnCloseDisable.LoadBitmap(IDB_FRM_BTN_CLOSE_DISABLE);

    m_btnClose.GetObject(sizeof(BITMAP), &bmClose);

    m_y = GetSystemMetrics(SM_CYFRAME) + nPlusPos;
    m_x = (m_rect.right - m_rect.left) - m_y;

    int x = m_x;

    // 영역을 벗어났는지 아닌지 확인해주기 위한 영역 설정
    m_hRgnClose        = CreateRectRgn(x, m_y, x + bmClose.bmWidth, m_y + bmClose.bmHeight);

    nBorderHeight    = m_rect.bottom - m_rect.top - 4;
    nBorderGap        = bmCen.bmHeight - 1;
    nBorderLeftGap = m_rtTop.right - 3;
    skinCen            = m_rtTop.right - bmLeft.bmWidth - bmRight.bmWidth;

    m_rtLeft.SetRect(0, nBorderGap, 3, nBorderHeight);
    m_rtRight.SetRect(nBorderLeftGap, nBorderGap, m_rtTop.right, nBorderHeight);
    m_rtBottom.SetRect(0, nBorderHeight, m_rtTop.right, nBorderHeight + 3);    
}


void CDialogSkin::DrawCloseButton(bool bDCType=false)
{
    CDC* pDC = GetWindowDC();
    CDC bitmapDC;
    bitmapDC.CreateCompatibleDC(pDC);
    int x = m_x - bmClose.bmWidth;

    if(!m_bActive)
    {
        bitmapDC.SelectObject(&m_btnCloseDisable);
    }
    else if (m_bBtnActive)
    {
        // 선택 시
        bitmapDC.SelectObject(&m_btnCloseActive);
        m_bBtnActive = false;
        pDC->BitBlt(x, m_y, bmClose.bmWidth, bmClose.bmHeight, &bitmapDC, 0, 0, SRCCOPY);
        return;
    }
    else if (m_nNcButtonOver == 3)
    {
        // 오버 시
        bitmapDC.SelectObject(&m_btnCloseOver);
        pDC->BitBlt(x, m_y, bmClose.bmWidth, bmClose.bmHeight, &bitmapDC, 0, 0, SRCCOPY);
        return;
    }
    else
    {
        // 일반
        bitmapDC.SelectObject(&m_btnClose);
    }

    m_pmDC->BitBlt(x, m_y, bmClose.bmWidth, bmClose.bmHeight, &bitmapDC, 0, 0, SRCCOPY);
}

void CDialogSkin::OnNcMouseMove(UINT nHitTest, CPoint point)
{
    //TRACE(L"NcMouseMove\n");
    // 해당 영역에 올라가면
    // None : 0
    // Min : 1
    // Close : 3
    //
    // m_nNcButtonOver
    // Frame window 상에서는 none, min, max, close가 필요하지만
    // 이건 차일드 윈도우라.. 필요가 없어서 지