// ========================================================================== // Class Implementation : COXRulerOrganizer // ========================================================================== // Version: 9.3 // This software along with its related components, documentation and files ("The Libraries") // is © 1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is // governed by a software license agreement ("Agreement"). Copies of the Agreement are // available at The Code Project (www.codeproject.com), as part of the package you downloaded // to obtain this file, or directly from our office. For a copy of the license governing // this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900. // ////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "OXRulerOrganizer.h" #include #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[]=__FILE__; #endif double COXRulerBar::PIXEL_PAD=14.0; COXRulerBar::COXRulerBar() : m_rectInvert(0,0,0,0), m_rectOldInvert(0,0,0,0) { m_bHorizontal=TRUE; m_nPos=0; m_nZoom=100; m_nCalibrate=100; m_bOldIsHilited=FALSE; m_dDisplayDpi=100.0; m_bUseNonDefaultDpi=FALSE; } COXRulerBar::~COXRulerBar() { } BOOL COXRulerBar::Init(BOOL bHorizontal /*=TRUE*/, UINT nCalibrate /*=100*/) { m_nCalibrate=nCalibrate; m_bHorizontal=bHorizontal; // for 1, 2, ... // font for horizontal bar VERIFY(m_font.CreateFont(-12,0,0,0,FW_NORMAL,FALSE,FALSE,FALSE,ANSI_CHARSET, 0,0,0,0,_T("Times New Roman"))); // font for vertical bar VERIFY(m_fontVert.CreateFont(-12,0,900,0,FW_NORMAL,FALSE,FALSE,FALSE,ANSI_CHARSET, 0,0,0,0,_T("Times New Roman"))); return TRUE; } BEGIN_MESSAGE_MAP(COXRulerBar, CWnd) //{{AFX_MSG_MAP(COXRulerBar) ON_WM_PAINT() //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // COXRulerBar implementation BOOL COXRulerBar::Create(CWnd* pParentWnd, BOOL bHorizontal, CRect rect, DWORD dwStyle/*=WS_CHILD|WS_VISIBLE*/, UINT nCalibrate/*=100*/, UINT nID/*=IDC_STATIC*/) { ASSERT(pParentWnd!=NULL); WNDCLASS wndClass; wndClass.style=CS_DBLCLKS; wndClass.lpfnWndProc=AfxWndProc; wndClass.cbClsExtra=0; wndClass.cbWndExtra=0; wndClass.hInstance=AfxGetInstanceHandle(); wndClass.hIcon=::LoadCursor(NULL,IDC_ARROW); wndClass.hCursor=0; wndClass.hbrBackground=(HBRUSH)(COLOR_WINDOW+1); wndClass.lpszMenuName=NULL; wndClass.lpszClassName=_T("RulerBarWnd"); if(!AfxRegisterClass(&wndClass)) { return FALSE; } if(!CWnd::Create(wndClass.lpszClassName,NULL,dwStyle,rect,pParentWnd,nID,NULL)) { return FALSE; } return Init(bHorizontal,nCalibrate); } void COXRulerBar::DrawTick(CDC* pDC, CRect& rect, double dOffset, BOOL bMidTick) { int nTickX, nTickY; int nTickWidth, nTickHeight; if(dOffset < 0) return; if(m_bHorizontal) { if(bMidTick) nTickHeight=rect.Height()/2; else nTickHeight=rect.Height()/4; nTickWidth=0; nTickX=(int)floor(rect.left+dOffset); nTickY=rect.top+(rect.Height()-nTickHeight+1)/2; } else { if(bMidTick) nTickWidth=rect.Width()/2; else nTickWidth=rect.Width()/4; nTickHeight=0; nTickX=rect.left+(rect.Width()-nTickWidth+1)/2; nTickY=(int)floor(rect.top+dOffset); } CPoint pt(nTickX, nTickY); CPoint ptTo(nTickX+nTickWidth, nTickY+nTickHeight); if(rect.PtInRect(pt) && rect.PtInRect(ptTo)) { pDC->MoveTo(nTickX, nTickY); pDC->LineTo(nTickX+nTickWidth, nTickY+nTickHeight); } } void COXRulerBar::DrawScale(CDC* pDC, CRect rect) { CBrush bkgndBrush(::GetSysColor(COLOR_WINDOW)); pDC->FillRect(rect,&bkgndBrush); pDC->DrawEdge(rect,EDGE_SUNKEN,BF_RECT); rect.DeflateRect(2,2); // Clip all output to the rectangle pDC->IntersectClipRect(rect); CFont* oldFont=NULL; if(m_bHorizontal) pDC->SelectObject(&m_font); else pDC->SelectObject(&m_fontVert); COLORREF crOldText=pDC->SetTextColor(::GetSysColor(COLOR_BTNTEXT)); CPen pen(PS_SOLID,1,::GetSysColor(COLOR_BTNTEXT)); CPen* oldPen=pDC->SelectObject(&pen); int oldMode=pDC->SetBkMode(TRANSPARENT); double dSpacer=m_nZoom*0.1; int nTicksPerUnit=(100!=m_nCalibrate) ? 4 : 10; double dDPI=(m_bUseNonDefaultDpi) ? m_dDisplayDpi : 100.0; double dPixelsPerUnit=m_nZoom*100.0/m_nCalibrate*dDPI/100.0; double dTickSpace=dPixelsPerUnit/nTicksPerUnit; double dOrg=nTicksPerUnit-fmod(m_nPos, dSpacer); int nSpan=(m_bHorizontal) ? rect.Width() : rect.Height(); double dSpanEnd=nSpan+2*dSpacer+dPixelsPerUnit; int nUnitNumber=(int)(m_nPos/dPixelsPerUnit); int nXErr=0; int nYErr=0; for(double d=dOrg; dPIXEL_PAD/2.0) { for(int nIndex=1; nIndex=PIXEL_PAD) { double dOffset=d+dPos+0.5+dTickSpace*nTicksPerUnit/2; DrawTick(pDC,rect,dOffset,TRUE); } if(dPixelsPerUnit0) { char nCh=(char)(48+(nUnitNumber%10)); // 47 is '0' // Keep things as ASCII, since Unicode numeric characters // may be non-consective CString sCh(nCh); if(dPixelsPerUnitGetOutputTextExtent(sCh); int nXPos; int nYPos; if(m_bHorizontal) { nXPos=(int)floor(rect.left+d+dPos+0.5-(sNum.cx/2.0)); nYPos=rect.top+(rect.Height()-sNum.cy)/2; if(nXPos <= nXErr) continue; else nXErr=sNum.cx+nXPos; } else { nXPos=rect.left+(rect.Width()-sNum.cy)/2; nYPos=(int)floor(rect.top+d+dPos+0.5+(sNum.cx/2.0)); if(nYPos <= nYErr) continue; else nYErr=sNum.cy+nYPos; } pDC->TextOut(nXPos, nYPos, sCh); } } // Get rid of the clipping region pDC->SelectClipRgn(NULL,RGN_COPY); pDC->SetBkMode(oldMode); pDC->SelectObject(oldPen); pDC->SetTextColor(crOldText); pDC->SelectObject(oldFont); } void COXRulerBar::DrawRuler(CDC* pDC, CRect rect, CRect rectClip) { ASSERT(pDC!=NULL); if(rect.IsRectEmpty() || rectClip.IsRectEmpty()) return; // create and initialize the memory DC which we are going to draw on first CDC memDc; CBitmap bmpMem,*pOldMemBmp; memDc.CreateCompatibleDC( pDC ); bmpMem.CreateCompatibleBitmap( pDC, rect.right, rect.bottom ); pOldMemBmp = memDc.SelectObject( &bmpMem ); memDc.IntersectClipRect(rectClip); DrawScale(&memDc,rect); memDc.SelectClipRgn(NULL,RGN_COPY); if(m_bOldIsHilited) { CRect rectIntersect; rectIntersect.IntersectRect(m_rectOldInvert,rectClip); ReverseHilite(&memDc,rectIntersect); } //Copy drawing from memory device context to the screen pDC->BitBlt( rect.left, rect.top, rect.Width(), rect.Height(), &memDc, rect.left, rect.top, SRCCOPY ); // a final clean up memDc.SelectObject( pOldMemBmp ); } void COXRulerBar::ReverseHilite(CDC* pDC, CRect rect) { ASSERT(pDC!=NULL); pDC->PatBlt(rect.left,rect.top,rect.Width(),rect.Height(),PATINVERT); } void COXRulerBar::Hilite(CDC* pDC, CRect rect, int nStartPos, int nWidth/*=1*/) { ASSERT(pDC!=NULL); BOOL bRemoveHilite=FALSE; m_rectInvert=rect; m_rectInvert.DeflateRect(2,2); if(m_bHorizontal) { m_rectInvert.left+=nStartPos; bRemoveHilite=((m_rectInvert.left<=2) || (m_rectInvert.left>=m_rectInvert.right)); m_rectInvert.right=m_rectInvert.left+nWidth; if(m_rectInvert.right>rect.right-2) m_rectInvert.right=rect.right-2; } else { m_rectInvert.top+=nStartPos; bRemoveHilite=((m_rectInvert.top<=2) || (m_rectInvert.top>=m_rectInvert.bottom)); m_rectInvert.bottom=m_rectInvert.top+nWidth; if(m_rectInvert.bottom>rect.bottom-2) m_rectInvert.bottom=rect.bottom-2; } m_rectInvert.NormalizeRect(); // for nWidth < 0 if(bRemoveHilite) { if(m_bOldIsHilited) { CRect rectOldInvert=m_rectOldInvert; rectOldInvert.IntersectRect(rectOldInvert,rect); ReverseHilite(pDC,rectOldInvert); } m_rectOldInvert.SetRectEmpty(); m_rectInvert.SetRectEmpty(); m_bOldIsHilited=FALSE; } else { if(m_bOldIsHilited) { CRect rectIntersect; rectIntersect.IntersectRect(m_rectOldInvert,m_rectInvert); CRect rectOldInvert; rectOldInvert.SubtractRect(m_rectOldInvert,rectIntersect); rectOldInvert.IntersectRect(rectOldInvert,rect); ReverseHilite(pDC,rectOldInvert); CRect rectInvert; rectInvert.SubtractRect(m_rectInvert,rectIntersect); ReverseHilite(pDC,rectInvert); } else ReverseHilite(pDC,m_rectInvert); m_bOldIsHilited=TRUE; m_rectOldInvert=m_rectInvert; } } ///////////////////////////////////////////////////////////////////////////// // COXRulerBar message handlers void COXRulerBar::Scroll(UINT nPos, BOOL bRedraw/*=TRUE*/) { m_nPos=m_nZoom*nPos/100; if(bRedraw) Invalidate(); } void COXRulerBar::Zoom(UINT nZoom, BOOL bRedraw/*=TRUE*/) { m_nZoom=nZoom; if(bRedraw) Invalidate(); } void COXRulerBar::SetDpi(double dDpi, BOOL bRedraw/*=TRUE*/) { m_dDisplayDpi=dDpi; m_bUseNonDefaultDpi=TRUE; if(bRedraw) Invalidate(); } void COXRulerBar::UseDefaultDpi(BOOL bUseDefaultDpi, BOOL bRedraw/*=TRUE*/) { m_bUseNonDefaultDpi=!bUseDefaultDpi; if(bRedraw) Invalidate(); } void COXRulerBar::SetCalibrate(UINT nCalibrate, BOOL bRedraw/*=TRUE*/) { m_nCalibrate=nCalibrate; if(bRedraw) Invalidate(); } void COXRulerBar::OnPaint() { CPaintDC dc(this); // device context for painting CRect rectClient; GetClientRect(rectClient); CRect rectClip; dc.GetClipBox(&rectClip); // invalidated region DrawRuler(&dc,rectClient,rectClip); } //////////////////////////////////////////////////////////////////////////////////// COXRulerOrganizer::COXRulerOrganizer() : m_bShowHorzRulerBar(FALSE), m_bShowVertRulerBar(FALSE), m_rectHorzRulerBar(0,0,0,0), m_rectVertRulerBar(0,0,0,0), m_rectBlank(0,0,0,0), m_nHorzRulerBarHeight(OXRB_HORZHEIGHT), m_nVertRulerBarWidth(OXRB_VERTWIDTH), m_nTimerCheckScrollPos(NULL) { } COXRulerOrganizer::~COXRulerOrganizer() { } BOOL COXRulerOrganizer::Attach(CWnd* pWnd, BOOL bHorzRuler/*=TRUE*/, BOOL bVertRuler/*=TRUE*/) { ASSERT(pWnd!=NULL); if(!::IsWindow(pWnd->GetSafeHwnd())) { TRACE(_T("COXRulerOrganizer::Attach: failed, invalid window handle has been specified\n")); return FALSE; } if(IsAttached()) { TRACE(_T("COXRulerOrganizer::Attach: failed, object already has attached window. Call Detach() before!\n")); return FALSE; } HookWindow(pWnd); // create ruler bars ASSERT(!::IsWindow(m_horzRulerBar.GetSafeHwnd()) && !::IsWindow(m_vertRulerBar.GetSafeHwnd())); CRect rect(0,0,0,0); if(!m_horzRulerBar.Create(pWnd,TRUE,rect,WS_CHILD) || !m_vertRulerBar.Create(pWnd,FALSE,rect,WS_CHILD)) { TRACE(_T("COXRulerOrganizer::Attach: failed to create the ruler bars\n")); Detach(); return FALSE; } // create timer for checking scroll position m_nTimerCheckScrollPos=pWnd->SetTimer(IDT_OXRBO_CHECK_SCROLLPOS, OXRBO_CHECK_SCROLLPOS_DELAY,NULL); if(m_nTimerCheckScrollPos==NULL) { TRACE(_T("COXRulerOrganizer::Attach: failed to create timer\n")); Detach(); return FALSE; } m_bShowHorzRulerBar=bHorzRuler; m_bShowVertRulerBar=bVertRuler; CalcLayout(TRUE); return TRUE; } void COXRulerOrganizer::Detach() { // destroy ruler bars if(::IsWindow(m_horzRulerBar.GetSafeHwnd())) m_horzRulerBar.DestroyWindow(); if(::IsWindow(m_vertRulerBar.GetSafeHwnd())) m_vertRulerBar.DestroyWindow(); if(::IsWindow(m_hWndHooked)) { if(m_nTimerCheckScrollPos!=NULL) { GetHookedWnd()->KillTimer(m_nTimerCheckScrollPos); } RedrawAttached(TRUE); } UnhookWindow(); m_bShowHorzRulerBar=FALSE; m_bShowVertRulerBar=FALSE; m_nTimerCheckScrollPos=NULL; } void COXRulerOrganizer::SetHorzRulerBarHeight(int nHorzRulerBarHeight) { m_nHorzRulerBarHeight=nHorzRulerBarHeight; CalcLayout(TRUE); } void COXRulerOrganizer::SetVertRulerBarWidth(int nVertRulerBarWidth) { m_nVertRulerBarWidth=nVertRulerBarWidth; CalcLayout(TRUE); } LRESULT COXRulerOrganizer::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) { #if defined (_WINDLL) #if defined (_AFXDLL) AFX_MANAGE_STATE(AfxGetAppModuleState()); #else AFX_MANAGE_STATE(AfxGetStaticModuleState()); #endif #endif LRESULT result=0; switch(uMsg) { case WM_NCCALCSIZE: { result=COXHookWnd::WindowProc(uMsg,wParam,lParam); LPNCCALCSIZE_PARAMS lpncsp=(LPNCCALCSIZE_PARAMS)lParam; // add an offset if(GetShowVertRulerBar()) { if(lpncsp->rgrc[0].right-lpncsp->rgrc[0].left> GetVertRulerBarWidth()+OXRBO_BORDER) { lpncsp->rgrc[0].left+=GetVertRulerBarWidth()+OXRBO_BORDER; } else { lpncsp->rgrc[0].left=lpncsp->rgrc[0].right; } } if(GetShowHorzRulerBar()) { if(lpncsp->rgrc[0].bottom-lpncsp->rgrc[0].top> GetHorzRulerBarHeight()+OXRBO_BORDER) { lpncsp->rgrc[0].top+=GetHorzRulerBarHeight()+OXRBO_BORDER; } else { lpncsp->rgrc[0].top=lpncsp->rgrc[0].bottom; } } break; } case WM_NCPAINT: { result=COXHookWnd::WindowProc(uMsg,wParam,lParam); CWindowDC dc(GetHookedWnd()); if(!m_rectBlank.IsRectEmpty()) dc.FillSolidRect(m_rectBlank,::GetSysColor(COLOR_BTNFACE)); if(GetShowHorzRulerBar()) { m_horzRulerBar.DrawRuler(&dc,m_rectHorzRulerBar,m_rectHorzRulerBar); dc.MoveTo(m_rectHorzRulerBar.left+1,m_rectHorzRulerBar.bottom+1); dc.LineTo(m_rectHorzRulerBar.right+1,m_rectHorzRulerBar.bottom+1); CPen pen(PS_SOLID,1,::GetSysColor(COLOR_3DSHADOW)); CPen* pOldPen=dc.SelectObject(&pen); dc.MoveTo(m_rectHorzRulerBar.left,m_rectHorzRulerBar.bottom); dc.LineTo(m_rectHorzRulerBar.right+1,m_rectHorzRulerBar.bottom); if(pOldPen!=NULL) dc.SelectObject(pOldPen); } if(GetShowVertRulerBar()) { m_vertRulerBar.DrawRuler(&dc,m_rectVertRulerBar,m_rectVertRulerBar); dc.MoveTo(m_rectVertRulerBar.right+1,m_rectVertRulerBar.top+1); dc.LineTo(m_rectVertRulerBar.right+1,m_rectVertRulerBar.bottom+1); CPen pen(PS_SOLID,1,::GetSysColor(COLOR_3DSHADOW)); CPen* pOldPen=dc.SelectObject(&pen); dc.MoveTo(m_rectVertRulerBar.right,m_rectVertRulerBar.top); dc.LineTo(m_rectVertRulerBar.right,m_rectVertRulerBar.bottom+1); if(pOldPen!=NULL) dc.SelectObject(pOldPen); } break; } case WM_SIZE: { result=COXHookWnd::WindowProc(uMsg,wParam,lParam); if(wParam==SIZE_MAXIMIZED || wParam==SIZE_RESTORED) { CalcLayout(); CheckScrollPos(); RedrawAttached(); } break; } case WM_HSCROLL: { result=COXHookWnd::WindowProc(uMsg,wParam,lParam); CheckScrollPos(); break; } case WM_VSCROLL: { result=COXHookWnd::WindowProc(uMsg,wParam,lParam); CheckScrollPos(); break; } case WM_MOUSEMOVE: { result=COXHookWnd::WindowProc(uMsg,wParam,lParam); if(GetShowHorzRulerBar()) { HiliteRuler(LOWORD(lParam),TRUE,1); } if(GetShowVertRulerBar()) { HiliteRuler(HIWORD(lParam),FALSE,1); } break; } case WM_TIMER: { if(wParam==(UINT)m_nTimerCheckScrollPos) { CheckScrollPos(); return 0; } result=COXHookWnd::WindowProc(uMsg,wParam,lParam); break; } default: { result=COXHookWnd::WindowProc(uMsg,wParam,lParam); break; } } return result; } void COXRulerOrganizer::CalcLayout(BOOL bRepositionBars/*=FALSE*/) { m_rectHorzRulerBar.SetRectEmpty(); m_rectVertRulerBar.SetRectEmpty(); m_rectBlank.SetRectEmpty(); if(!IsAttached()) return; ASSERT(::IsWindow(m_hWndHooked)); CRect rectWindow; ::GetWindowRect(m_hWndHooked,rectWindow); GetHookedWnd()->ScreenToClient(rectWindow); CRect rect; ::GetClientRect(m_hWndHooked,rect); rect-=rectWindow.TopLeft(); DWORD dwStyle=GetHookedWnd()->GetStyle(); if((dwStyle&WS_VSCROLL)==WS_VSCROLL) { rect.right+=::GetSystemMetrics(SM_CXVSCROLL); } if((dwStyle&WS_HSCROLL)==WS_HSCROLL) { rect.bottom+=::GetSystemMetrics(SM_CYHSCROLL); } if(GetShowVertRulerBar()) { rect.left-=GetVertRulerBarWidth()+OXRBO_BORDER; } if(GetShowHorzRulerBar()) { rect.top-=GetHorzRulerBarHeight()+OXRBO_BORDER; } if(GetShowHorzRulerBar() && GetShowVertRulerBar()) { m_rectBlank=rect; m_rectBlank.right=m_rectBlank.left+GetVertRulerBarWidth(); m_rectBlank.bottom=m_rectBlank.top+GetHorzRulerBarHeight(); } if(GetShowHorzRulerBar()) { m_rectHorzRulerBar.left=m_rectBlank.right; m_rectHorzRulerBar.right=rect.right; m_rectHorzRulerBar.top=rect.top; m_rectHorzRulerBar.bottom=m_rectHorzRulerBar.top+GetHorzRulerBarHeight(); } if(GetShowVertRulerBar()) { m_rectVertRulerBar.top=m_rectBlank.bottom; m_rectVertRulerBar.bottom=rect.bottom; m_rectVertRulerBar.left=rect.left; m_rectVertRulerBar.right=m_rectVertRulerBar.left+GetVertRulerBarWidth(); } if(bRepositionBars) RedrawAttached(TRUE); } void COXRulerOrganizer::CheckScrollPos() { BOOL bRedraw=FALSE; if(GetShowHorzRulerBar()) { SCROLLINFO scrollInfo={ sizeof(SCROLLINFO) }; if(GetHookedWnd()->GetScrollInfo(SB_HORZ,&scrollInfo)) { int nNewScrollPos=scrollInfo.nPos; if(nNewScrollPos!=0 && scrollInfo.nPage!=0) { CRect rectClient; ::GetClientRect(m_hWndHooked,rectClient); nNewScrollPos*=(rectClient.Width()/scrollInfo.nPage); } int nOldScrollPos=(int)m_horzRulerBar.GetCurrentPos(); nNewScrollPos=nNewScrollPos*100/m_horzRulerBar.GetZoomLevel(); m_horzRulerBar.Scroll(nNewScrollPos,FALSE); if(nOldScrollPos!=(int)m_horzRulerBar.GetCurrentPos()) { bRedraw=TRUE; } } } if(GetShowVertRulerBar()) { SCROLLINFO scrollInfo={ sizeof(SCROLLINFO) }; if(GetHookedWnd()->GetScrollInfo(SB_VERT,&scrollInfo)) { int nNewScrollPos=scrollInfo.nPos; if(nNewScrollPos!=0 && scrollInfo.nPage!=0) { CRect rectClient; ::GetClientRect(m_hWndHooked,rectClient); nNewScrollPos*=(rectClient.Height()/scrollInfo.nPage); } int nOldScrollPos=(int)m_vertRulerBar.GetCurrentPos(); nNewScrollPos=nNewScrollPos*100/m_vertRulerBar.GetZoomLevel(); m_vertRulerBar.Scroll(nNewScrollPos,FALSE); if(nOldScrollPos!=(int)m_vertRulerBar.GetCurrentPos()) { bRedraw=TRUE; } } } if(bRedraw) { RedrawAttached(); TRACE(_T("RedrawAttached\n")); } }