// TabSheet.cpp : implementation file // #include "stdafx.h" #include "TabSheet.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif // constant string definitions here (or you can put it into resource string table) #define IDS_UTIL_TAB "TAB" #define IDS_UTIL_UXTHEME "UxTheme.dll" #define IDS_UTIL_THEMEACT "IsThemeActive" #define IDS_UTIL_THEMEOPN "OpenThemeData" #define IDS_UTIL_THEMEBCKG "DrawThemeBackground" /***********************************************************************************************************/ ///////////////////////////////////////////////////////////////////////////// // CTabSheet CTabSheet::CTabSheet() { m_nNumOfPages = 0; m_nCurrentPage = 0; m_eTabOrientation=e_tabBottom; // default initial orientation is: bottom } CTabSheet::~CTabSheet() { } BEGIN_MESSAGE_MAP(CTabSheet, CTabCtrl) //{{AFX_MSG_MAP(CTabSheet) ON_WM_LBUTTONDOWN() //}}AFX_MSG_MAP ON_WM_PAINT() END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CTabSheet message handlers BOOL CTabSheet::AddPage(LPCTSTR title, CDialog *pDialog,UINT ID) { if( MAXPAGE == m_nNumOfPages ) return FALSE; m_nNumOfPages++; m_pPages[m_nNumOfPages-1] = pDialog; m_IDD[m_nNumOfPages-1] = ID; m_Title[m_nNumOfPages-1] = title; return TRUE; } void CTabSheet::SetRect() { CRect tabRect, itemRect; int nX, nY, nXc, nYc; GetClientRect(&tabRect); GetItemRect(0, &itemRect); nX=itemRect.left; nY=itemRect.bottom+1; nXc=tabRect.right-itemRect.left-2; nYc=tabRect.bottom-nY-2; m_pPages[0]->SetWindowPos(&wndTop, nX, nY, nXc, nYc, SWP_SHOWWINDOW); for( int nCount=1; nCount < m_nNumOfPages; nCount++ ) m_pPages[nCount]->SetWindowPos(&wndTop, nX, nY, nXc, nYc, SWP_HIDEWINDOW); } void CTabSheet::Show() { for( int i=0; i < m_nNumOfPages; i++ ) { m_pPages[i]->Create( m_IDD[i], this ); InsertItem( i, m_Title[i] ); } m_pPages[0]->ShowWindow(SW_SHOW); for( i=1; i < m_nNumOfPages; i++) m_pPages[i]->ShowWindow(SW_HIDE); SetRect(); } void CTabSheet::OnLButtonDown(UINT nFlags, CPoint point) { CTabCtrl::OnLButtonDown(nFlags, point); if(m_nCurrentPage != GetCurFocus()) { m_pPages[m_nCurrentPage]->ShowWindow(SW_HIDE); m_nCurrentPage=GetCurFocus(); m_pPages[m_nCurrentPage]->ShowWindow(SW_SHOW); // m_pPages[m_nCurrentPage]->SetFocus(); } } int CTabSheet::SetCurSel(int nItem) { if( nItem < 0 || nItem >= m_nNumOfPages) return -1; int ret = m_nCurrentPage; if(m_nCurrentPage != nItem ) { m_pPages[m_nCurrentPage]->ShowWindow(SW_HIDE); m_nCurrentPage = nItem; m_pPages[m_nCurrentPage]->ShowWindow(SW_SHOW); // m_pPages[m_nCurrentPage]->SetFocus(); CTabCtrl::SetCurSel(nItem); } return ret; } int CTabSheet::GetCurSel() { return CTabCtrl::GetCurSel(); } void CTabSheet::OnPaint() { CPaintDC dc(this); // device context for painting // TODO: 在此处添加消息处理程序代码 // 不为绘图消息调用 CTabCtrl::OnPaint() //AfxMessageBox("dd"); CRect rcClip; rcClip.SetRectEmpty(); dc.GetClipBox(rcClip); // 1st paint the tab body CRect rcPage,rcItem,rcClient; GetClientRect(&rcPage); rcClient=rcPage; AdjustRect(FALSE,rcPage); switch(m_eTabOrientation) { case e_tabTop: rcClient.top =rcPage.top -2; break; case e_tabBottom: rcClient.bottom=rcPage.bottom+3; break; case e_tabLeft: rcClient.left =rcPage.left -1; break; case e_tabRight: rcClient.right =rcPage.right +3; break; default: ASSERT(FALSE); return; } UINT uiVertBottm; uiVertBottm =(m_eTabOrientation&1)? 8:0; // 8=bottom uiVertBottm|=(m_eTabOrientation&2)?16:0; // 16=vertical UINT uiFlags=1|uiVertBottm; // 1=body DrawThemesXpTabItem(&dc, -1, rcClient,uiFlags); // TABP_PANE=9,0,'TAB' int nTab=GetItemCount(); // paint the tabs first and then the borders if(!nTab) return; // no tab pages added // 2nd paint the inactive tabs CRect ; TCHITTESTINFO hti; hti.flags=0; ::GetCursorPos(&hti.pt); ScreenToClient(&hti.pt); int ixHot=HitTest(&hti); int ixSel=GetCurSel(); for(int ixTab=0; ixTab good for rotating bitmap in any resolution bihOut.biWidth =szBmp.cx; bihOut.biHeight=szBmp.cy; int nBmpWdtPS=DWordAlign(szBmp.cx*3); int nSzBuffPS=((nBmpWdtPS*szBmp.cy)/8+2)*8; LPBYTE pcImg=NULL; if(bBottom || bVertic) { pcImg=new BYTE[nSzBuffPS]; ASSERT(pcImg); } int nStart=0,nLenSub=0; if(bBody && bBottom && !bVertic) nStart=3,nLenSub=4; // if bottom oriented flip the body contest only (no shadows were flipped) // 3rd if it is left oriented tab, draw tab context before mirroring or rotating (before GetDIBits) if(bVertic) { if(bBody || !bBottom) bihOut.biHeight=-szBmp.cy; if(!bBottom && !bBody && ixItem>=0) // { if(bSel) rcMem.bottom--; DrawTabItem(&dcMem, ixItem, rcMem, uiFlag); ixItem=-1; } } // rotate or mirror // 4th get bits (for rotate) and mirror: body=(all except top) tab=(all except top) if(bVertic || bBottom) // get bits: { GetDIBits(*pDC, bmpMem.operator HBITMAP(),nStart,szBmp.cy-nLenSub,pcImg,&biOut,DIB_RGB_COLORS); if(bBottom) // mirror: body=(bottom and right) tab=(bottom and right) { bihOut.biHeight=-szBmp.cy; // to mirror bitmap is eough to use negative height between Get/SetDIBits SetDIBits(*pDC, bmpMem.operator HBITMAP(),nStart,szBmp.cy-nLenSub,pcImg,&biOut,DIB_RGB_COLORS); if(bBody && bVertic) // when it is right oriented body -> flip twice, first flip border shadows, than flip shaded inside body again { nStart=2; nLenSub=4; bihOut.biHeight=szBmp.cy; GetDIBits(*pDC, bmpMem.operator HBITMAP(),nStart,szBmp.cy-nLenSub,pcImg,&biOut,DIB_RGB_COLORS); bihOut.biHeight=-szBmp.cy; // to mirror bitmap is eough to use negative height between Get/SetDIBits SetDIBits(*pDC, bmpMem.operator HBITMAP(),nStart,szBmp.cy-nLenSub,pcImg,&biOut,DIB_RGB_COLORS); } } } // 5th if it is bottom or right oriented tab, draw after mirroring background (do GetDIBits again) if(!bBody && ixItem>=0) // { if(bSel) rcMem.bottom--; DrawTabItem(&dcMem, ixItem, rcMem, uiFlag); if(bVertic) // if it is right tab, do GetDIBits again { bihOut.biHeight=-szBmp.cy; GetDIBits(*pDC, bmpMem.operator HBITMAP(),nStart,szBmp.cy-nLenSub,pcImg,&biOut,DIB_RGB_COLORS); } } // 6th: do rotate now, finaly if(bVertic) // force rotating bitmap as RGB -> good for any resolution { SwapVars(szBmp.cx,szBmp.cy); int nBmpWdtPD=DWordAlign(szBmp.cx*3); int nPadD=nBmpWdtPD-szBmp.cx*3; int nSzBuffPD=((nBmpWdtPD*szBmp.cy)/8+2)*8; LPBYTE pcImgRotate=new BYTE[nSzBuffPD]; ASSERT(pcImgRotate); int nWidth,nHeight=szBmp.cy,nHeight1=nHeight-1; //==================================== //------------------------------------ // here is the example how to speed up lengthy repeatetive processing by using inline assembler #define __USE_MASM__ // the same processing is in C and in asm. To use C -> comment the beginning of this line // Do the actual whole RGB bitmap rotating in C or assembler #ifndef __USE_MASM__ LPBYTE pcImgS=pcImg; LPBYTE pcImgD=pcImgRotate; int ixHeight=0; BOOL bLast=FALSE; while(ixHeightcould not read/write DWORD)!! else { ixHeight++; pcImgD+=nPadD; // destination bitmap horizontal padding to DWORD pcImgS=pcImg+(ixHeight*3); // reset the source to the begining of the next vertical line } } #else // __USE_MASM__ nBmpWdtPS-=4; // adjust esi increment (due to esi self-incrementing by movsd) nWidth=szBmp.cx; __asm { mov esi, pcImg // source index mov edi, pcImgRotate // destination index xor ebx, ebx // vertical counter loop_height: mov ecx, nWidth // horizontal counter cmp ebx, nHeight1 // check is it the last line jne loop_width dec ecx // if it is decremnt for the last pixel loop_width: movsd // copies 4 bytes and increments source and destination by 4 (we need only 3 bytes copied 'one pixel' RGB triplet) dec edi // adjust edi to 'as incremented by 3' add esi,nBmpWdtPS // adjust esi to the next source line loop loop_width // loop one hotizontal destination line cmp ebx, nHeight1 // check is it the last line je do_last // if not last, do incrementing here inc ebx // increment vertical counter add edi, nPadD // adjust destination index by possible padding to DWORD mov esi, ebx // reset the source index: add vertical counter * 3 shl esi, 1 // (is the same as * 2 +1*) add esi, ebx // +1* add esi, pcImg // add to the beginning of the source jmp loop_height // loop whole height do_last: // the last pixel is done by movsw // moving first two bytes movsb // and than by moving the very last byte } #endif // __USE_MASM__ dcMem.SelectObject(pBmpOld); bmpMem.DeleteObject(); // recreate rotated bitmap bmpMem.CreateCompatibleBitmap(pDC,szBmp.cx,szBmp.cy); dcMem.SelectObject(&bmpMem); bihOut.biWidth =szBmp.cx; bihOut.biHeight=bBody?-szBmp.cy:szBmp.cy; SetDIBits(*pDC, bmpMem.operator HBITMAP(),0,szBmp.cy,pcImgRotate,&biOut,DIB_RGB_COLORS); // set rotated bitmap bits delete pcImgRotate; } if(pcImg) delete pcImg; // 6th blit mirrored/rotated image to the screen pDC->BitBlt(rcItem.left,rcItem.top,szBmp.cx,szBmp.cy,&dcMem,0,0,SRCCOPY); // dcMem.SelectObject(pBmpOld); CRect rc(100,0,1050,50);//85 //pDC->FillSolidRect(rc, g_Color); } //---------------------------------------------------------------------------------------------------------- // draw tab item context: possible icon and text void CTabSheet::DrawTabItem(CDC* pDC, int ixItem, const CRect& rcItemC, UINT uiFlags) { TC_ITEM tci; CString sText; tci.mask =TCIF_TEXT | TCIF_IMAGE; tci.pszText =sText.GetBuffer(128); tci.cchTextMax=127; GetItem(ixItem,&tci); sText.ReleaseBuffer(); BOOL bSel =(uiFlags&2)?TRUE:FALSE; BOOL bBottom=(uiFlags&8)?TRUE:FALSE; CRect rcItem=rcItemC; if(bSel) rcItem.bottom -= 1; else rcItem.bottom += 2; rcItem.left+=(bBottom?3:6); // text & icon rcItem.top +=(bBottom?3:2) + (bSel ? 1 : (bBottom?0:3)); int nOldMode=pDC->SetBkMode(TRANSPARENT); HIMAGELIST hilTabs = (HIMAGELIST)TabCtrl_GetImageList(GetSafeHwnd()); // icon if(hilTabs && tci.iImage>=0) { ImageList_Draw(hilTabs, tci.iImage, *pDC, rcItem.left+(bSel?2:0), rcItem.top+(bSel?0:-2), ILD_TRANSPARENT); rcItem.left+=19; } else rcItem.OffsetRect(-2,0); if(sText.GetLength()) { CFont* pOldFont=pDC->SelectObject(GetFont()); // prepare dc rcItem.right-=3; // text CRect rect(0,0,rcItem.Width(),20); ::DrawText(pDC->GetSafeHdc(),sText.GetBuffer(sText.GetLength()+4),-1,rect,DT_CALCRECT|DT_SINGLELINE|DT_MODIFYSTRING|DT_END_ELLIPSIS); sText.ReleaseBuffer(); rcItem.OffsetRect((bBottom?-1:0),(bSel?1:-1)); pDC->DrawText(sText, rcItem, DT_NOPREFIX|DT_CENTER); pDC->SelectObject(pOldFont); } pDC->SetBkMode(nOldMode); } //---------------------------------------------------------------------------------------------------------- //BOOL CXPTabCtrl::IsExtendedTabTheamedXP() //{ // if(!m_bTabExtended || !::IsWindow(GetSafeHwnd())) // return FALSE; // DWORD dwStyle=GetStyle(); // m_eTabOrientation=(dwStyle&TCS_BOTTOM)?e_tabBottom:e_tabTop; // if(dwStyle&TCS_VERTICAL) // m_eTabOrientation=(m_eTabOrientation==e_tabTop)?e_tabLeft:e_tabRight; //#ifdef USE_DEFAULT_XP_TOPTAB // if(m_eTabOrientation==e_tabTop) return FALSE; //#endif // return IsThemeActiveXP()?TRUE:FALSE; //} //========================================================================================================== //// these two messages are necessary only to properly redraw deselected tab background, because //void CTabSheet::OnTabSelChanging(NMHDR* pNMHDR, LRESULT* pResult) // selected rect was inflated by 2 points //{ // UNUSED_ALWAYS(pNMHDR); // m_ixSelOld=GetCurSel(); // *pResult=0L; //} //---------------------------------------------------------------------------------------------------------- //void CTabSheet::OnTabSelChanged(NMHDR* pNMHDR, LRESULT* pResult) //{ // UNUSED_ALWAYS(pNMHDR); // if(m_ixSelOld>=0 && m_ixSelOld!=GetCurSel() && IsExtendedTabTheamedXP()) // else // { CWnd* pWndParent=GetParent(); // m_iNowSel = GetCurSel(); // if(pWndParent) // { CRect rcOldSel; GetItemRect(m_ixSelOld, rcOldSel); rcOldSel.InflateRect(2,2); // ClientToScreen(&rcOldSel); pWndParent->ScreenToClient(&rcOldSel); // pWndParent->InvalidateRect(rcOldSel); // } } // *pResult=1L; //} /***********************************************************************************************************/ // Helper functions /***********************************************************************************************************/ int DWordAlign(int n) { const int rem=n%4; if(rem) n+=(4-rem); return n; } //---------------------------------------------------------------------------------------------------------- BOOL IsThemeActiveEx() { // check theme activity always (could change during application running) HINSTANCE hDll=LoadLibrary(CString((LPCTSTR)IDS_UTIL_UXTHEME)); // 'UxTheme.dll' if(hDll==NULL) return FALSE; // the DLL won't be available on anything except Windows XP UINT (PASCAL *pfnIsThemeActive)(); (FARPROC&)pfnIsThemeActive=GetProcAddress(hDll,CString((LPCTSTR)IDS_UTIL_THEMEACT)); // 'IsThemeActive' UINT uiThemeActive=0; if(pfnIsThemeActive) uiThemeActive=pfnIsThemeActive(); FreeLibrary(hDll); return uiThemeActive?TRUE:FALSE; } //---------------------------------------------------------------------------------------------------------- #define PACKVERSION(major,minor) MAKELONG(minor,major) DWORD GetWinVersion() { static DWORD c_dwWinVers=0; // check win version only once (will not change during application) if(!c_dwWinVers) { OSVERSIONINFO osvi; ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); // Initialize the OSVERSIONINFO structure. osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&osvi); c_dwWinVers=PACKVERSION(osvi.dwMajorVersion,osvi.dwMinorVersion); } return c_dwWinVers; } //---------------------------------------------------------------------------------------------------------- BOOL IsWinXP() { return GetWinVersion()>=PACKVERSION(5,1)?TRUE:FALSE; } //---------------------------------------------------------------------------------------------------------- BOOL IsThemeActiveXP() { return (IsWinXP() && IsThemeActiveEx())?TRUE:FALSE; } //---------------------------------------------------------------------------------------------------------- #define WPART_NAME_SZ 128 HRESULT DrawThemesPart(HDC hDC, int iPartId, int iStateId, LPCSTR uiPartNameID, LPRECT prcBox) { if(!IsWinXP()) return E_FAIL; HINSTANCE hDll=LoadLibrary(CString((LPCTSTR)IDS_UTIL_UXTHEME)); // 'UxTheme.dll' if(!hDll) return E_FAIL; BOOL (PASCAL* pfnIsThemeActive)(); UINT hTheme=0; (FARPROC&)pfnIsThemeActive=GetProcAddress(hDll,CString((LPCTSTR)IDS_UTIL_THEMEACT)); // 'IsThemeActive' HRESULT hResult=E_FAIL; if(pfnIsThemeActive && pfnIsThemeActive()) { CString sPartName((LPCTSTR)uiPartNameID); if(sPartName.GetLength()>0) { WCHAR swPartName[WPART_NAME_SZ]; MultiByteToWideChar(CP_ACP,0,(LPCSTR)sPartName,-1,swPartName,sizeof(swPartName)/sizeof(WCHAR)); UINT (PASCAL* pfnOpenThemeData)(HWND hwnd, LPCWSTR pszClassList); (FARPROC&)pfnOpenThemeData=GetProcAddress(hDll,CString((LPCTSTR)IDS_UTIL_THEMEOPN));// 'OpenThemeData' if(pfnOpenThemeData && (hTheme=pfnOpenThemeData(NULL, swPartName))!=0) { UINT (PASCAL* pfnDrawThemeBackground)(UINT htheme,HDC hdc,int iPartID,int iStateID,const RECT* prcBx,const RECT* prcClip); (FARPROC&)pfnDrawThemeBackground=GetProcAddress(hDll,CString((LPCTSTR)IDS_UTIL_THEMEBCKG)); // 'DrawThemeBackground' if(pfnDrawThemeBackground) hResult=pfnDrawThemeBackground(hTheme, hDC, iPartId, iStateId, prcBox, NULL); } } } FreeLibrary(hDll); return hResult; }