📄 ftab.cpp
字号:
////////////////////////////////////////////////////////////////
// 1999 Microsoft Systems Journal
// If this program works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
//
// 05/09/02 Lynn McGuire updated with new code from Paul for arrow controls
// http://msdn.microsoft.com/msdnmag/issues/02/10/CQA/
// 09/22/03 Lynn McGuire converted the selected tab font to the same as the
// tab list (made bold also)
// 09/25/03 Lynn McGuire added user editing to the sheet name tabs
// 09/29/03 Lynn McGuire added a right mouse click popup menu
// 09/30/03 Lynn McGuire added first and last arrow controls
#include "stdafx.h"
#include "ftab.h"
// #include "resource.h"
#define ID_EDIT_SHEETS 34069
#define ID_EDIT_ADDSHEET 34071
#define ID_EDIT_DELETESHEET 34072
#define ID_EDIT_RENAMESHEET 34073
#define EditBoxId 5555
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
//////////////////
// Private class to represent one folder tab
//
class CFolderTab {
private:
CString m_sText; // tab text
CRect m_rect; // bounding rect
CRgn m_rgn; // polygon region to fill (trapezoid)
int ComputeRgn(CDC& dc, int x);
int Draw(CDC& dc, CFont& font, BOOL bSelected);
BOOL HitTest(CPoint pt) { return m_rgn.PtInRegion(pt); }
CRect GetRect() const { return m_rect; }
void GetTrapezoid(const CRect& rc, CPoint* pts) const;
friend class CFolderTabCtrl;
public:
CFolderTab(LPCTSTR lpszText) : m_sText(lpszText) { }
LPCTSTR GetText() const { return m_sText; }
void SetText(LPCTSTR lpszText) { m_sText = lpszText; }
};
const CXOFFSET = 8; // defined pitch of trapezoid slant
const CXMARGIN = 2; // left/right text margin
const CYMARGIN = 1; // top/bottom text margin
const CYBORDER = 1; // top border thickness
const CXBUTTON = GetSystemMetrics(SM_CXVSCROLL);
//////////////////
// Compute the the points, rect and region for a tab.
// Input x is starting x pos.
//
int CFolderTab::ComputeRgn(CDC& dc, int x)
{
m_rgn.DeleteObject();
CRect& rc = m_rect;
rc.SetRectEmpty();
// calculate desired text rectangle
dc.DrawText(m_sText, &rc, DT_CALCRECT);
rc.right += 2 * CXOFFSET + 3 * CXMARGIN; // add margins
rc.bottom = rc.top + GetSystemMetrics(SM_CYHSCROLL); // ht = scrollbar height
rc += CPoint(x,0); // shift right
// create trapezoid region
CPoint pts[4];
GetTrapezoid(rc, pts);
m_rgn.CreatePolygonRgn(pts, 4, WINDING);
return rc.Width();
}
//////////////////
// Given the boundint rect, compute trapezoid region.
// Note that the right and bottom edges not included in rect or
// trapezoid; these are normal rules of geometry.
//
void CFolderTab::GetTrapezoid(const CRect& rc, CPoint* pts) const
{
pts[0] = rc.TopLeft();
pts[1] = CPoint(rc.left + CXOFFSET, rc.bottom);
pts[2] = CPoint(rc.right- CXOFFSET-1, rc.bottom);
pts[3] = CPoint(rc.right-1, rc.top);
}
//////////////////
// Draw tab in normal or highlighted state
//
int CFolderTab::Draw(CDC& dc, CFont& font, BOOL bSelected)
{
COLORREF bgColor = GetSysColor(bSelected ? COLOR_WINDOW : COLOR_3DFACE);
COLORREF fgColor = GetSysColor(bSelected ? COLOR_WINDOWTEXT : COLOR_BTNTEXT);
CBrush brush(bgColor); // background brush
dc.SetBkColor(bgColor); // text background
dc.SetTextColor(fgColor); // text color = fg color
CPen blackPen(PS_SOLID, 1, RGB(0,0,0)); // black
CPen shadowPen(PS_SOLID, 1, GetSysColor(COLOR_3DSHADOW));
// Fill trapezoid
CPoint pts[4];
CRect rc = m_rect;
GetTrapezoid(rc, pts);
CPen* pOldPen = dc.SelectObject(&blackPen);
dc.FillRgn(&m_rgn, &brush);
// Draw edges. This is requires two corrections:
// 1) Trapezoid dimensions don't include the right and bottom edges,
// so must use one pixel less on bottom (cybottom)
// 2) the endpoint of LineTo is not included when drawing the line, so
// must add one pixel (cytop)
//
pts[1].y--; // correction #1: true bottom edge y-coord
pts[2].y--; // ...ditto
pts[3].y--; // correction #2: extend final LineTo
dc.MoveTo(pts[0]); // upper left
dc.LineTo(pts[1]); // bottom left
dc.SelectObject(&shadowPen); // bottom line is shadow color
dc.MoveTo(pts[1]); // line is inside trapezoid bottom
dc.LineTo(pts[2]); // ...
dc.SelectObject(&blackPen); // upstroke is black
dc.LineTo(pts[3]); // y-1 to include endpoint
if (!bSelected)
{
// if not highlighted, upstroke has a 3D shadow, one pixel inside
pts[2].x--; // offset left one pixel
pts[3].x--; // ...ditto
dc.SelectObject(&shadowPen);
dc.MoveTo(pts[2]);
dc.LineTo(pts[3]);
}
dc.SelectObject(pOldPen);
// draw text
rc.DeflateRect(CXOFFSET + CXMARGIN, CYMARGIN);
CFont* pOldFont = dc.SelectObject(&font);
dc.DrawText(m_sText, &rc, DT_CENTER|DT_VCENTER|DT_SINGLELINE);
dc.SelectObject(pOldFont);
return m_rect.right;
}
//////////////////////////////////////////////////////////////////
// CFolderTabCtrl
IMPLEMENT_DYNAMIC(CFolderTabCtrl, CWnd)
BEGIN_MESSAGE_MAP(CFolderTabCtrl, CWnd)
ON_WM_CREATE()
ON_WM_PAINT()
ON_WM_SIZE()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONDBLCLK()
ON_WM_RBUTTONDOWN()
ON_BN_CLICKED(FTBFIRST,OnFirstTab)
ON_BN_CLICKED(FTBPREV,OnPrevTab)
ON_BN_CLICKED(FTBNEXT,OnNextTab)
ON_BN_CLICKED(FTBLAST,OnLastTab)
ON_EN_KILLFOCUS(EditBoxId,OnEditBoxKillFocus)
ON_COMMAND(ID_EDIT_SHEETS,OnEditProperties)
ON_COMMAND(ID_EDIT_ADDSHEET,OnEditAddSheet)
ON_COMMAND(ID_EDIT_DELETESHEET,OnEditDeleteSheet)
ON_COMMAND(ID_EDIT_RENAMESHEET,OnEditRenameSheet)
END_MESSAGE_MAP()
CFolderTabCtrl::CFolderTabCtrl()
{
m_iCurItem =
m_dwFtabStyle =
m_cxDesired =
m_cxButtons =
m_iFirstTab = 0;
m_editBox = NULL;
}
CFolderTabCtrl::~CFolderTabCtrl()
{
while (!m_lsTabs.IsEmpty())
delete (CFolderTab*)m_lsTabs.RemoveHead();
if (m_editBox)
delete m_editBox;
}
//////////////////
// Create folder tab control from static control.
// Destroys the static control. This is convenient for dialogs
//
BOOL CFolderTabCtrl::CreateFromStatic(UINT nID, CWnd* pParent)
{
CStatic wndStatic;
if (!wndStatic.SubclassDlgItem(nID, pParent))
return FALSE;
CRect rc;
wndStatic.GetWindowRect(&rc);
pParent->ScreenToClient(&rc);
wndStatic.DestroyWindow();
rc.bottom = rc.top + GetDesiredHeight();
return Create(WS_CHILD|WS_VISIBLE, rc, pParent, nID);
}
//////////////////
// Create folder tab control.
//
BOOL CFolderTabCtrl::Create(DWORD dwStyle, const RECT& rc,
CWnd* pParent, UINT nID, DWORD dwFtabStyle)
{
ASSERT(pParent);
ASSERT(dwStyle & WS_CHILD);
m_dwFtabStyle = dwFtabStyle;
static LPCTSTR lpClassName = _T("PDFolderTab");
static BOOL bRegistered = FALSE; // registered?
if (!bRegistered)
{
WNDCLASS wc;
memset(&wc, 0, sizeof(wc));
wc.style = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS | CS_DBLCLKS;
wc.lpfnWndProc = (WNDPROC)::DefWindowProc; // will get hooked by MFC
wc.hInstance = AfxGetInstanceHandle();
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = CreateSolidBrush(GetSysColor(COLOR_3DFACE));
wc.lpszMenuName = NULL;
wc.lpszClassName = lpClassName;
if (!AfxRegisterClass(&wc))
{
TRACE("*** CFolderTabCtrl::AfxRegisterClass failed!\n");
return FALSE;
}
bRegistered = TRUE;
}
if (!CWnd::CreateEx(0, lpClassName, NULL, dwStyle, rc, pParent, nID))
return FALSE;
// initialize fonts
LOGFONT lf;
memset(&lf, 0, sizeof(lf));
lf.lfHeight = GetSystemMetrics(SM_CYHSCROLL)-CYMARGIN;
lf.lfWeight = FW_NORMAL;
lf.lfCharSet = DEFAULT_CHARSET;
_tcscpy(lf.lfFaceName, _T("Arial"));
m_fontNormal.CreateFontIndirect(&lf);
// Lynn McGuire 9/19/03 dont make the selected font smaller
// lf.lfHeight -= 2;
// Lynn McGuire 9/19/03 make the selected font bold like Excel
lf.lfWeight = FW_BOLD;
m_fontSelected.CreateFontIndirect(&lf);
return TRUE;
}
LPCTSTR CFolderTabCtrl::GetItemText(int iItem)
{
CFolderTab* pft = GetTab(iItem);
return pft ? pft->GetText() : NULL;
}
void CFolderTabCtrl::SetItemText(int iItem, LPCTSTR lpText)
{
CFolderTab* pft = GetTab(iItem);
if (pft) {
pft->SetText(lpText);
}
}
//////////////////
// copy a font
//
static void CopyFont(CFont& dst, CFont& src)
{
dst.DeleteObject();
LOGFONT lf;
VERIFY(src.GetLogFont(&lf));
dst.CreateFontIndirect(&lf);
}
//////////////////
// Set normal, selected fonts
//
void CFolderTabCtrl::SetFonts(CFont& fontNormal, CFont& fontSelected)
{
CopyFont(m_fontNormal, fontNormal);
CopyFont(m_fontSelected, fontSelected);
}
//////////////////
// Paint function
//
void CFolderTabCtrl::OnPaint()
{
CPaintDC dc(this); // device context for painting
CFolderTab* firstTab = GetTab(m_iFirstTab);
int xOrigin = m_cxButtons;
if (firstTab)
xOrigin -= firstTab->GetRect().left;
dc.SetViewportOrg(xOrigin,0);
CRect rc;
GetClientRect(&rc);
CFolderTab* pCurTab = NULL;
// draw all the normal (non-selected) tabs
int n = GetItemCount();
for (int i=0; i<n; i++)
{
CFolderTab* pTab = GetTab(i);
ASSERT(pTab);
if (i==m_iCurItem) {
pCurTab = pTab;
} else {
pTab->Draw(dc, m_fontNormal, FALSE);
}
}
// draw selected tab last so it will be "on top" of the others
if (pCurTab)
pCurTab->Draw(dc, m_fontSelected, TRUE);
// draw border: line along the top edge, excluding selected tab
CRect rcCurTab(0,0,0,0);
if (pCurTab)
rcCurTab = pCurTab->GetRect();
rc.right -= xOrigin;
CPen blackPen(PS_SOLID, 1, RGB(0,0,0)); // black
CPen* pOldPen = dc.SelectObject(&blackPen);
dc.MoveTo(rcCurTab.right, rcCurTab.top);
dc.LineTo(rc.right, rc.top);
if (m_dwFtabStyle & FTS_FULLBORDER)
{
dc.MoveTo(rc.right-1, rc.top);
dc.LineTo(rc.right-1, rc.bottom-1);
dc.LineTo(rc.left, rc.bottom-1);
dc.LineTo(rc.left, rc.top);
} else {
dc.MoveTo(rc.left, rc.top);
}
dc.LineTo(rcCurTab.TopLeft());
dc.SelectObject(pOldPen);
}
//////////////////
// Handle mouse click: select new tab, if any. Notify parent, of course
//
void CFolderTabCtrl::OnLButtonDown(UINT nFlags, CPoint pt)
{
int iTab = HitTest(pt);
if (iTab>=0 && iTab!=m_iCurItem)
{
SelectItem(iTab);
NMFOLDERTAB nm;
nm.hwndFrom = m_hWnd;
nm.idFrom = GetDlgCtrlID();
nm.code = FTN_TABCHANGED;
nm.iItem = iTab;
CWnd* pParent = GetParent();
pParent->SendMessage(WM_NOTIFY, nm.idFrom, (LPARAM)&nm);
}
}
WNDPROC DefEditBoxProc = NULL;
int UserPressedEnterOnTheEditBox = false;
HWND EditBoxHandle = 0;
LRESULT CALLBACK MyEditBoxProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CHAR:
if ( VK_RETURN == wParam )
{
// User pressed ENTER -- do what you want here
UserPressedEnterOnTheEditBox = true;
// HWND editBoxHandle = GetDlgItem (hDlg, EditBoxId);
SendMessage (EditBoxHandle, WM_CLOSE, 0, 0);
return 0;
}
else if ( VK_ESCAPE == wParam )
{
// User pressed ESCAPE -- do what you want here
UserPressedEnterOnTheEditBox = false;
// HWND editBoxHandle = GetDlgItem (hDlg, EditBoxId);
SendMessage (EditBoxHandle, WM_CLOSE, 0, 0);
return 0;
}
else
return (LRESULT) CallWindowProc
(DefEditBoxProc, hDlg, message, wParam, lParam);
break;
default:
return (LRESULT) CallWindowProc
(DefEditBoxProc, hDlg, message, wParam, lParam);
break;
}
return(0);
}
//////////////////
// Handle mouse click: select new tab, if any. Notify parent, of course
//
void CFolderTabCtrl::OnLButtonDblClk(UINT nFlags, CPoint pt)
{
int iTab = HitTest(pt);
if (iTab >= 0)
{
SelectItem (iTab);
OnEditRenameSheet ();
}
}
void CFolderTabCtrl::OnEditBoxKillFocus()
{
//MessageBox ("CFolderTabCtrl::OnEditBoxKillFocus - got here !");
if (m_editBox)
{
if (UserPressedEnterOnTheEditBox)
{
char buffer [4096];
m_editBox -> GetWindowText (buffer, sizeof (buffer) - 1);
CFolderTab * pTab = GetTab (m_iCurItem);
pTab -> SetText (buffer);
Invalidate ();
RecomputeLayout ();
NMFOLDERTAB nm;
nm.hwndFrom = m_hWnd;
nm.idFrom = GetDlgCtrlID ();
nm.code = FTN_TABNAMECHANGED;
nm.iItem = m_iCurItem;
nm.lpText = buffer;
CWnd* pParent = GetParent ();
pParent -> Invalidate ();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -