📄 browsectrl.cpp
字号:
///////////////////////////////////////////////////////////////////////////
// BrowseCtrl.cpp
//
// CBrowseCtrl is a CButton derived control used to let the user browse for
// files or folders on their systems.
//
// CBrowseCtrl integrates an edit-box and a browse-button together so the
// develops only need to maintain one control in their projects instead of two.
// Moreover, CBrowseCtrl draws its own "build-in" images on the browse-button
// without requiring the developers to include any extra icon, bitmap or other
// image resources.
//
// Written by Abin (abinn32@yahoo.com)
//
// History:
//
// Jan 08, 2004 - Initial public release.
// Jan 09, 2004 - 1, Improved drawing functions to properly handle the case in
// which the control client area is too small to be drawn.
// In debug mode it will provide a message box to inform the
// developer about that issue.
// 2, Improved the edit box so it acts better upon receiving and
// losing the input focus.
// Jan 10, 2004 - 1, Fixed a problem where the edit bix is not properly redrawn
// in some mouse events. Thanks to JOHN11.
// 2, Added method "GetSelectedCount" which returns the number of
// items the user has selected in the most recent file/folder
// dialog that was terminated by IDOK.
// 3, Improved the mouse/focus events monitoring functions.
// 4, Fixed a drawing glitch when the user clicks on the edges of
// the edit box.
// 5, Changed the drawing rects calculating methods for performance
// improvement.
// Jan 14, 2004 - 1, Updated "SetPathName" and "GetPathName" member functions.
// 2, Altered the message sending method so the lparam is now a
// pointer to this CBrowseCtrl object.
// Jan 22, 2004 - 1, Added methods to monitor whether the user has manually changed
// the contents of the edit box when BC_CTL_ALLOWEDIT is set. The
// return value of "GetPathName" will also be properly affected.
// 2, The window titles of file/folder dialogs can now be accessed
// by calling "SetDialogTitle" and "GetDialogTitle".
// 3, The banner text of folder dialogs can now be accessed by calling
// "SetDialogBanner" and "GetDialogBanner".
// 4, Added method "ModifyButtonStyle" to allow convenient style changing.
// Feb 07, 2004 - 1, Improved drawing functions so that images/text are partially drawn
// if there is not enough space.
//
///////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "BrowseCtrl.h"
#include <afxdlgs.h>
#include <string.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define ICON_WIDTH 16
#define ICON_HEIGHT 16
#define BTN_EDGE 2
#define WIDTH_SPACE (BTN_EDGE * 2 + 7)
#define EDITCTRL_ID 12345
#define TOOLTIPCTRL_ID 12346
#define ID_TMR_MOUSEMONITOR 12347
#define ID_TMR_FOCUSMONITOR 12348
// color defines
#define BC_COLOR_WHITE RGB(255, 255, 255)
#define BC_COLOR_BLACK RGB(0, 0, 0)
#define BC_COLOR_DARK RGB(128, 128, 128)
#define BC_COLOR_GRAY RGB(192, 192, 192)
#define BC_COLOR_YELLOW RGB(255, 255, 0)
#define BC_COLOR_BLUE RGB(0, 0, 255)
#define BC_COLOR_OLIVE RGB(128, 128, 0)
#define BC_COLOR_CYAN RGB(0, 255, 255)
#define BC_COLOR_NAVY RGB(0, 0, 128)
// maximum buffer length for retrieving pathname
#define MAX_SUM_PATHNAME 65535
// "CLineDraw" class is used for helping on drawing lines, the default behavior of
// CDC::LineTo on not drawing the last pixel could be extremely inconvenient in
// certain cases.
class CLineDraw
{
public:
// Constructor
CLineDraw(CDC* pDC, long lInitX = 0, long lInitY = 0)
{
ASSERT(pDC != NULL);
m_pDC = pDC;
m_ptCur.x = lInitX;
m_ptCur.y = lInitY;
m_ptInit.x = lInitX;
m_ptInit.y = lInitY;
}
// Set the point the top-lect corner of the rect, offset the point,
// then move dc to that point
void InitOffsetMoveTo(long x, long y)
{
m_ptCur = m_ptInit;
m_ptCur.Offset(x, y);
m_pDC->MoveTo(m_ptCur);
}
// Offset the point and move dc to the point
void OffsetMoveTo(long x, long y)
{
m_ptCur.Offset(x, y);
m_pDC->MoveTo(m_ptCur);
}
// Offset the point and draw a line to the point, then set pixel at the point
// using the color of current pen stored in the dc
void OffsetLineTo(long x, long y)
{
m_ptCur.Offset(x, y);
m_pDC->LineTo(m_ptCur);
m_pDC->GetCurrentPen()->GetLogPen(&m_lgp);
m_pDC->SetPixel(m_ptCur, m_lgp.lopnColor);
}
private:
CDC* m_pDC;
CPoint m_ptCur;
LOGPEN m_lgp;
CPoint m_ptInit;
};
/////////////////////////////////////////////////////////////////////////////
// CBrowseCtrl Implementations
/////////////////////////////////////////////////////////////////////////////
CBrowseCtrl::CBrowseCtrl()
{
m_dwStyle = BC_BTN_ICON | BC_ICO_ARROWFOLDER; // By default, the "arrow folder" icon is drawn on the browse button
m_nSelCount = 0;
m_bEditFocused = FALSE;
m_nPathNamesLen = 0;
m_bDlgPopped = FALSE;
m_nNotifyMsg = 0;
m_bOwnCreate = FALSE;
m_bBtnPushed = FALSE;
m_bMouseIn = FALSE;
m_sTootipText = _T("浏览");
m_sDlgBanner = _T("请从列表中选择文件:");
m_bReadOnlyRef = FALSE;
m_bOpenFileDialog = TRUE;
m_dwFileFlags = OFN_ALLOWMULTISELECT | OFN_OVERWRITEPROMPT | OFN_CREATEPROMPT | OFN_ENABLESIZING | OFN_EXPLORER;
m_nFolderFlags = BIF_RETURNONLYFSDIRS;
m_sFilter = _T("All Files (*.*)|*.*||");
m_lpszPathNames = NULL;
m_bEditChanged = FALSE;
}
CBrowseCtrl::~CBrowseCtrl()
{
if (m_lpszPathNames != NULL)
delete [] m_lpszPathNames;
}
BEGIN_MESSAGE_MAP(CBrowseCtrl, CButton)
//{{AFX_MSG_MAP(CBrowseCtrl)
ON_WM_PAINT()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_WM_TIMER()
ON_WM_DESTROY()
ON_WM_MOUSEMOVE()
ON_WM_SETFOCUS()
ON_WM_MOVE()
ON_WM_SIZE()
ON_WM_ENABLE()
ON_WM_KEYDOWN()
ON_WM_KEYUP()
//}}AFX_MSG_MAP
ON_EN_CHANGE(EDITCTRL_ID, OnChangeEdit)
END_MESSAGE_MAP()
void CBrowseCtrl::OnChangeEdit()
{
if (!m_bDlgPopped && (m_dwStyle & BC_CTL_ALLOWEDIT))
{
// The user has changed the edit box contents manually
m_wndEdit.GetWindowText(m_sEditText);
m_bEditChanged = TRUE;
}
}
void CBrowseCtrl::PreSubclassWindow()
{
// TODO: Add your specialized code here and/or call the base class
CButton::PreSubclassWindow();
if (!m_bOwnCreate)
CreateCtrl(); // only create the edit/tooltip control once!
}
void CBrowseCtrl::SetButtonStyle(DWORD dwStyles)
{
// Change the control style
if (m_dwStyle != dwStyles)
{
m_dwStyle = dwStyles;
if (::IsWindow(m_hWnd))
{
RecalculateRects();
CreateEdit();
RedrawWindow();
}
}
}
DWORD CBrowseCtrl::GetButtonStyle() const
{
return m_dwStyle;
}
BOOL CBrowseCtrl::CreateCtrl()
{
if (!::IsWindow(m_hWnd))
return FALSE;
RecalculateRects();
// Move window text from actual window to the fake button
if (m_sButtonText.IsEmpty())
CButton::GetWindowText(m_sButtonText);
CButton::SetWindowText(_T(""));
if (!CreateEdit())
{
ASSERT(FALSE);
return FALSE;
}
// Create tooltip control
if (!m_wndTooltip.Create(this))
{
ASSERT(FALSE);
return FALSE;
}
m_wndTooltip.AddTool(this, m_sTootipText, m_rcButton, TOOLTIPCTRL_ID);
SetTimer(ID_TMR_FOCUSMONITOR, 1, NULL);
return TRUE;
}
void CBrowseCtrl::OnPaint()
{
CPaintDC dc(this); // device context for painting
COLORREF bkColor = ::GetSysColor(COLOR_BTNFACE);
// TODO: Add your message handler code here
DrawButtonFrame(&dc, bkColor);
// Draw text or different images on the browse button
CRect rect = m_rcContent;
// If the button is pushed down, make it look sunken.
if (m_bBtnPushed)
rect.OffsetRect(1, 1);
if (m_dwStyle & BC_BTN_ICON)
{
if (m_dwStyle & BC_ICO_ARROWFOLDER)
{
DrawButtonArrowFolder(&dc, bkColor, rect);
}
else if (m_dwStyle & BC_ICO_FOLDER)
{
DrawButtonFolder(&dc, bkColor, rect);
}
else if (m_dwStyle & BC_ICO_EXPLORER)
{
DrawButtonExplorer(&dc, bkColor, rect);
}
else
{
// Do we have any other stuff to draw?
}
}
else
{
DrawButtonText(&dc, bkColor, rect);
}
}
void CBrowseCtrl::OnSetFocus(CWnd* pOldWnd)
{
KillTimer(ID_TMR_FOCUSMONITOR);
if (!m_bEditFocused && pOldWnd != &m_wndEdit)
{
CButton::OnSetFocus(pOldWnd);
m_wndEdit.SetFocus();
m_wndEdit.SetSel(0, -1);
m_bEditFocused = TRUE;
}
SetTimer(ID_TMR_FOCUSMONITOR, 1, NULL);
}
void CBrowseCtrl::OnSetEditFocus()
{
m_wndEdit.SetSel(0, -1);
}
void CBrowseCtrl::OnKillEditFocus()
{
m_wndEdit.SetSel(-1, -1);
}
int CBrowseCtrl::DoModal()
{
// Popup the file/folder dialog
if (m_bDlgPopped)
return IDCANCEL;
m_bDlgPopped = TRUE;
// Either popup a file dialog, or a folder dialog
int nRes = (m_dwStyle & BC_CTL_FOLDERSONLY) ? FolderDoModal() : FileDoModal();
if (m_nNotifyMsg > 0)
GetParent()->SendMessage(m_nNotifyMsg, (WPARAM)nRes, (LPARAM)this);
m_bDlgPopped = FALSE;
return nRes;
}
int CBrowseCtrl::GetSelectedCount() const
{
return m_nSelCount;
}
void CBrowseCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CButton::OnLButtonDown(nFlags, point);
if (m_rcButton.PtInRect(point))
{
OnKillEditFocus();
SetCapture();
m_bBtnPushed = TRUE;
}
RedrawWindow();
}
void CBrowseCtrl::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CButton::OnLButtonUp(nFlags, point);
if (GetCapture() == this)
{
ReleaseCapture();
m_bBtnPushed = FALSE;
RedrawWindow(&m_rcButton);
if (m_rcButton.PtInRect(point))
OnButtonClicked();
}
}
void CBrowseCtrl::DrawButtonFrame(CDC *pDC, COLORREF bkColor) const
{
CRect rect = m_rcButton;
pDC->FillSolidRect(&rect, bkColor);
if (m_dwStyle & BC_BTN_FLAT)
{
if (m_bBtnPushed)
{
pDC->Draw3dRect(rect, ::GetSysColor(COLOR_3DDKSHADOW), ::GetSysColor(COLOR_3DHIGHLIGHT));
}
else
{
if (m_bMouseIn)
pDC->Draw3dRect(rect, ::GetSysColor(COLOR_3DHIGHLIGHT), ::GetSysColor(COLOR_3DSHADOW));
}
}
else
{
if (!m_bBtnPushed)
{
pDC->Draw3dRect(rect, ::GetSysColor(COLOR_3DHIGHLIGHT), ::GetSysColor(COLOR_3DDKSHADOW));
rect.DeflateRect(1, 1);
pDC->Draw3dRect(rect, ::GetSysColor(COLOR_3DLIGHT), ::GetSysColor(COLOR_3DSHADOW));
}
else
{
pDC->Draw3dRect(rect, ::GetSysColor(COLOR_3DDKSHADOW), ::GetSysColor(COLOR_3DDKSHADOW));
rect.DeflateRect(1, 1);
pDC->Draw3dRect(rect, ::GetSysColor(COLOR_3DSHADOW), ::GetSysColor(COLOR_3DSHADOW));
}
}
}
BOOL CBrowseCtrl::PreTranslateMessage(MSG* pMsg)
{
// TODO: Add your specialized code here and/or call the base class
m_wndTooltip.RelayEvent(pMsg);
// Popup the file/folder dialog when user hits "Enter" key when the
// control window is active
if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN)
{
OnButtonClicked();
return TRUE;
}
return CButton::PreTranslateMessage(pMsg);
}
void CBrowseCtrl::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
if (nIDEvent == ID_TMR_MOUSEMONITOR)
{
// monitors mouse enter/leave events
if (m_bMouseIn)
{
POINT pt;
::GetCursorPos(&pt);
CRect rect = m_rcButton;
ClientToScreen(&rect);
if (!rect.PtInRect(pt))
{
KillTimer(ID_TMR_MOUSEMONITOR);
m_bMouseIn = FALSE;
OnMouseLeave();
}
}
}
else if (nIDEvent == ID_TMR_FOCUSMONITOR)
{
// monitors control focus events
if (GetFocus() != &m_wndEdit)
{
if (m_bEditFocused)
{
m_bEditFocused = FALSE;
OnKillEditFocus();
}
}
else
{
if (!m_bEditFocused)
{
m_bEditFocused = TRUE;
OnSetEditFocus();
}
}
}
CButton::OnTimer(nIDEvent);
}
void CBrowseCtrl::OnDestroy()
{
KillTimer(ID_TMR_MOUSEMONITOR);
KillTimer(ID_TMR_FOCUSMONITOR);
m_wndEdit.DestroyWindow();
m_wndTooltip.DestroyWindow();
CButton::OnDestroy();
}
void CBrowseCtrl::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CButton::OnMouseMove(nFlags, point);
if (!m_bMouseIn && m_rcButton.PtInRect(point))
{
m_bMouseIn = TRUE;
if (GetCapture() == this)
m_bBtnPushed = TRUE;
SetTimer(ID_TMR_MOUSEMONITOR, 1, NULL);
OnMouseEnter();
}
}
void CBrowseCtrl::OnMouseEnter()
{
RedrawWindow(&m_rcButton);
}
void CBrowseCtrl::OnMouseLeave()
{
m_bBtnPushed = FALSE;
RedrawWindow();
}
void CBrowseCtrl::SetTooltipText(LPCTSTR lpszText)
{
m_sTootipText = lpszText;
if (::IsWindow(m_hWnd))
m_wndTooltip.UpdateTipText(lpszText, this, TOOLTIPCTRL_ID);
}
void CBrowseCtrl::SetWindowText(LPCTSTR lpszString)
{
SetButtonText(lpszString);
}
int CBrowseCtrl::GetWindowText(LPTSTR lpszStringBuf, int nMaxCount) const
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -