You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3271 lines
85 KiB
3271 lines
85 KiB
// WTL Version 3.1
|
|
// Copyright (C) 1997-2000 Microsoft Corporation
|
|
// All rights reserved.
|
|
//
|
|
// This file is a part of Windows Template Library.
|
|
// The code and information is provided "as-is" without
|
|
// warranty of any kind, either expressed or implied.
|
|
|
|
#ifndef __ATLMISC_H__
|
|
#define __ATLMISC_H__
|
|
|
|
#pragma once
|
|
|
|
#ifndef __cplusplus
|
|
#error ATL requires C++ compilation (use a .cpp suffix)
|
|
#endif
|
|
|
|
#ifndef __ATLAPP_H__
|
|
#error atlmisc.h requires atlapp.h to be included first
|
|
#endif
|
|
|
|
|
|
#ifndef _WTL_NO_WTYPES
|
|
#define __ATLTYPES_H__
|
|
#endif //!_WTL_NO_WTYPES
|
|
|
|
#ifdef _ATL_TMP_NO_CSTRING
|
|
#define _WTL_NO_CSTRING
|
|
#endif
|
|
|
|
#ifndef _WTL_NO_CSTRING
|
|
#define __ATLSTR_H__
|
|
#if defined(_ATL_USE_CSTRING_FLOAT) && defined(_ATL_MIN_CRT)
|
|
#error Cannot use CString floating point formatting with _ATL_MIN_CRT defined
|
|
#endif //defined(_ATL_USE_CSTRING_FLOAT) && defined(_ATL_MIN_CRT)
|
|
#ifndef _DEBUG
|
|
#include <stdio.h>
|
|
#endif //!_DEBUG
|
|
#endif //!_WTL_NO_CSTRING
|
|
|
|
|
|
namespace WTL
|
|
{
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Forward declarations
|
|
|
|
#ifndef _WTL_NO_WTYPES
|
|
class CSize;
|
|
class CPoint;
|
|
class CRect;
|
|
#endif //!_WTL_NO_WTYPES
|
|
#ifndef _WTL_NO_CSTRING
|
|
class CString;
|
|
#endif //!_WTL_NO_CSTRING
|
|
template <class T, int t_cchItemLen> class CRecentDocumentListBase;
|
|
class CRecentDocumentList;
|
|
class CFindFile;
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CSize - Wrapper for Windows SIZE structure.
|
|
|
|
#ifndef _WTL_NO_WTYPES
|
|
|
|
class CSize : public tagSIZE
|
|
{
|
|
public:
|
|
// Constructors
|
|
CSize();
|
|
CSize(int initCX, int initCY);
|
|
CSize(SIZE initSize);
|
|
CSize(POINT initPt);
|
|
CSize(DWORD dwSize);
|
|
|
|
// Operations
|
|
BOOL operator==(SIZE size) const;
|
|
BOOL operator!=(SIZE size) const;
|
|
void operator+=(SIZE size);
|
|
void operator-=(SIZE size);
|
|
void SetSize(int CX, int CY);
|
|
|
|
// Operators returning CSize values
|
|
CSize operator+(SIZE size) const;
|
|
CSize operator-(SIZE size) const;
|
|
CSize operator-() const;
|
|
|
|
// Operators returning CPoint values
|
|
CPoint operator+(POINT point) const;
|
|
CPoint operator-(POINT point) const;
|
|
|
|
// Operators returning CRect values
|
|
CRect operator+(const RECT* lpRect) const;
|
|
CRect operator-(const RECT* lpRect) const;
|
|
};
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CPoint - Wrapper for Windows POINT structure.
|
|
|
|
class CPoint : public tagPOINT
|
|
{
|
|
public:
|
|
// Constructors
|
|
CPoint();
|
|
CPoint(int initX, int initY);
|
|
CPoint(POINT initPt);
|
|
CPoint(SIZE initSize);
|
|
CPoint(DWORD dwPoint);
|
|
|
|
// Operations
|
|
void Offset(int xOffset, int yOffset);
|
|
void Offset(POINT point);
|
|
void Offset(SIZE size);
|
|
BOOL operator==(POINT point) const;
|
|
BOOL operator!=(POINT point) const;
|
|
void operator+=(SIZE size);
|
|
void operator-=(SIZE size);
|
|
void operator+=(POINT point);
|
|
void operator-=(POINT point);
|
|
void SetPoint(int X, int Y);
|
|
|
|
// Operators returning CPoint values
|
|
CPoint operator+(SIZE size) const;
|
|
CPoint operator-(SIZE size) const;
|
|
CPoint operator-() const;
|
|
CPoint operator+(POINT point) const;
|
|
|
|
// Operators returning CSize values
|
|
CSize operator-(POINT point) const;
|
|
|
|
// Operators returning CRect values
|
|
CRect operator+(const RECT* lpRect) const;
|
|
CRect operator-(const RECT* lpRect) const;
|
|
};
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CRect - Wrapper for Windows RECT structure.
|
|
|
|
class CRect : public tagRECT
|
|
{
|
|
public:
|
|
// Constructors
|
|
CRect();
|
|
CRect(int l, int t, int r, int b);
|
|
CRect(const RECT& srcRect);
|
|
CRect(LPCRECT lpSrcRect);
|
|
CRect(POINT point, SIZE size);
|
|
CRect(POINT topLeft, POINT bottomRight);
|
|
|
|
// Attributes (in addition to RECT members)
|
|
int Width() const;
|
|
int Height() const;
|
|
CSize Size() const;
|
|
CPoint& TopLeft();
|
|
CPoint& BottomRight();
|
|
const CPoint& TopLeft() const;
|
|
const CPoint& BottomRight() const;
|
|
CPoint CenterPoint() const;
|
|
|
|
// convert between CRect and LPRECT/LPCRECT (no need for &)
|
|
operator LPRECT();
|
|
operator LPCRECT() const;
|
|
|
|
BOOL IsRectEmpty() const;
|
|
BOOL IsRectNull() const;
|
|
BOOL PtInRect(POINT point) const;
|
|
|
|
// Operations
|
|
void SetRect(int x1, int y1, int x2, int y2);
|
|
void SetRect(POINT topLeft, POINT bottomRight);
|
|
void SetRectEmpty();
|
|
void CopyRect(LPCRECT lpSrcRect);
|
|
BOOL EqualRect(LPCRECT lpRect) const;
|
|
|
|
void InflateRect(int x, int y);
|
|
void InflateRect(SIZE size);
|
|
void InflateRect(LPCRECT lpRect);
|
|
void InflateRect(int l, int t, int r, int b);
|
|
void DeflateRect(int x, int y);
|
|
void DeflateRect(SIZE size);
|
|
void DeflateRect(LPCRECT lpRect);
|
|
void DeflateRect(int l, int t, int r, int b);
|
|
|
|
void OffsetRect(int x, int y);
|
|
void OffsetRect(SIZE size);
|
|
void OffsetRect(POINT point);
|
|
void NormalizeRect();
|
|
|
|
// absolute position of rectangle
|
|
void MoveToY(int y);
|
|
void MoveToX(int x);
|
|
void MoveToXY(int x, int y);
|
|
void MoveToXY(POINT point);
|
|
|
|
// operations that fill '*this' with result
|
|
BOOL IntersectRect(LPCRECT lpRect1, LPCRECT lpRect2);
|
|
BOOL UnionRect(LPCRECT lpRect1, LPCRECT lpRect2);
|
|
BOOL SubtractRect(LPCRECT lpRectSrc1, LPCRECT lpRectSrc2);
|
|
|
|
// Additional Operations
|
|
void operator=(const RECT& srcRect);
|
|
BOOL operator==(const RECT& rect) const;
|
|
BOOL operator!=(const RECT& rect) const;
|
|
void operator+=(POINT point);
|
|
void operator+=(SIZE size);
|
|
void operator+=(LPCRECT lpRect);
|
|
void operator-=(POINT point);
|
|
void operator-=(SIZE size);
|
|
void operator-=(LPCRECT lpRect);
|
|
void operator&=(const RECT& rect);
|
|
void operator|=(const RECT& rect);
|
|
|
|
// Operators returning CRect values
|
|
CRect operator+(POINT point) const;
|
|
CRect operator-(POINT point) const;
|
|
CRect operator+(LPCRECT lpRect) const;
|
|
CRect operator+(SIZE size) const;
|
|
CRect operator-(SIZE size) const;
|
|
CRect operator-(LPCRECT lpRect) const;
|
|
CRect operator&(const RECT& rect2) const;
|
|
CRect operator|(const RECT& rect2) const;
|
|
CRect MulDiv(int nMultiplier, int nDivisor) const;
|
|
};
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CSize, CPoint, CRect Implementation
|
|
|
|
// CSize
|
|
inline CSize::CSize()
|
|
{ /* random filled */ }
|
|
inline CSize::CSize(int initCX, int initCY)
|
|
{ cx = initCX; cy = initCY; }
|
|
inline CSize::CSize(SIZE initSize)
|
|
{ *(SIZE*)this = initSize; }
|
|
inline CSize::CSize(POINT initPt)
|
|
{ *(POINT*)this = initPt; }
|
|
inline CSize::CSize(DWORD dwSize)
|
|
{
|
|
cx = (short)LOWORD(dwSize);
|
|
cy = (short)HIWORD(dwSize);
|
|
}
|
|
inline BOOL CSize::operator==(SIZE size) const
|
|
{ return (cx == size.cx && cy == size.cy); }
|
|
inline BOOL CSize::operator!=(SIZE size) const
|
|
{ return (cx != size.cx || cy != size.cy); }
|
|
inline void CSize::operator+=(SIZE size)
|
|
{ cx += size.cx; cy += size.cy; }
|
|
inline void CSize::operator-=(SIZE size)
|
|
{ cx -= size.cx; cy -= size.cy; }
|
|
inline void CSize::SetSize(int CX, int CY)
|
|
{ cx = CX; cy = CY; }
|
|
inline CSize CSize::operator+(SIZE size) const
|
|
{ return CSize(cx + size.cx, cy + size.cy); }
|
|
inline CSize CSize::operator-(SIZE size) const
|
|
{ return CSize(cx - size.cx, cy - size.cy); }
|
|
inline CSize CSize::operator-() const
|
|
{ return CSize(-cx, -cy); }
|
|
inline CPoint CSize::operator+(POINT point) const
|
|
{ return CPoint(cx + point.x, cy + point.y); }
|
|
inline CPoint CSize::operator-(POINT point) const
|
|
{ return CPoint(cx - point.x, cy - point.y); }
|
|
inline CRect CSize::operator+(const RECT* lpRect) const
|
|
{ return CRect(lpRect) + *this; }
|
|
inline CRect CSize::operator-(const RECT* lpRect) const
|
|
{ return CRect(lpRect) - *this; }
|
|
|
|
// CPoint
|
|
inline CPoint::CPoint()
|
|
{ /* random filled */ }
|
|
inline CPoint::CPoint(int initX, int initY)
|
|
{ x = initX; y = initY; }
|
|
inline CPoint::CPoint(POINT initPt)
|
|
{ *(POINT*)this = initPt; }
|
|
inline CPoint::CPoint(SIZE initSize)
|
|
{ *(SIZE*)this = initSize; }
|
|
inline CPoint::CPoint(DWORD dwPoint)
|
|
{
|
|
x = (short)LOWORD(dwPoint);
|
|
y = (short)HIWORD(dwPoint);
|
|
}
|
|
inline void CPoint::Offset(int xOffset, int yOffset)
|
|
{ x += xOffset; y += yOffset; }
|
|
inline void CPoint::Offset(POINT point)
|
|
{ x += point.x; y += point.y; }
|
|
inline void CPoint::Offset(SIZE size)
|
|
{ x += size.cx; y += size.cy; }
|
|
inline BOOL CPoint::operator==(POINT point) const
|
|
{ return (x == point.x && y == point.y); }
|
|
inline BOOL CPoint::operator!=(POINT point) const
|
|
{ return (x != point.x || y != point.y); }
|
|
inline void CPoint::operator+=(SIZE size)
|
|
{ x += size.cx; y += size.cy; }
|
|
inline void CPoint::operator-=(SIZE size)
|
|
{ x -= size.cx; y -= size.cy; }
|
|
inline void CPoint::operator+=(POINT point)
|
|
{ x += point.x; y += point.y; }
|
|
inline void CPoint::operator-=(POINT point)
|
|
{ x -= point.x; y -= point.y; }
|
|
inline void CPoint::SetPoint(int X, int Y)
|
|
{ x = X; y = Y; }
|
|
inline CPoint CPoint::operator+(SIZE size) const
|
|
{ return CPoint(x + size.cx, y + size.cy); }
|
|
inline CPoint CPoint::operator-(SIZE size) const
|
|
{ return CPoint(x - size.cx, y - size.cy); }
|
|
inline CPoint CPoint::operator-() const
|
|
{ return CPoint(-x, -y); }
|
|
inline CPoint CPoint::operator+(POINT point) const
|
|
{ return CPoint(x + point.x, y + point.y); }
|
|
inline CSize CPoint::operator-(POINT point) const
|
|
{ return CSize(x - point.x, y - point.y); }
|
|
inline CRect CPoint::operator+(const RECT* lpRect) const
|
|
{ return CRect(lpRect) + *this; }
|
|
inline CRect CPoint::operator-(const RECT* lpRect) const
|
|
{ return CRect(lpRect) - *this; }
|
|
|
|
// CRect
|
|
inline CRect::CRect()
|
|
{ /* random filled */ }
|
|
inline CRect::CRect(int l, int t, int r, int b)
|
|
{ left = l; top = t; right = r; bottom = b; }
|
|
inline CRect::CRect(const RECT& srcRect)
|
|
{ ::CopyRect(this, &srcRect); }
|
|
inline CRect::CRect(LPCRECT lpSrcRect)
|
|
{ ::CopyRect(this, lpSrcRect); }
|
|
inline CRect::CRect(POINT point, SIZE size)
|
|
{ right = (left = point.x) + size.cx; bottom = (top = point.y) + size.cy; }
|
|
inline CRect::CRect(POINT topLeft, POINT bottomRight)
|
|
{ left = topLeft.x; top = topLeft.y;
|
|
right = bottomRight.x; bottom = bottomRight.y; }
|
|
inline int CRect::Width() const
|
|
{ return right - left; }
|
|
inline int CRect::Height() const
|
|
{ return bottom - top; }
|
|
inline CSize CRect::Size() const
|
|
{ return CSize(right - left, bottom - top); }
|
|
inline CPoint& CRect::TopLeft()
|
|
{ return *((CPoint*)this); }
|
|
inline CPoint& CRect::BottomRight()
|
|
{ return *((CPoint*)this + 1); }
|
|
inline const CPoint& CRect::TopLeft() const
|
|
{ return *((CPoint*)this); }
|
|
inline const CPoint& CRect::BottomRight() const
|
|
{ return *((CPoint*)this + 1); }
|
|
inline CPoint CRect::CenterPoint() const
|
|
{ return CPoint((left + right) / 2, (top + bottom) / 2); }
|
|
inline CRect::operator LPRECT()
|
|
{ return this; }
|
|
inline CRect::operator LPCRECT() const
|
|
{ return this; }
|
|
inline BOOL CRect::IsRectEmpty() const
|
|
{ return ::IsRectEmpty(this); }
|
|
inline BOOL CRect::IsRectNull() const
|
|
{ return (left == 0 && right == 0 && top == 0 && bottom == 0); }
|
|
inline BOOL CRect::PtInRect(POINT point) const
|
|
{ return ::PtInRect(this, point); }
|
|
inline void CRect::SetRect(int x1, int y1, int x2, int y2)
|
|
{ ::SetRect(this, x1, y1, x2, y2); }
|
|
inline void CRect::SetRect(POINT topLeft, POINT bottomRight)
|
|
{ ::SetRect(this, topLeft.x, topLeft.y, bottomRight.x, bottomRight.y); }
|
|
inline void CRect::SetRectEmpty()
|
|
{ ::SetRectEmpty(this); }
|
|
inline void CRect::CopyRect(LPCRECT lpSrcRect)
|
|
{ ::CopyRect(this, lpSrcRect); }
|
|
inline BOOL CRect::EqualRect(LPCRECT lpRect) const
|
|
{ return ::EqualRect(this, lpRect); }
|
|
inline void CRect::InflateRect(int x, int y)
|
|
{ ::InflateRect(this, x, y); }
|
|
inline void CRect::InflateRect(SIZE size)
|
|
{ ::InflateRect(this, size.cx, size.cy); }
|
|
inline void CRect::DeflateRect(int x, int y)
|
|
{ ::InflateRect(this, -x, -y); }
|
|
inline void CRect::DeflateRect(SIZE size)
|
|
{ ::InflateRect(this, -size.cx, -size.cy); }
|
|
inline void CRect::OffsetRect(int x, int y)
|
|
{ ::OffsetRect(this, x, y); }
|
|
inline void CRect::OffsetRect(POINT point)
|
|
{ ::OffsetRect(this, point.x, point.y); }
|
|
inline void CRect::OffsetRect(SIZE size)
|
|
{ ::OffsetRect(this, size.cx, size.cy); }
|
|
inline BOOL CRect::IntersectRect(LPCRECT lpRect1, LPCRECT lpRect2)
|
|
{ return ::IntersectRect(this, lpRect1, lpRect2);}
|
|
inline BOOL CRect::UnionRect(LPCRECT lpRect1, LPCRECT lpRect2)
|
|
{ return ::UnionRect(this, lpRect1, lpRect2); }
|
|
inline void CRect::operator=(const RECT& srcRect)
|
|
{ ::CopyRect(this, &srcRect); }
|
|
inline BOOL CRect::operator==(const RECT& rect) const
|
|
{ return ::EqualRect(this, &rect); }
|
|
inline BOOL CRect::operator!=(const RECT& rect) const
|
|
{ return !::EqualRect(this, &rect); }
|
|
inline void CRect::operator+=(POINT point)
|
|
{ ::OffsetRect(this, point.x, point.y); }
|
|
inline void CRect::operator+=(SIZE size)
|
|
{ ::OffsetRect(this, size.cx, size.cy); }
|
|
inline void CRect::operator+=(LPCRECT lpRect)
|
|
{ InflateRect(lpRect); }
|
|
inline void CRect::operator-=(POINT point)
|
|
{ ::OffsetRect(this, -point.x, -point.y); }
|
|
inline void CRect::operator-=(SIZE size)
|
|
{ ::OffsetRect(this, -size.cx, -size.cy); }
|
|
inline void CRect::operator-=(LPCRECT lpRect)
|
|
{ DeflateRect(lpRect); }
|
|
inline void CRect::operator&=(const RECT& rect)
|
|
{ ::IntersectRect(this, this, &rect); }
|
|
inline void CRect::operator|=(const RECT& rect)
|
|
{ ::UnionRect(this, this, &rect); }
|
|
inline CRect CRect::operator+(POINT pt) const
|
|
{ CRect rect(*this); ::OffsetRect(&rect, pt.x, pt.y); return rect; }
|
|
inline CRect CRect::operator-(POINT pt) const
|
|
{ CRect rect(*this); ::OffsetRect(&rect, -pt.x, -pt.y); return rect; }
|
|
inline CRect CRect::operator+(SIZE size) const
|
|
{ CRect rect(*this); ::OffsetRect(&rect, size.cx, size.cy); return rect; }
|
|
inline CRect CRect::operator-(SIZE size) const
|
|
{ CRect rect(*this); ::OffsetRect(&rect, -size.cx, -size.cy); return rect; }
|
|
inline CRect CRect::operator+(LPCRECT lpRect) const
|
|
{ CRect rect(this); rect.InflateRect(lpRect); return rect; }
|
|
inline CRect CRect::operator-(LPCRECT lpRect) const
|
|
{ CRect rect(this); rect.DeflateRect(lpRect); return rect; }
|
|
inline CRect CRect::operator&(const RECT& rect2) const
|
|
{ CRect rect; ::IntersectRect(&rect, this, &rect2);
|
|
return rect; }
|
|
inline CRect CRect::operator|(const RECT& rect2) const
|
|
{ CRect rect; ::UnionRect(&rect, this, &rect2);
|
|
return rect; }
|
|
inline BOOL CRect::SubtractRect(LPCRECT lpRectSrc1, LPCRECT lpRectSrc2)
|
|
{ return ::SubtractRect(this, lpRectSrc1, lpRectSrc2); }
|
|
|
|
inline void CRect::NormalizeRect()
|
|
{
|
|
int nTemp;
|
|
if (left > right)
|
|
{
|
|
nTemp = left;
|
|
left = right;
|
|
right = nTemp;
|
|
}
|
|
if (top > bottom)
|
|
{
|
|
nTemp = top;
|
|
top = bottom;
|
|
bottom = nTemp;
|
|
}
|
|
}
|
|
|
|
inline void CRect::MoveToY(int y)
|
|
{ bottom = Height() + y; top = y; }
|
|
inline void CRect::MoveToX(int x)
|
|
{ right = Width() + x; left = x; }
|
|
inline void CRect::MoveToXY(int x, int y)
|
|
{ MoveToX(x); MoveToY(y); }
|
|
inline void CRect::MoveToXY(POINT pt)
|
|
{ MoveToX(pt.x); MoveToY(pt.y); }
|
|
|
|
inline void CRect::InflateRect(LPCRECT lpRect)
|
|
{
|
|
left -= lpRect->left;
|
|
top -= lpRect->top;
|
|
right += lpRect->right;
|
|
bottom += lpRect->bottom;
|
|
}
|
|
|
|
inline void CRect::InflateRect(int l, int t, int r, int b)
|
|
{
|
|
left -= l;
|
|
top -= t;
|
|
right += r;
|
|
bottom += b;
|
|
}
|
|
|
|
inline void CRect::DeflateRect(LPCRECT lpRect)
|
|
{
|
|
left += lpRect->left;
|
|
top += lpRect->top;
|
|
right -= lpRect->right;
|
|
bottom -= lpRect->bottom;
|
|
}
|
|
|
|
inline void CRect::DeflateRect(int l, int t, int r, int b)
|
|
{
|
|
left += l;
|
|
top += t;
|
|
right -= r;
|
|
bottom -= b;
|
|
}
|
|
|
|
inline CRect CRect::MulDiv(int nMultiplier, int nDivisor) const
|
|
{
|
|
return CRect(
|
|
::MulDiv(left, nMultiplier, nDivisor),
|
|
::MulDiv(top, nMultiplier, nDivisor),
|
|
::MulDiv(right, nMultiplier, nDivisor),
|
|
::MulDiv(bottom, nMultiplier, nDivisor));
|
|
}
|
|
|
|
#endif //!_WTL_NO_WTYPES
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CString - String class
|
|
|
|
#ifndef _WTL_NO_CSTRING
|
|
|
|
struct CStringData
|
|
{
|
|
long nRefs; // reference count
|
|
int nDataLength;
|
|
int nAllocLength;
|
|
// TCHAR data[nAllocLength]
|
|
|
|
TCHAR* data()
|
|
{ return (TCHAR*)(this + 1); }
|
|
};
|
|
|
|
// Globals
|
|
|
|
// For an empty string, m_pchData will point here
|
|
// (note: avoids special case of checking for NULL m_pchData)
|
|
// empty string data (and locked)
|
|
_declspec(selectany) int rgInitData[] = { -1, 0, 0, 0 };
|
|
_declspec(selectany) CStringData* _atltmpDataNil = (CStringData*)&rgInitData;
|
|
_declspec(selectany) LPCTSTR _atltmpPchNil = (LPCTSTR)(((BYTE*)&rgInitData) + sizeof(CStringData));
|
|
|
|
|
|
class CString
|
|
{
|
|
public:
|
|
// Constructors
|
|
CString();
|
|
CString(const CString& stringSrc);
|
|
CString(TCHAR ch, int nRepeat = 1);
|
|
CString(LPCSTR lpsz);
|
|
CString(LPCWSTR lpsz);
|
|
CString(LPCTSTR lpch, int nLength);
|
|
CString(const unsigned char* psz);
|
|
|
|
// Attributes & Operations
|
|
// as an array of characters
|
|
int GetLength() const;
|
|
BOOL IsEmpty() const;
|
|
void Empty(); // free up the data
|
|
|
|
TCHAR GetAt(int nIndex) const; // 0 based
|
|
TCHAR operator[](int nIndex) const; // same as GetAt
|
|
void SetAt(int nIndex, TCHAR ch);
|
|
operator LPCTSTR() const; // as a C string
|
|
|
|
// overloaded assignment
|
|
const CString& operator=(const CString& stringSrc);
|
|
const CString& operator=(TCHAR ch);
|
|
#ifdef _UNICODE
|
|
const CString& operator=(char ch);
|
|
#endif
|
|
const CString& operator=(LPCSTR lpsz);
|
|
const CString& operator=(LPCWSTR lpsz);
|
|
const CString& operator=(const unsigned char* psz);
|
|
|
|
// string concatenation
|
|
const CString& operator+=(const CString& string);
|
|
const CString& operator+=(TCHAR ch);
|
|
#ifdef _UNICODE
|
|
const CString& operator+=(char ch);
|
|
#endif
|
|
const CString& operator+=(LPCTSTR lpsz);
|
|
|
|
friend CString __stdcall operator+(const CString& string1, const CString& string2);
|
|
friend CString __stdcall operator+(const CString& string, TCHAR ch);
|
|
friend CString __stdcall operator+(TCHAR ch, const CString& string);
|
|
#ifdef _UNICODE
|
|
friend CString __stdcall operator+(const CString& string, char ch);
|
|
friend CString __stdcall operator+(char ch, const CString& string);
|
|
#endif
|
|
friend CString __stdcall operator+(const CString& string, LPCTSTR lpsz);
|
|
friend CString __stdcall operator+(LPCTSTR lpsz, const CString& string);
|
|
|
|
// string comparison
|
|
int Compare(LPCTSTR lpsz) const; // straight character
|
|
int CompareNoCase(LPCTSTR lpsz) const; // ignore case
|
|
int Collate(LPCTSTR lpsz) const; // NLS aware
|
|
|
|
// simple sub-string extraction
|
|
CString Mid(int nFirst, int nCount) const;
|
|
CString Mid(int nFirst) const;
|
|
CString Left(int nCount) const;
|
|
CString Right(int nCount) const;
|
|
|
|
CString SpanIncluding(LPCTSTR lpszCharSet) const;
|
|
CString SpanExcluding(LPCTSTR lpszCharSet) const;
|
|
|
|
// upper/lower/reverse conversion
|
|
void MakeUpper();
|
|
void MakeLower();
|
|
void MakeReverse();
|
|
|
|
// trimming whitespace (either side)
|
|
void TrimRight();
|
|
void TrimLeft();
|
|
|
|
// advanced manipulation
|
|
// replace occurrences of chOld with chNew
|
|
int Replace(TCHAR chOld, TCHAR chNew);
|
|
// replace occurrences of substring lpszOld with lpszNew;
|
|
// empty lpszNew removes instances of lpszOld
|
|
int Replace(LPCTSTR lpszOld, LPCTSTR lpszNew);
|
|
// remove occurrences of chRemove
|
|
int Remove(TCHAR chRemove);
|
|
// insert character at zero-based index; concatenates
|
|
// if index is past end of string
|
|
int Insert(int nIndex, TCHAR ch);
|
|
// insert substring at zero-based index; concatenates
|
|
// if index is past end of string
|
|
int Insert(int nIndex, LPCTSTR pstr);
|
|
// delete nCount characters starting at zero-based index
|
|
int Delete(int nIndex, int nCount = 1);
|
|
|
|
// searching (return starting index, or -1 if not found)
|
|
// look for a single character match
|
|
int Find(TCHAR ch) const; // like "C" strchr
|
|
int ReverseFind(TCHAR ch) const;
|
|
int FindOneOf(LPCTSTR lpszCharSet) const;
|
|
|
|
// look for a specific sub-string
|
|
int Find(LPCTSTR lpszSub) const; // like "C" strstr
|
|
|
|
// Concatentation for non strings
|
|
const CString& Append(int n)
|
|
{
|
|
TCHAR szBuffer[10];
|
|
wsprintf(szBuffer,_T("%d"),n);
|
|
ConcatInPlace(SafeStrlen(szBuffer), szBuffer);
|
|
return *this;
|
|
}
|
|
|
|
// simple formatting
|
|
void __cdecl Format(LPCTSTR lpszFormat, ...);
|
|
void __cdecl Format(UINT nFormatID, ...);
|
|
|
|
// formatting for localization (uses FormatMessage API)
|
|
BOOL __cdecl FormatMessage(LPCTSTR lpszFormat, ...);
|
|
BOOL __cdecl FormatMessage(UINT nFormatID, ...);
|
|
|
|
// Windows support
|
|
BOOL LoadString(UINT nID); // load from string resource
|
|
// 255 chars max
|
|
#ifndef _UNICODE
|
|
// ANSI <-> OEM support (convert string in place)
|
|
void AnsiToOem();
|
|
void OemToAnsi();
|
|
#endif
|
|
|
|
#ifndef _ATL_NO_COM
|
|
// OLE BSTR support (use for OLE automation)
|
|
BSTR AllocSysString() const;
|
|
BSTR SetSysString(BSTR* pbstr) const;
|
|
#endif //!_ATL_NO_COM
|
|
|
|
// Access to string implementation buffer as "C" character array
|
|
LPTSTR GetBuffer(int nMinBufLength);
|
|
void ReleaseBuffer(int nNewLength = -1);
|
|
LPTSTR GetBufferSetLength(int nNewLength);
|
|
void FreeExtra();
|
|
|
|
// Use LockBuffer/UnlockBuffer to turn refcounting off
|
|
LPTSTR LockBuffer();
|
|
void UnlockBuffer();
|
|
|
|
// Implementation
|
|
public:
|
|
~CString();
|
|
int GetAllocLength() const;
|
|
|
|
static BOOL __stdcall _IsValidString(LPCWSTR lpsz, int nLength)
|
|
{
|
|
if(lpsz == NULL)
|
|
return FALSE;
|
|
return !::IsBadStringPtrW(lpsz, nLength);
|
|
}
|
|
|
|
static BOOL __stdcall _IsValidString(LPCSTR lpsz, int nLength)
|
|
{
|
|
if(lpsz == NULL)
|
|
return FALSE;
|
|
return !::IsBadStringPtrA(lpsz, nLength);
|
|
}
|
|
|
|
protected:
|
|
LPTSTR m_pchData; // pointer to ref counted string data
|
|
|
|
// implementation helpers
|
|
CStringData* GetData() const;
|
|
void Init();
|
|
void AllocCopy(CString& dest, int nCopyLen, int nCopyIndex, int nExtraLen) const;
|
|
BOOL AllocBuffer(int nLen);
|
|
void AssignCopy(int nSrcLen, LPCTSTR lpszSrcData);
|
|
BOOL ConcatCopy(int nSrc1Len, LPCTSTR lpszSrc1Data, int nSrc2Len, LPCTSTR lpszSrc2Data);
|
|
void ConcatInPlace(int nSrcLen, LPCTSTR lpszSrcData);
|
|
void FormatV(LPCTSTR lpszFormat, va_list argList);
|
|
void CopyBeforeWrite();
|
|
BOOL AllocBeforeWrite(int nLen);
|
|
void Release();
|
|
static void PASCAL Release(CStringData* pData);
|
|
static int PASCAL SafeStrlen(LPCTSTR lpsz);
|
|
|
|
static int __stdcall _LoadString(UINT nID, LPTSTR lpszBuf, UINT nMaxBuf)
|
|
{
|
|
#ifdef _DEBUG
|
|
// LoadString without annoying warning from the Debug kernel if the
|
|
// segment containing the string is not present
|
|
if (::FindResource(_Module.GetResourceInstance(), MAKEINTRESOURCE((nID>>4) + 1), RT_STRING) == NULL)
|
|
{
|
|
lpszBuf[0] = '\0';
|
|
return 0; // not found
|
|
}
|
|
#endif //_DEBUG
|
|
int nLen = ::LoadString(_Module.GetResourceInstance(), nID, lpszBuf, nMaxBuf);
|
|
if (nLen == 0)
|
|
lpszBuf[0] = '\0';
|
|
return nLen;
|
|
}
|
|
|
|
static const CString& __stdcall _GetEmptyString()
|
|
{
|
|
return *(CString*)&_atltmpPchNil;
|
|
}
|
|
|
|
// CString conversion helpers
|
|
static int __cdecl _wcstombsz(char* mbstr, const wchar_t* wcstr, size_t count)
|
|
{
|
|
if (count == 0 && mbstr != NULL)
|
|
return 0;
|
|
|
|
int result = ::WideCharToMultiByte(CP_ACP, 0, wcstr, -1, mbstr, (int)count, NULL, NULL);
|
|
ATLASSERT(mbstr == NULL || result <= (int)count);
|
|
if (result > 0)
|
|
mbstr[result - 1] = 0;
|
|
return result;
|
|
}
|
|
|
|
static int __cdecl _mbstowcsz(wchar_t* wcstr, const char* mbstr, size_t count)
|
|
{
|
|
if (count == 0 && wcstr != NULL)
|
|
return 0;
|
|
|
|
int result = ::MultiByteToWideChar(CP_ACP, 0, mbstr, -1, wcstr, (int)count);
|
|
ATLASSERT(wcstr == NULL || result <= (int)count);
|
|
if (result > 0)
|
|
wcstr[result - 1] = 0;
|
|
return result;
|
|
}
|
|
|
|
// Helpers to avoid CRT startup code
|
|
static TCHAR* _cstrchr(const TCHAR* p, TCHAR ch)
|
|
{
|
|
//strchr for '\0' should succeed
|
|
while (*p != 0)
|
|
{
|
|
if (*p == ch)
|
|
break;
|
|
p = ::CharNext(p);
|
|
}
|
|
return (TCHAR*)((*p == ch) ? p : NULL);
|
|
}
|
|
static TCHAR* _cstrchr_db(const TCHAR* p, TCHAR ch1, TCHAR ch2)
|
|
{
|
|
const TCHAR* lpsz = NULL;
|
|
while (*p != 0)
|
|
{
|
|
if (*p == ch1 && *(p + 1) == ch2)
|
|
{
|
|
lpsz = p;
|
|
break;
|
|
}
|
|
p = ::CharNext(p);
|
|
}
|
|
return (TCHAR*)lpsz;
|
|
}
|
|
static TCHAR* _cstrrchr(const TCHAR* p, TCHAR ch)
|
|
{
|
|
const TCHAR* lpsz = NULL;
|
|
while (*p != 0)
|
|
{
|
|
if (*p == ch)
|
|
lpsz = p;
|
|
p = ::CharNext(p);
|
|
}
|
|
return (TCHAR*)lpsz;
|
|
}
|
|
static TCHAR* _cstrrev(TCHAR* pStr)
|
|
{
|
|
// Optimize NULL, zero-length, and single-char case.
|
|
if ((pStr == NULL) || (pStr[0] == '\0') || (pStr[1] == '\0'))
|
|
return pStr;
|
|
|
|
TCHAR* p = pStr;
|
|
|
|
while (p[1] != 0)
|
|
{
|
|
TCHAR* pNext = ::CharNext(p);
|
|
if(pNext > p + 1)
|
|
{
|
|
char p1 = *(char*)p;
|
|
*(char*)p = *(char*)(p + 1);
|
|
*(char*)(p + 1) = p1;
|
|
}
|
|
p = pNext;
|
|
}
|
|
|
|
TCHAR* q = pStr;
|
|
|
|
while (q < p)
|
|
{
|
|
TCHAR t = *q;
|
|
*q = *p;
|
|
*p = t;
|
|
q++;
|
|
p--;
|
|
}
|
|
return (TCHAR*)pStr;
|
|
}
|
|
static TCHAR* _cstrstr(const TCHAR* pStr, const TCHAR* pCharSet)
|
|
{
|
|
int nLen = lstrlen(pCharSet);
|
|
if (nLen == 0)
|
|
return (TCHAR*)pStr;
|
|
|
|
const TCHAR* pRet = NULL;
|
|
const TCHAR* pCur = pStr;
|
|
while((pStr = _cstrchr(pCur, *pCharSet)) != NULL)
|
|
{
|
|
if(memcmp(pCur, pCharSet, nLen * sizeof(TCHAR)) == 0)
|
|
{
|
|
pRet = pCur;
|
|
break;
|
|
}
|
|
pCur = ::CharNext(pCur);
|
|
}
|
|
return (TCHAR*) pRet;
|
|
}
|
|
static int _cstrspn(const TCHAR* pStr, const TCHAR* pCharSet)
|
|
{
|
|
int nRet = 0;
|
|
TCHAR* p = (TCHAR*)pStr;
|
|
while (*p != 0)
|
|
{
|
|
TCHAR* pNext = ::CharNext(p);
|
|
if(pNext > p + 1)
|
|
{
|
|
if(_cstrchr_db(pCharSet, *p, *(p + 1)) == NULL)
|
|
break;
|
|
nRet += 2;
|
|
}
|
|
else
|
|
{
|
|
if(_cstrchr(pCharSet, *p) == NULL)
|
|
break;
|
|
nRet++;
|
|
}
|
|
p = pNext;
|
|
}
|
|
return nRet;
|
|
}
|
|
static int _cstrcspn(const TCHAR* pStr, const TCHAR* pCharSet)
|
|
{
|
|
int nRet = 0;
|
|
TCHAR* p = (TCHAR*)pStr;
|
|
while (*p != 0)
|
|
{
|
|
TCHAR* pNext = ::CharNext(p);
|
|
if(pNext > p + 1)
|
|
{
|
|
if(_cstrchr_db(pCharSet, *p, *(p + 1)) != NULL)
|
|
break;
|
|
nRet += 2;
|
|
}
|
|
else
|
|
{
|
|
if(_cstrchr(pCharSet, *p) != NULL)
|
|
break;
|
|
nRet++;
|
|
}
|
|
p = pNext;
|
|
}
|
|
return nRet;
|
|
}
|
|
static TCHAR* _cstrpbrk(const TCHAR* p, const TCHAR* lpszCharSet)
|
|
{
|
|
while (*p != 0)
|
|
{
|
|
if (_cstrchr(lpszCharSet, *p) != NULL)
|
|
{
|
|
return (TCHAR*)p;
|
|
break;
|
|
}
|
|
p = ::CharNext(p);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static int _cstrisdigit(TCHAR ch)
|
|
{
|
|
WORD type;
|
|
GetStringTypeEx(GetThreadLocale(), CT_CTYPE1, &ch, 1, &type);
|
|
return (type & C1_DIGIT) == C1_DIGIT;
|
|
}
|
|
|
|
static int _cstrisspace(TCHAR ch)
|
|
{
|
|
WORD type;
|
|
GetStringTypeEx(GetThreadLocale(), CT_CTYPE1, &ch, 1, &type);
|
|
return (type & C1_SPACE) == C1_SPACE;
|
|
}
|
|
|
|
static int _cstrcmp(const TCHAR* pstrOne, const TCHAR* pstrOther)
|
|
{
|
|
return lstrcmp(pstrOne, pstrOther);
|
|
}
|
|
|
|
static int _cstrcmpi(const TCHAR* pstrOne, const TCHAR* pstrOther)
|
|
{
|
|
return lstrcmpi(pstrOne, pstrOther);
|
|
}
|
|
|
|
static int _cstrcoll(const TCHAR* pstrOne, const TCHAR* pstrOther)
|
|
{
|
|
int nRet = CompareString(GetThreadLocale(), 0, pstrOne, -1, pstrOther, -1);
|
|
ATLASSERT(nRet != 0);
|
|
return nRet - 2; // Convert to strcmp convention. This really is documented.
|
|
}
|
|
|
|
static int _cstrcolli(const TCHAR* pstrOne, const TCHAR* pstrOther)
|
|
{
|
|
int nRet = CompareString(GetThreadLocale(), NORM_IGNORECASE, pstrOne, -1, pstrOther, -1);
|
|
ATLASSERT(nRet != 0);
|
|
return nRet - 2; // Convert to strcmp convention. This really is documented.
|
|
}
|
|
};
|
|
|
|
// Compare helpers
|
|
bool __stdcall operator==(const CString& s1, const CString& s2);
|
|
bool __stdcall operator==(const CString& s1, LPCTSTR s2);
|
|
bool __stdcall operator==(LPCTSTR s1, const CString& s2);
|
|
bool __stdcall operator!=(const CString& s1, const CString& s2);
|
|
bool __stdcall operator!=(const CString& s1, LPCTSTR s2);
|
|
bool __stdcall operator!=(LPCTSTR s1, const CString& s2);
|
|
bool __stdcall operator<(const CString& s1, const CString& s2);
|
|
bool __stdcall operator<(const CString& s1, LPCTSTR s2);
|
|
bool __stdcall operator<(LPCTSTR s1, const CString& s2);
|
|
bool __stdcall operator>(const CString& s1, const CString& s2);
|
|
bool __stdcall operator>(const CString& s1, LPCTSTR s2);
|
|
bool __stdcall operator>(LPCTSTR s1, const CString& s2);
|
|
bool __stdcall operator<=(const CString& s1, const CString& s2);
|
|
bool __stdcall operator<=(const CString& s1, LPCTSTR s2);
|
|
bool __stdcall operator<=(LPCTSTR s1, const CString& s2);
|
|
bool __stdcall operator>=(const CString& s1, const CString& s2);
|
|
bool __stdcall operator>=(const CString& s1, LPCTSTR s2);
|
|
bool __stdcall operator>=(LPCTSTR s1, const CString& s2);
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CString Implementation
|
|
|
|
inline CStringData* CString::GetData() const
|
|
{ ATLASSERT(m_pchData != NULL); return ((CStringData*)m_pchData) - 1; }
|
|
inline void CString::Init()
|
|
{ m_pchData = _GetEmptyString().m_pchData; }
|
|
inline CString::CString(const unsigned char* lpsz)
|
|
{ Init(); *this = (LPCSTR)lpsz; }
|
|
inline const CString& CString::operator=(const unsigned char* lpsz)
|
|
{ *this = (LPCSTR)lpsz; return *this; }
|
|
#ifdef _UNICODE
|
|
inline const CString& CString::operator+=(char ch)
|
|
{ *this += (TCHAR)ch; return *this; }
|
|
inline const CString& CString::operator=(char ch)
|
|
{ *this = (TCHAR)ch; return *this; }
|
|
inline CString __stdcall operator+(const CString& string, char ch)
|
|
{ return string + (TCHAR)ch; }
|
|
inline CString __stdcall operator+(char ch, const CString& string)
|
|
{ return (TCHAR)ch + string; }
|
|
#endif
|
|
|
|
inline int CString::GetLength() const
|
|
{ return GetData()->nDataLength; }
|
|
inline int CString::GetAllocLength() const
|
|
{ return GetData()->nAllocLength; }
|
|
inline BOOL CString::IsEmpty() const
|
|
{ return GetData()->nDataLength == 0; }
|
|
inline CString::operator LPCTSTR() const
|
|
{ return m_pchData; }
|
|
inline int PASCAL CString::SafeStrlen(LPCTSTR lpsz)
|
|
{ return (lpsz == NULL) ? 0 : lstrlen(lpsz); }
|
|
|
|
// CString support (windows specific)
|
|
inline int CString::Compare(LPCTSTR lpsz) const
|
|
{ return _cstrcmp(m_pchData, lpsz); } // MBCS/Unicode aware
|
|
inline int CString::CompareNoCase(LPCTSTR lpsz) const
|
|
{ return _cstrcmpi(m_pchData, lpsz); } // MBCS/Unicode aware
|
|
// CString::Collate is often slower than Compare but is MBSC/Unicode
|
|
// aware as well as locale-sensitive with respect to sort order.
|
|
inline int CString::Collate(LPCTSTR lpsz) const
|
|
{ return _cstrcoll(m_pchData, lpsz); } // locale sensitive
|
|
|
|
inline TCHAR CString::GetAt(int nIndex) const
|
|
{
|
|
ATLASSERT(nIndex >= 0);
|
|
ATLASSERT(nIndex < GetData()->nDataLength);
|
|
return m_pchData[nIndex];
|
|
}
|
|
inline TCHAR CString::operator[](int nIndex) const
|
|
{
|
|
// same as GetAt
|
|
ATLASSERT(nIndex >= 0);
|
|
ATLASSERT(nIndex < GetData()->nDataLength);
|
|
return m_pchData[nIndex];
|
|
}
|
|
inline bool __stdcall operator==(const CString& s1, const CString& s2)
|
|
{ return s1.Compare(s2) == 0; }
|
|
inline bool __stdcall operator==(const CString& s1, LPCTSTR s2)
|
|
{ return s1.Compare(s2) == 0; }
|
|
inline bool __stdcall operator==(LPCTSTR s1, const CString& s2)
|
|
{ return s2.Compare(s1) == 0; }
|
|
inline bool __stdcall operator!=(const CString& s1, const CString& s2)
|
|
{ return s1.Compare(s2) != 0; }
|
|
inline bool __stdcall operator!=(const CString& s1, LPCTSTR s2)
|
|
{ return s1.Compare(s2) != 0; }
|
|
inline bool __stdcall operator!=(LPCTSTR s1, const CString& s2)
|
|
{ return s2.Compare(s1) != 0; }
|
|
inline bool __stdcall operator<(const CString& s1, const CString& s2)
|
|
{ return s1.Compare(s2) < 0; }
|
|
inline bool __stdcall operator<(const CString& s1, LPCTSTR s2)
|
|
{ return s1.Compare(s2) < 0; }
|
|
inline bool __stdcall operator<(LPCTSTR s1, const CString& s2)
|
|
{ return s2.Compare(s1) > 0; }
|
|
inline bool __stdcall operator>(const CString& s1, const CString& s2)
|
|
{ return s1.Compare(s2) > 0; }
|
|
inline bool __stdcall operator>(const CString& s1, LPCTSTR s2)
|
|
{ return s1.Compare(s2) > 0; }
|
|
inline bool __stdcall operator>(LPCTSTR s1, const CString& s2)
|
|
{ return s2.Compare(s1) < 0; }
|
|
inline bool __stdcall operator<=(const CString& s1, const CString& s2)
|
|
{ return s1.Compare(s2) <= 0; }
|
|
inline bool __stdcall operator<=(const CString& s1, LPCTSTR s2)
|
|
{ return s1.Compare(s2) <= 0; }
|
|
inline bool __stdcall operator<=(LPCTSTR s1, const CString& s2)
|
|
{ return s2.Compare(s1) >= 0; }
|
|
inline bool __stdcall operator>=(const CString& s1, const CString& s2)
|
|
{ return s1.Compare(s2) >= 0; }
|
|
inline bool __stdcall operator>=(const CString& s1, LPCTSTR s2)
|
|
{ return s1.Compare(s2) >= 0; }
|
|
inline bool __stdcall operator>=(LPCTSTR s1, const CString& s2)
|
|
{ return s2.Compare(s1) <= 0; }
|
|
|
|
inline CString::CString()
|
|
{
|
|
Init();
|
|
}
|
|
|
|
inline CString::CString(const CString& stringSrc)
|
|
{
|
|
ATLASSERT(stringSrc.GetData()->nRefs != 0);
|
|
if (stringSrc.GetData()->nRefs >= 0)
|
|
{
|
|
ATLASSERT(stringSrc.GetData() != _atltmpDataNil);
|
|
m_pchData = stringSrc.m_pchData;
|
|
InterlockedIncrement(&GetData()->nRefs);
|
|
}
|
|
else
|
|
{
|
|
Init();
|
|
*this = stringSrc.m_pchData;
|
|
}
|
|
}
|
|
|
|
inline BOOL CString::AllocBuffer(int nLen)
|
|
// always allocate one extra character for '\0' termination
|
|
// assumes [optimistically] that data length will equal allocation length
|
|
{
|
|
ATLASSERT(nLen >= 0);
|
|
ATLASSERT(nLen <= INT_MAX - 1); // max size (enough room for 1 extra)
|
|
|
|
if (nLen == 0)
|
|
{
|
|
Init();
|
|
}
|
|
else
|
|
{
|
|
CStringData* pData = NULL;
|
|
ATLTRY(pData = (CStringData*)new BYTE[sizeof(CStringData) + (nLen + 1) * sizeof(TCHAR)]);
|
|
if(pData == NULL)
|
|
return FALSE;
|
|
|
|
pData->nRefs = 1;
|
|
pData->data()[nLen] = '\0';
|
|
pData->nDataLength = nLen;
|
|
pData->nAllocLength = nLen;
|
|
m_pchData = pData->data();
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
inline void CString::Release()
|
|
{
|
|
if (GetData() != _atltmpDataNil)
|
|
{
|
|
ATLASSERT(GetData()->nRefs != 0);
|
|
if (InterlockedDecrement(&GetData()->nRefs) <= 0)
|
|
delete[] (BYTE*)GetData();
|
|
Init();
|
|
}
|
|
}
|
|
|
|
inline void PASCAL CString::Release(CStringData* pData)
|
|
{
|
|
if (pData != _atltmpDataNil)
|
|
{
|
|
ATLASSERT(pData->nRefs != 0);
|
|
if (InterlockedDecrement(&pData->nRefs) <= 0)
|
|
delete[] (BYTE*)pData;
|
|
}
|
|
}
|
|
|
|
inline void CString::Empty()
|
|
{
|
|
if (GetData()->nDataLength == 0)
|
|
return;
|
|
|
|
if (GetData()->nRefs >= 0)
|
|
Release();
|
|
else
|
|
*this = _T("");
|
|
|
|
ATLASSERT(GetData()->nDataLength == 0);
|
|
ATLASSERT(GetData()->nRefs < 0 || GetData()->nAllocLength == 0);
|
|
}
|
|
|
|
inline void CString::CopyBeforeWrite()
|
|
{
|
|
if (GetData()->nRefs > 1)
|
|
{
|
|
CStringData* pData = GetData();
|
|
Release();
|
|
if(AllocBuffer(pData->nDataLength))
|
|
memcpy(m_pchData, pData->data(), (pData->nDataLength + 1) * sizeof(TCHAR));
|
|
}
|
|
ATLASSERT(GetData()->nRefs <= 1);
|
|
}
|
|
|
|
inline BOOL CString::AllocBeforeWrite(int nLen)
|
|
{
|
|
BOOL bRet = TRUE;
|
|
if (GetData()->nRefs > 1 || nLen > GetData()->nAllocLength)
|
|
{
|
|
Release();
|
|
bRet = AllocBuffer(nLen);
|
|
}
|
|
ATLASSERT(GetData()->nRefs <= 1);
|
|
return bRet;
|
|
}
|
|
|
|
inline CString::~CString()
|
|
// free any attached data
|
|
{
|
|
if (GetData() != _atltmpDataNil)
|
|
{
|
|
if (InterlockedDecrement(&GetData()->nRefs) <= 0)
|
|
delete[] (BYTE*)GetData();
|
|
}
|
|
}
|
|
|
|
inline void CString::AllocCopy(CString& dest, int nCopyLen, int nCopyIndex,
|
|
int nExtraLen) const
|
|
{
|
|
// will clone the data attached to this string
|
|
// allocating 'nExtraLen' characters
|
|
// Places results in uninitialized string 'dest'
|
|
// Will copy the part or all of original data to start of new string
|
|
|
|
int nNewLen = nCopyLen + nExtraLen;
|
|
if (nNewLen == 0)
|
|
{
|
|
dest.Init();
|
|
}
|
|
else
|
|
{
|
|
if(dest.AllocBuffer(nNewLen))
|
|
memcpy(dest.m_pchData, m_pchData + nCopyIndex, nCopyLen * sizeof(TCHAR));
|
|
}
|
|
}
|
|
|
|
inline CString::CString(LPCTSTR lpsz)
|
|
{
|
|
Init();
|
|
if (lpsz != NULL && HIWORD(lpsz) == NULL)
|
|
{
|
|
UINT nID = LOWORD((DWORD_PTR)lpsz);
|
|
if (!LoadString(nID))
|
|
ATLTRACE2(atlTraceUI, 0, _T("Warning: implicit LoadString(%u) in CString failed\n"), nID);
|
|
}
|
|
else
|
|
{
|
|
int nLen = SafeStrlen(lpsz);
|
|
if (nLen != 0)
|
|
{
|
|
if(AllocBuffer(nLen))
|
|
memcpy(m_pchData, lpsz, nLen * sizeof(TCHAR));
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef _UNICODE
|
|
inline CString::CString(LPCSTR lpsz)
|
|
{
|
|
Init();
|
|
int nSrcLen = lpsz != NULL ? lstrlenA(lpsz) : 0;
|
|
if (nSrcLen != 0)
|
|
{
|
|
if(AllocBuffer(nSrcLen))
|
|
{
|
|
_mbstowcsz(m_pchData, lpsz, nSrcLen + 1);
|
|
ReleaseBuffer();
|
|
}
|
|
}
|
|
}
|
|
#else //_UNICODE
|
|
inline CString::CString(LPCWSTR lpsz)
|
|
{
|
|
Init();
|
|
int nSrcLen = lpsz != NULL ? wcslen(lpsz) : 0;
|
|
if (nSrcLen != 0)
|
|
{
|
|
if(AllocBuffer(nSrcLen * 2))
|
|
{
|
|
_wcstombsz(m_pchData, lpsz, (nSrcLen * 2) + 1);
|
|
ReleaseBuffer();
|
|
}
|
|
}
|
|
}
|
|
#endif //!_UNICODE
|
|
|
|
// Assignment operators
|
|
// All assign a new value to the string
|
|
// (a) first see if the buffer is big enough
|
|
// (b) if enough room, copy on top of old buffer, set size and type
|
|
// (c) otherwise free old string data, and create a new one
|
|
//
|
|
// All routines return the new string (but as a 'const CString&' so that
|
|
// assigning it again will cause a copy, eg: s1 = s2 = "hi there".
|
|
//
|
|
|
|
inline void CString::AssignCopy(int nSrcLen, LPCTSTR lpszSrcData)
|
|
{
|
|
if(AllocBeforeWrite(nSrcLen))
|
|
{
|
|
memcpy(m_pchData, lpszSrcData, nSrcLen * sizeof(TCHAR));
|
|
GetData()->nDataLength = nSrcLen;
|
|
m_pchData[nSrcLen] = '\0';
|
|
}
|
|
}
|
|
|
|
inline const CString& CString::operator=(const CString& stringSrc)
|
|
{
|
|
if (m_pchData != stringSrc.m_pchData)
|
|
{
|
|
if ((GetData()->nRefs < 0 && GetData() != _atltmpDataNil) || stringSrc.GetData()->nRefs < 0)
|
|
{
|
|
// actual copy necessary since one of the strings is locked
|
|
AssignCopy(stringSrc.GetData()->nDataLength, stringSrc.m_pchData);
|
|
}
|
|
else
|
|
{
|
|
// can just copy references around
|
|
Release();
|
|
ATLASSERT(stringSrc.GetData() != _atltmpDataNil);
|
|
m_pchData = stringSrc.m_pchData;
|
|
InterlockedIncrement(&GetData()->nRefs);
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
inline const CString& CString::operator=(LPCTSTR lpsz)
|
|
{
|
|
ATLASSERT(lpsz == NULL || _IsValidString(lpsz, FALSE));
|
|
AssignCopy(SafeStrlen(lpsz), lpsz);
|
|
return *this;
|
|
}
|
|
|
|
#ifdef _UNICODE
|
|
inline const CString& CString::operator=(LPCSTR lpsz)
|
|
{
|
|
int nSrcLen = lpsz != NULL ? lstrlenA(lpsz) : 0;
|
|
if(AllocBeforeWrite(nSrcLen))
|
|
{
|
|
_mbstowcsz(m_pchData, lpsz, nSrcLen + 1);
|
|
ReleaseBuffer();
|
|
}
|
|
return *this;
|
|
}
|
|
#else //!_UNICODE
|
|
inline const CString& CString::operator=(LPCWSTR lpsz)
|
|
{
|
|
int nSrcLen = lpsz != NULL ? wcslen(lpsz) : 0;
|
|
if(AllocBeforeWrite(nSrcLen * 2))
|
|
{
|
|
_wcstombsz(m_pchData, lpsz, (nSrcLen * 2) + 1);
|
|
ReleaseBuffer();
|
|
}
|
|
return *this;
|
|
}
|
|
#endif //!_UNICODE
|
|
|
|
// Concatenation
|
|
// NOTE: "operator+" is done as friend functions for simplicity
|
|
// There are three variants:
|
|
// CString + CString
|
|
// and for ? = TCHAR, LPCTSTR
|
|
// CString + ?
|
|
// ? + CString
|
|
|
|
inline BOOL CString::ConcatCopy(int nSrc1Len, LPCTSTR lpszSrc1Data,
|
|
int nSrc2Len, LPCTSTR lpszSrc2Data)
|
|
{
|
|
// -- master concatenation routine
|
|
// Concatenate two sources
|
|
// -- assume that 'this' is a new CString object
|
|
|
|
BOOL bRet = TRUE;
|
|
int nNewLen = nSrc1Len + nSrc2Len;
|
|
if (nNewLen != 0)
|
|
{
|
|
bRet = AllocBuffer(nNewLen);
|
|
if (bRet)
|
|
{
|
|
memcpy(m_pchData, lpszSrc1Data, nSrc1Len * sizeof(TCHAR));
|
|
memcpy(m_pchData + nSrc1Len, lpszSrc2Data, nSrc2Len * sizeof(TCHAR));
|
|
}
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
inline CString __stdcall operator+(const CString& string1, const CString& string2)
|
|
{
|
|
CString s;
|
|
s.ConcatCopy(string1.GetData()->nDataLength, string1.m_pchData, string2.GetData()->nDataLength, string2.m_pchData);
|
|
return s;
|
|
}
|
|
|
|
inline CString __stdcall operator+(const CString& string, LPCTSTR lpsz)
|
|
{
|
|
ATLASSERT(lpsz == NULL || CString::_IsValidString(lpsz, FALSE));
|
|
CString s;
|
|
s.ConcatCopy(string.GetData()->nDataLength, string.m_pchData, CString::SafeStrlen(lpsz), lpsz);
|
|
return s;
|
|
}
|
|
|
|
inline CString __stdcall operator+(LPCTSTR lpsz, const CString& string)
|
|
{
|
|
ATLASSERT(lpsz == NULL || CString::_IsValidString(lpsz, FALSE));
|
|
CString s;
|
|
s.ConcatCopy(CString::SafeStrlen(lpsz), lpsz, string.GetData()->nDataLength, string.m_pchData);
|
|
return s;
|
|
}
|
|
|
|
inline void CString::ConcatInPlace(int nSrcLen, LPCTSTR lpszSrcData)
|
|
{
|
|
// -- the main routine for += operators
|
|
|
|
// concatenating an empty string is a no-op!
|
|
if (nSrcLen == 0)
|
|
return;
|
|
|
|
// if the buffer is too small, or we have a width mis-match, just
|
|
// allocate a new buffer (slow but sure)
|
|
if (GetData()->nRefs > 1 || GetData()->nDataLength + nSrcLen > GetData()->nAllocLength)
|
|
{
|
|
// we have to grow the buffer, use the ConcatCopy routine
|
|
CStringData* pOldData = GetData();
|
|
if (ConcatCopy(GetData()->nDataLength, m_pchData, nSrcLen, lpszSrcData))
|
|
{
|
|
ATLASSERT(pOldData != NULL);
|
|
CString::Release(pOldData);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// fast concatenation when buffer big enough
|
|
memcpy(m_pchData + GetData()->nDataLength, lpszSrcData, nSrcLen * sizeof(TCHAR));
|
|
GetData()->nDataLength += nSrcLen;
|
|
ATLASSERT(GetData()->nDataLength <= GetData()->nAllocLength);
|
|
m_pchData[GetData()->nDataLength] = '\0';
|
|
}
|
|
}
|
|
|
|
inline const CString& CString::operator+=(LPCTSTR lpsz)
|
|
{
|
|
ATLASSERT(lpsz == NULL || _IsValidString(lpsz, FALSE));
|
|
ConcatInPlace(SafeStrlen(lpsz), lpsz);
|
|
return *this;
|
|
}
|
|
|
|
inline const CString& CString::operator+=(TCHAR ch)
|
|
{
|
|
ConcatInPlace(1, &ch);
|
|
return *this;
|
|
}
|
|
|
|
inline const CString& CString::operator+=(const CString& string)
|
|
{
|
|
ConcatInPlace(string.GetData()->nDataLength, string.m_pchData);
|
|
return *this;
|
|
}
|
|
|
|
inline LPTSTR CString::GetBuffer(int nMinBufLength)
|
|
{
|
|
ATLASSERT(nMinBufLength >= 0);
|
|
|
|
if (GetData()->nRefs > 1 || nMinBufLength > GetData()->nAllocLength)
|
|
{
|
|
// we have to grow the buffer
|
|
CStringData* pOldData = GetData();
|
|
int nOldLen = GetData()->nDataLength; // AllocBuffer will tromp it
|
|
if (nMinBufLength < nOldLen)
|
|
nMinBufLength = nOldLen;
|
|
|
|
if(AllocBuffer(nMinBufLength))
|
|
{
|
|
memcpy(m_pchData, pOldData->data(), (nOldLen + 1) * sizeof(TCHAR));
|
|
GetData()->nDataLength = nOldLen;
|
|
CString::Release(pOldData);
|
|
}
|
|
}
|
|
ATLASSERT(GetData()->nRefs <= 1);
|
|
|
|
// return a pointer to the character storage for this string
|
|
ATLASSERT(m_pchData != NULL);
|
|
return m_pchData;
|
|
}
|
|
|
|
inline void CString::ReleaseBuffer(int nNewLength)
|
|
{
|
|
CopyBeforeWrite(); // just in case GetBuffer was not called
|
|
|
|
if (nNewLength == -1)
|
|
nNewLength = lstrlen(m_pchData); // zero terminated
|
|
|
|
ATLASSERT(nNewLength <= GetData()->nAllocLength);
|
|
GetData()->nDataLength = nNewLength;
|
|
m_pchData[nNewLength] = '\0';
|
|
}
|
|
|
|
inline LPTSTR CString::GetBufferSetLength(int nNewLength)
|
|
{
|
|
ATLASSERT(nNewLength >= 0);
|
|
|
|
GetBuffer(nNewLength);
|
|
GetData()->nDataLength = nNewLength;
|
|
m_pchData[nNewLength] = '\0';
|
|
return m_pchData;
|
|
}
|
|
|
|
inline void CString::FreeExtra()
|
|
{
|
|
ATLASSERT(GetData()->nDataLength <= GetData()->nAllocLength);
|
|
if (GetData()->nDataLength != GetData()->nAllocLength)
|
|
{
|
|
CStringData* pOldData = GetData();
|
|
if(AllocBuffer(GetData()->nDataLength))
|
|
{
|
|
memcpy(m_pchData, pOldData->data(), pOldData->nDataLength * sizeof(TCHAR));
|
|
ATLASSERT(m_pchData[GetData()->nDataLength] == '\0');
|
|
CString::Release(pOldData);
|
|
}
|
|
}
|
|
ATLASSERT(GetData() != NULL);
|
|
}
|
|
|
|
inline LPTSTR CString::LockBuffer()
|
|
{
|
|
LPTSTR lpsz = GetBuffer(0);
|
|
GetData()->nRefs = -1;
|
|
return lpsz;
|
|
}
|
|
|
|
inline void CString::UnlockBuffer()
|
|
{
|
|
ATLASSERT(GetData()->nRefs == -1);
|
|
if (GetData() != _atltmpDataNil)
|
|
GetData()->nRefs = 1;
|
|
}
|
|
|
|
inline int CString::Find(TCHAR ch) const
|
|
{
|
|
// find first single character
|
|
LPTSTR lpsz = _cstrchr(m_pchData, (_TUCHAR)ch);
|
|
|
|
// return -1 if not found and index otherwise
|
|
return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
|
|
}
|
|
|
|
inline int CString::FindOneOf(LPCTSTR lpszCharSet) const
|
|
{
|
|
ATLASSERT(_IsValidString(lpszCharSet, FALSE));
|
|
LPTSTR lpsz = _cstrpbrk(m_pchData, lpszCharSet);
|
|
return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
|
|
}
|
|
|
|
inline void CString::MakeUpper()
|
|
{
|
|
CopyBeforeWrite();
|
|
CharUpper(m_pchData);
|
|
}
|
|
|
|
inline void CString::MakeLower()
|
|
{
|
|
CopyBeforeWrite();
|
|
CharLower(m_pchData);
|
|
}
|
|
|
|
inline void CString::MakeReverse()
|
|
{
|
|
CopyBeforeWrite();
|
|
_cstrrev(m_pchData);
|
|
}
|
|
|
|
inline void CString::SetAt(int nIndex, TCHAR ch)
|
|
{
|
|
ATLASSERT(nIndex >= 0);
|
|
ATLASSERT(nIndex < GetData()->nDataLength);
|
|
|
|
CopyBeforeWrite();
|
|
m_pchData[nIndex] = ch;
|
|
}
|
|
|
|
#ifndef _UNICODE
|
|
inline void CString::AnsiToOem()
|
|
{
|
|
CopyBeforeWrite();
|
|
::AnsiToOem(m_pchData, m_pchData);
|
|
}
|
|
inline void CString::OemToAnsi()
|
|
{
|
|
CopyBeforeWrite();
|
|
::OemToAnsi(m_pchData, m_pchData);
|
|
}
|
|
#endif
|
|
|
|
inline CString::CString(TCHAR ch, int nLength)
|
|
{
|
|
ATLASSERT(!_istlead(ch)); // can't create a lead byte string
|
|
Init();
|
|
if (nLength >= 1)
|
|
{
|
|
if(AllocBuffer(nLength))
|
|
{
|
|
#ifdef _UNICODE
|
|
for (int i = 0; i < nLength; i++)
|
|
m_pchData[i] = ch;
|
|
#else
|
|
memset(m_pchData, ch, nLength);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
inline CString::CString(LPCTSTR lpch, int nLength)
|
|
{
|
|
Init();
|
|
if (nLength != 0)
|
|
{
|
|
if(AllocBuffer(nLength))
|
|
memcpy(m_pchData, lpch, nLength * sizeof(TCHAR));
|
|
}
|
|
}
|
|
|
|
inline const CString& CString::operator=(TCHAR ch)
|
|
{
|
|
ATLASSERT(!_istlead(ch)); // can't set single lead byte
|
|
AssignCopy(1, &ch);
|
|
return *this;
|
|
}
|
|
|
|
inline CString __stdcall operator+(const CString& string1, TCHAR ch)
|
|
{
|
|
CString s;
|
|
s.ConcatCopy(string1.GetData()->nDataLength, string1.m_pchData, 1, &ch);
|
|
return s;
|
|
}
|
|
|
|
inline CString __stdcall operator+(TCHAR ch, const CString& string)
|
|
{
|
|
CString s;
|
|
s.ConcatCopy(1, &ch, string.GetData()->nDataLength, string.m_pchData);
|
|
return s;
|
|
}
|
|
|
|
inline CString CString::Mid(int nFirst) const
|
|
{
|
|
return Mid(nFirst, GetData()->nDataLength - nFirst);
|
|
}
|
|
|
|
inline CString CString::Mid(int nFirst, int nCount) const
|
|
{
|
|
// out-of-bounds requests return sensible things
|
|
if (nFirst < 0)
|
|
nFirst = 0;
|
|
if (nCount < 0)
|
|
nCount = 0;
|
|
|
|
if (nFirst + nCount > GetData()->nDataLength)
|
|
nCount = GetData()->nDataLength - nFirst;
|
|
if (nFirst > GetData()->nDataLength)
|
|
nCount = 0;
|
|
|
|
CString dest;
|
|
AllocCopy(dest, nCount, nFirst, 0);
|
|
return dest;
|
|
}
|
|
|
|
inline CString CString::Right(int nCount) const
|
|
{
|
|
if (nCount < 0)
|
|
nCount = 0;
|
|
else if (nCount > GetData()->nDataLength)
|
|
nCount = GetData()->nDataLength;
|
|
|
|
CString dest;
|
|
AllocCopy(dest, nCount, GetData()->nDataLength-nCount, 0);
|
|
return dest;
|
|
}
|
|
|
|
inline CString CString::Left(int nCount) const
|
|
{
|
|
if (nCount < 0)
|
|
nCount = 0;
|
|
else if (nCount > GetData()->nDataLength)
|
|
nCount = GetData()->nDataLength;
|
|
|
|
CString dest;
|
|
AllocCopy(dest, nCount, 0, 0);
|
|
return dest;
|
|
}
|
|
|
|
// strspn equivalent
|
|
inline CString CString::SpanIncluding(LPCTSTR lpszCharSet) const
|
|
{
|
|
ATLASSERT(_IsValidString(lpszCharSet, FALSE));
|
|
return Left(_cstrspn(m_pchData, lpszCharSet));
|
|
}
|
|
|
|
// strcspn equivalent
|
|
inline CString CString::SpanExcluding(LPCTSTR lpszCharSet) const
|
|
{
|
|
ATLASSERT(_IsValidString(lpszCharSet, FALSE));
|
|
return Left(_cstrcspn(m_pchData, lpszCharSet));
|
|
}
|
|
|
|
inline int CString::ReverseFind(TCHAR ch) const
|
|
{
|
|
// find last single character
|
|
LPTSTR lpsz = _cstrrchr(m_pchData, (_TUCHAR)ch);
|
|
|
|
// return -1 if not found, distance from beginning otherwise
|
|
return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
|
|
}
|
|
|
|
// find a sub-string (like strstr)
|
|
inline int CString::Find(LPCTSTR lpszSub) const
|
|
{
|
|
ATLASSERT(_IsValidString(lpszSub, FALSE));
|
|
|
|
// find first matching substring
|
|
LPTSTR lpsz = _cstrstr(m_pchData, lpszSub);
|
|
|
|
// return -1 for not found, distance from beginning otherwise
|
|
return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
|
|
}
|
|
|
|
inline void CString::FormatV(LPCTSTR lpszFormat, va_list argList)
|
|
{
|
|
ATLASSERT(_IsValidString(lpszFormat, FALSE));
|
|
|
|
enum _FormatModifiers
|
|
{
|
|
FORCE_ANSI = 0x10000,
|
|
FORCE_UNICODE = 0x20000,
|
|
FORCE_INT64 = 0x40000
|
|
};
|
|
|
|
va_list argListSave = argList;
|
|
|
|
// make a guess at the maximum length of the resulting string
|
|
int nMaxLen = 0;
|
|
for (LPCTSTR lpsz = lpszFormat; *lpsz != '\0'; lpsz = ::CharNext(lpsz))
|
|
{
|
|
// handle '%' character, but watch out for '%%'
|
|
if (*lpsz != '%' || *(lpsz = ::CharNext(lpsz)) == '%')
|
|
{
|
|
nMaxLen += (int)lstrlen(lpsz);
|
|
continue;
|
|
}
|
|
|
|
int nItemLen = 0;
|
|
|
|
// handle '%' character with format
|
|
int nWidth = 0;
|
|
for (; *lpsz != '\0'; lpsz = ::CharNext(lpsz))
|
|
{
|
|
// check for valid flags
|
|
if (*lpsz == '#')
|
|
nMaxLen += 2; // for '0x'
|
|
else if (*lpsz == '*')
|
|
nWidth = va_arg(argList, int);
|
|
else if (*lpsz == '-' || *lpsz == '+' || *lpsz == '0' || *lpsz == ' ')
|
|
;
|
|
else // hit non-flag character
|
|
break;
|
|
}
|
|
// get width and skip it
|
|
if (nWidth == 0)
|
|
{
|
|
// width indicated by
|
|
nWidth = _ttoi(lpsz);
|
|
for (; *lpsz != '\0' && _cstrisdigit(*lpsz); lpsz = ::CharNext(lpsz))
|
|
;
|
|
}
|
|
ATLASSERT(nWidth >= 0);
|
|
|
|
int nPrecision = 0;
|
|
if (*lpsz == '.')
|
|
{
|
|
// skip past '.' separator (width.precision)
|
|
lpsz = ::CharNext(lpsz);
|
|
|
|
// get precision and skip it
|
|
if (*lpsz == '*')
|
|
{
|
|
nPrecision = va_arg(argList, int);
|
|
lpsz = ::CharNext(lpsz);
|
|
}
|
|
else
|
|
{
|
|
nPrecision = _ttoi(lpsz);
|
|
for (; *lpsz != '\0' && _cstrisdigit(*lpsz); lpsz = ::CharNext(lpsz))
|
|
;
|
|
}
|
|
ATLASSERT(nPrecision >= 0);
|
|
}
|
|
|
|
// should be on type modifier or specifier
|
|
int nModifier = 0;
|
|
if(lpsz[0] == _T('I') && lpsz[1] == _T('6') && lpsz[2] == _T('4'))
|
|
{
|
|
lpsz += 3;
|
|
nModifier = FORCE_INT64;
|
|
}
|
|
else
|
|
{
|
|
switch (*lpsz)
|
|
{
|
|
// modifiers that affect size
|
|
case 'h':
|
|
nModifier = FORCE_ANSI;
|
|
lpsz = ::CharNext(lpsz);
|
|
break;
|
|
case 'l':
|
|
nModifier = FORCE_UNICODE;
|
|
lpsz = ::CharNext(lpsz);
|
|
break;
|
|
|
|
// modifiers that do not affect size
|
|
case 'F':
|
|
case 'N':
|
|
case 'L':
|
|
lpsz = ::CharNext(lpsz);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// now should be on specifier
|
|
switch (*lpsz | nModifier)
|
|
{
|
|
// single characters
|
|
case 'c':
|
|
case 'C':
|
|
nItemLen = 2;
|
|
va_arg(argList, TCHAR);
|
|
break;
|
|
case 'c' | FORCE_ANSI:
|
|
case 'C' | FORCE_ANSI:
|
|
nItemLen = 2;
|
|
va_arg(argList, char);
|
|
break;
|
|
case 'c' | FORCE_UNICODE:
|
|
case 'C' | FORCE_UNICODE:
|
|
nItemLen = 2;
|
|
va_arg(argList, WCHAR);
|
|
break;
|
|
|
|
// strings
|
|
case 's':
|
|
{
|
|
LPCTSTR pstrNextArg = va_arg(argList, LPCTSTR);
|
|
if (pstrNextArg == NULL)
|
|
{
|
|
nItemLen = 6; // "(null)"
|
|
}
|
|
else
|
|
{
|
|
nItemLen = lstrlen(pstrNextArg);
|
|
nItemLen = max(1, nItemLen);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 'S':
|
|
{
|
|
#ifndef _UNICODE
|
|
LPWSTR pstrNextArg = va_arg(argList, LPWSTR);
|
|
if (pstrNextArg == NULL)
|
|
{
|
|
nItemLen = 6; // "(null)"
|
|
}
|
|
else
|
|
{
|
|
nItemLen = (int)wcslen(pstrNextArg);
|
|
nItemLen = max(1, nItemLen);
|
|
}
|
|
#else
|
|
LPCSTR pstrNextArg = va_arg(argList, LPCSTR);
|
|
if (pstrNextArg == NULL)
|
|
{
|
|
nItemLen = 6; // "(null)"
|
|
}
|
|
else
|
|
{
|
|
nItemLen = lstrlenA(pstrNextArg);
|
|
nItemLen = max(1, nItemLen);
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
case 's' | FORCE_ANSI:
|
|
case 'S' | FORCE_ANSI:
|
|
{
|
|
LPCSTR pstrNextArg = va_arg(argList, LPCSTR);
|
|
if (pstrNextArg == NULL)
|
|
{
|
|
nItemLen = 6; // "(null)"
|
|
}
|
|
else
|
|
{
|
|
nItemLen = lstrlenA(pstrNextArg);
|
|
nItemLen = max(1, nItemLen);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 's' | FORCE_UNICODE:
|
|
case 'S' | FORCE_UNICODE:
|
|
{
|
|
LPWSTR pstrNextArg = va_arg(argList, LPWSTR);
|
|
if (pstrNextArg == NULL)
|
|
{
|
|
nItemLen = 6; // "(null)"
|
|
}
|
|
else
|
|
{
|
|
nItemLen = (int)wcslen(pstrNextArg);
|
|
nItemLen = max(1, nItemLen);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// adjust nItemLen for strings
|
|
if (nItemLen != 0)
|
|
{
|
|
nItemLen = max(nItemLen, nWidth);
|
|
if (nPrecision != 0)
|
|
nItemLen = min(nItemLen, nPrecision);
|
|
}
|
|
else
|
|
{
|
|
switch (*lpsz)
|
|
{
|
|
// integers
|
|
case 'd':
|
|
case 'i':
|
|
case 'u':
|
|
case 'x':
|
|
case 'X':
|
|
case 'o':
|
|
if (nModifier & FORCE_INT64)
|
|
va_arg(argList, __int64);
|
|
else
|
|
va_arg(argList, int);
|
|
nItemLen = 32;
|
|
nItemLen = max(nItemLen, nWidth + nPrecision);
|
|
break;
|
|
|
|
#ifndef _ATL_USE_CSTRING_FLOAT
|
|
case 'e':
|
|
case 'f':
|
|
case 'g':
|
|
case 'G':
|
|
ATLASSERT(!"Floating point (%%e, %%f, %%g, and %%G) is not supported by the WTL::CString class.");
|
|
#ifndef _DEBUG
|
|
::OutputDebugString(_T("Floating point (%%e, %%f, %%g, and %%G) is not supported by the WTL::CString class."));
|
|
::DebugBreak();
|
|
#endif //!_DEBUG
|
|
break;
|
|
#else //_ATL_USE_CSTRING_FLOAT
|
|
case 'e':
|
|
case 'g':
|
|
case 'G':
|
|
va_arg(argList, double);
|
|
nItemLen = 128;
|
|
nItemLen = max(nItemLen, nWidth + nPrecision);
|
|
break;
|
|
case 'f':
|
|
{
|
|
double f;
|
|
LPTSTR pszTemp;
|
|
|
|
// 312 == strlen("-1+(309 zeroes).")
|
|
// 309 zeroes == max precision of a double
|
|
// 6 == adjustment in case precision is not specified,
|
|
// which means that the precision defaults to 6
|
|
pszTemp = (LPTSTR)_alloca(max(nWidth, 312 + nPrecision + 6));
|
|
|
|
f = va_arg(argList, double);
|
|
_stprintf(pszTemp, _T( "%*.*f" ), nWidth, nPrecision + 6, f);
|
|
nItemLen = _tcslen(pszTemp);
|
|
}
|
|
break;
|
|
#endif //_ATL_USE_CSTRING_FLOAT
|
|
|
|
case 'p':
|
|
va_arg(argList, void*);
|
|
nItemLen = 32;
|
|
nItemLen = max(nItemLen, nWidth + nPrecision);
|
|
break;
|
|
|
|
// no output
|
|
case 'n':
|
|
va_arg(argList, int*);
|
|
break;
|
|
|
|
default:
|
|
ATLASSERT(FALSE); // unknown formatting option
|
|
}
|
|
}
|
|
|
|
// adjust nMaxLen for output nItemLen
|
|
nMaxLen += nItemLen;
|
|
}
|
|
|
|
GetBuffer(nMaxLen);
|
|
#ifndef _ATL_USE_CSTRING_FLOAT
|
|
int nRet = wvsprintf(m_pchData, lpszFormat, argListSave);
|
|
#else //_ATL_USE_CSTRING_FLOAT
|
|
int nRet = _vstprintf(m_pchData, lpszFormat, argListSave);
|
|
#endif //_ATL_USE_CSTRING_FLOAT
|
|
nRet; // ref
|
|
ATLASSERT(nRet <= GetAllocLength());
|
|
ReleaseBuffer();
|
|
|
|
va_end(argListSave);
|
|
}
|
|
|
|
// formatting (using wsprintf style formatting)
|
|
inline void __cdecl CString::Format(LPCTSTR lpszFormat, ...)
|
|
{
|
|
ATLASSERT(_IsValidString(lpszFormat, FALSE));
|
|
|
|
va_list argList;
|
|
va_start(argList, lpszFormat);
|
|
FormatV(lpszFormat, argList);
|
|
va_end(argList);
|
|
}
|
|
|
|
inline void __cdecl CString::Format(UINT nFormatID, ...)
|
|
{
|
|
CString strFormat;
|
|
BOOL bRet = strFormat.LoadString(nFormatID);
|
|
bRet; // ref
|
|
ATLASSERT(bRet != 0);
|
|
|
|
va_list argList;
|
|
va_start(argList, nFormatID);
|
|
FormatV(strFormat, argList);
|
|
va_end(argList);
|
|
}
|
|
|
|
// formatting (using FormatMessage style formatting)
|
|
inline BOOL __cdecl CString::FormatMessage(LPCTSTR lpszFormat, ...)
|
|
{
|
|
// format message into temporary buffer lpszTemp
|
|
va_list argList;
|
|
va_start(argList, lpszFormat);
|
|
LPTSTR lpszTemp;
|
|
BOOL bRet = TRUE;
|
|
|
|
if (::FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER,
|
|
lpszFormat, 0, 0, (LPTSTR)&lpszTemp, 0, &argList) == 0 || lpszTemp == NULL)
|
|
bRet = FALSE;
|
|
|
|
// assign lpszTemp into the resulting string and free the temporary
|
|
*this = lpszTemp;
|
|
LocalFree(lpszTemp);
|
|
va_end(argList);
|
|
return bRet;
|
|
}
|
|
|
|
inline BOOL __cdecl CString::FormatMessage(UINT nFormatID, ...)
|
|
{
|
|
// get format string from string table
|
|
CString strFormat;
|
|
BOOL bRetTmp = strFormat.LoadString(nFormatID);
|
|
bRetTmp; // ref
|
|
ATLASSERT(bRetTmp != 0);
|
|
|
|
// format message into temporary buffer lpszTemp
|
|
va_list argList;
|
|
va_start(argList, nFormatID);
|
|
LPTSTR lpszTemp;
|
|
BOOL bRet = TRUE;
|
|
|
|
if (::FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER,
|
|
strFormat, 0, 0, (LPTSTR)&lpszTemp, 0, &argList) == 0 || lpszTemp == NULL)
|
|
bRet = FALSE;
|
|
|
|
// assign lpszTemp into the resulting string and free lpszTemp
|
|
*this = lpszTemp;
|
|
LocalFree(lpszTemp);
|
|
va_end(argList);
|
|
return bRet;
|
|
}
|
|
|
|
inline void CString::TrimRight()
|
|
{
|
|
CopyBeforeWrite();
|
|
|
|
// find beginning of trailing spaces by starting at beginning (DBCS aware)
|
|
LPTSTR lpsz = m_pchData;
|
|
LPTSTR lpszLast = NULL;
|
|
while (*lpsz != '\0')
|
|
{
|
|
if (_cstrisspace(*lpsz))
|
|
{
|
|
if (lpszLast == NULL)
|
|
lpszLast = lpsz;
|
|
}
|
|
else
|
|
{
|
|
lpszLast = NULL;
|
|
}
|
|
lpsz = ::CharNext(lpsz);
|
|
}
|
|
|
|
if (lpszLast != NULL)
|
|
{
|
|
// truncate at trailing space start
|
|
*lpszLast = '\0';
|
|
GetData()->nDataLength = (int)(DWORD_PTR)(lpszLast - m_pchData);
|
|
}
|
|
}
|
|
|
|
inline void CString::TrimLeft()
|
|
{
|
|
CopyBeforeWrite();
|
|
|
|
// find first non-space character
|
|
LPCTSTR lpsz = m_pchData;
|
|
while (_cstrisspace(*lpsz))
|
|
lpsz = ::CharNext(lpsz);
|
|
|
|
// fix up data and length
|
|
int nDataLength = GetData()->nDataLength - (int)(DWORD_PTR)(lpsz - m_pchData);
|
|
memmove(m_pchData, lpsz, (nDataLength + 1) * sizeof(TCHAR));
|
|
GetData()->nDataLength = nDataLength;
|
|
}
|
|
|
|
inline int CString::Delete(int nIndex, int nCount /* = 1 */)
|
|
{
|
|
if (nIndex < 0)
|
|
nIndex = 0;
|
|
int nNewLength = GetData()->nDataLength;
|
|
if (nCount > 0 && nIndex < nNewLength)
|
|
{
|
|
CopyBeforeWrite();
|
|
int nBytesToCopy = nNewLength - (nIndex + nCount) + 1;
|
|
|
|
memmove(m_pchData + nIndex, m_pchData + nIndex + nCount, nBytesToCopy * sizeof(TCHAR));
|
|
GetData()->nDataLength = nNewLength - nCount;
|
|
}
|
|
|
|
return nNewLength;
|
|
}
|
|
|
|
inline int CString::Insert(int nIndex, TCHAR ch)
|
|
{
|
|
CopyBeforeWrite();
|
|
|
|
if (nIndex < 0)
|
|
nIndex = 0;
|
|
|
|
int nNewLength = GetData()->nDataLength;
|
|
if (nIndex > nNewLength)
|
|
nIndex = nNewLength;
|
|
nNewLength++;
|
|
|
|
if (GetData()->nAllocLength < nNewLength)
|
|
{
|
|
CStringData* pOldData = GetData();
|
|
LPTSTR pstr = m_pchData;
|
|
if(!AllocBuffer(nNewLength))
|
|
return -1;
|
|
memcpy(m_pchData, pstr, (pOldData->nDataLength + 1) * sizeof(TCHAR));
|
|
CString::Release(pOldData);
|
|
}
|
|
|
|
// move existing bytes down
|
|
memmove(m_pchData + nIndex + 1, m_pchData + nIndex, (nNewLength - nIndex) * sizeof(TCHAR));
|
|
m_pchData[nIndex] = ch;
|
|
GetData()->nDataLength = nNewLength;
|
|
|
|
return nNewLength;
|
|
}
|
|
|
|
inline int CString::Insert(int nIndex, LPCTSTR pstr)
|
|
{
|
|
if (nIndex < 0)
|
|
nIndex = 0;
|
|
|
|
int nInsertLength = SafeStrlen(pstr);
|
|
int nNewLength = GetData()->nDataLength;
|
|
if (nInsertLength > 0)
|
|
{
|
|
CopyBeforeWrite();
|
|
if (nIndex > nNewLength)
|
|
nIndex = nNewLength;
|
|
nNewLength += nInsertLength;
|
|
|
|
if (GetData()->nAllocLength < nNewLength)
|
|
{
|
|
CStringData* pOldData = GetData();
|
|
LPTSTR pstr = m_pchData;
|
|
if(!AllocBuffer(nNewLength))
|
|
return -1;
|
|
memcpy(m_pchData, pstr, (pOldData->nDataLength + 1) * sizeof(TCHAR));
|
|
CString::Release(pOldData);
|
|
}
|
|
|
|
// move existing bytes down
|
|
memmove(m_pchData + nIndex + nInsertLength, m_pchData + nIndex, (nNewLength - nIndex - nInsertLength + 1) * sizeof(TCHAR));
|
|
memcpy(m_pchData + nIndex, pstr, nInsertLength * sizeof(TCHAR));
|
|
GetData()->nDataLength = nNewLength;
|
|
}
|
|
|
|
return nNewLength;
|
|
}
|
|
|
|
inline int CString::Replace(TCHAR chOld, TCHAR chNew)
|
|
{
|
|
int nCount = 0;
|
|
|
|
// short-circuit the nop case
|
|
if (chOld != chNew)
|
|
{
|
|
// otherwise modify each character that matches in the string
|
|
CopyBeforeWrite();
|
|
LPTSTR psz = m_pchData;
|
|
LPTSTR pszEnd = psz + GetData()->nDataLength;
|
|
while (psz < pszEnd)
|
|
{
|
|
// replace instances of the specified character only
|
|
if (*psz == chOld)
|
|
{
|
|
*psz = chNew;
|
|
nCount++;
|
|
}
|
|
psz = ::CharNext(psz);
|
|
}
|
|
}
|
|
return nCount;
|
|
}
|
|
|
|
inline int CString::Replace(LPCTSTR lpszOld, LPCTSTR lpszNew)
|
|
{
|
|
// can't have empty or NULL lpszOld
|
|
|
|
int nSourceLen = SafeStrlen(lpszOld);
|
|
if (nSourceLen == 0)
|
|
return 0;
|
|
int nReplacementLen = SafeStrlen(lpszNew);
|
|
|
|
// loop once to figure out the size of the result string
|
|
int nCount = 0;
|
|
LPTSTR lpszStart = m_pchData;
|
|
LPTSTR lpszEnd = m_pchData + GetData()->nDataLength;
|
|
LPTSTR lpszTarget;
|
|
while (lpszStart < lpszEnd)
|
|
{
|
|
while ((lpszTarget = _cstrstr(lpszStart, lpszOld)) != NULL)
|
|
{
|
|
nCount++;
|
|
lpszStart = lpszTarget + nSourceLen;
|
|
}
|
|
lpszStart += lstrlen(lpszStart) + 1;
|
|
}
|
|
|
|
// if any changes were made, make them
|
|
if (nCount > 0)
|
|
{
|
|
CopyBeforeWrite();
|
|
|
|
// if the buffer is too small, just
|
|
// allocate a new buffer (slow but sure)
|
|
int nOldLength = GetData()->nDataLength;
|
|
int nNewLength = nOldLength + (nReplacementLen - nSourceLen) * nCount;
|
|
if (GetData()->nAllocLength < nNewLength || GetData()->nRefs > 1)
|
|
{
|
|
CStringData* pOldData = GetData();
|
|
LPTSTR pstr = m_pchData;
|
|
if(!AllocBuffer(nNewLength))
|
|
return -1;
|
|
memcpy(m_pchData, pstr, pOldData->nDataLength * sizeof(TCHAR));
|
|
CString::Release(pOldData);
|
|
}
|
|
// else, we just do it in-place
|
|
lpszStart = m_pchData;
|
|
lpszEnd = m_pchData + GetData()->nDataLength;
|
|
|
|
// loop again to actually do the work
|
|
while (lpszStart < lpszEnd)
|
|
{
|
|
while ( (lpszTarget = _cstrstr(lpszStart, lpszOld)) != NULL)
|
|
{
|
|
int nBalance = nOldLength - ((int)(DWORD_PTR)(lpszTarget - m_pchData) + nSourceLen);
|
|
memmove(lpszTarget + nReplacementLen, lpszTarget + nSourceLen, nBalance * sizeof(TCHAR));
|
|
memcpy(lpszTarget, lpszNew, nReplacementLen * sizeof(TCHAR));
|
|
lpszStart = lpszTarget + nReplacementLen;
|
|
lpszStart[nBalance] = '\0';
|
|
nOldLength += (nReplacementLen - nSourceLen);
|
|
}
|
|
lpszStart += lstrlen(lpszStart) + 1;
|
|
}
|
|
ATLASSERT(m_pchData[nNewLength] == '\0');
|
|
GetData()->nDataLength = nNewLength;
|
|
}
|
|
|
|
return nCount;
|
|
}
|
|
|
|
inline int CString::Remove(TCHAR chRemove)
|
|
{
|
|
CopyBeforeWrite();
|
|
|
|
LPTSTR pstrSource = m_pchData;
|
|
LPTSTR pstrDest = m_pchData;
|
|
LPTSTR pstrEnd = m_pchData + GetData()->nDataLength;
|
|
|
|
while (pstrSource < pstrEnd)
|
|
{
|
|
if (*pstrSource != chRemove)
|
|
{
|
|
*pstrDest = *pstrSource;
|
|
pstrDest = ::CharNext(pstrDest);
|
|
}
|
|
pstrSource = ::CharNext(pstrSource);
|
|
}
|
|
*pstrDest = '\0';
|
|
int nCount = (int)(DWORD_PTR)(pstrSource - pstrDest);
|
|
GetData()->nDataLength -= nCount;
|
|
|
|
return nCount;
|
|
}
|
|
|
|
#ifdef _UNICODE
|
|
#define CHAR_FUDGE 1 // one TCHAR unused is good enough
|
|
#else
|
|
#define CHAR_FUDGE 2 // two BYTES unused for case of DBC last char
|
|
#endif
|
|
|
|
inline BOOL CString::LoadString(UINT nID)
|
|
{
|
|
// try fixed buffer first (to avoid wasting space in the heap)
|
|
TCHAR szTemp[256];
|
|
int nCount = sizeof(szTemp) / sizeof(szTemp[0]);
|
|
int nLen = _LoadString(nID, szTemp, nCount);
|
|
if (nCount - nLen > CHAR_FUDGE)
|
|
{
|
|
*this = szTemp;
|
|
return nLen > 0;
|
|
}
|
|
|
|
// try buffer size of 512, then larger size until entire string is retrieved
|
|
int nSize = 256;
|
|
do
|
|
{
|
|
nSize += 256;
|
|
nLen = _LoadString(nID, GetBuffer(nSize - 1), nSize);
|
|
} while (nSize - nLen <= CHAR_FUDGE);
|
|
ReleaseBuffer();
|
|
|
|
return nLen > 0;
|
|
}
|
|
|
|
#ifndef _ATL_NO_COM
|
|
inline BSTR CString::AllocSysString() const
|
|
{
|
|
#if defined(_UNICODE) || defined(OLE2ANSI)
|
|
BSTR bstr = ::SysAllocStringLen(m_pchData, GetData()->nDataLength);
|
|
#else
|
|
int nLen = MultiByteToWideChar(CP_ACP, 0, m_pchData,
|
|
GetData()->nDataLength, NULL, NULL);
|
|
BSTR bstr = ::SysAllocStringLen(NULL, nLen);
|
|
if(bstr != NULL)
|
|
MultiByteToWideChar(CP_ACP, 0, m_pchData, GetData()->nDataLength, bstr, nLen);
|
|
#endif
|
|
return bstr;
|
|
}
|
|
|
|
inline BSTR CString::SetSysString(BSTR* pbstr) const
|
|
{
|
|
#if defined(_UNICODE) || defined(OLE2ANSI)
|
|
::SysReAllocStringLen(pbstr, m_pchData, GetData()->nDataLength);
|
|
#else
|
|
int nLen = MultiByteToWideChar(CP_ACP, 0, m_pchData,
|
|
GetData()->nDataLength, NULL, NULL);
|
|
if(::SysReAllocStringLen(pbstr, NULL, nLen))
|
|
MultiByteToWideChar(CP_ACP, 0, m_pchData, GetData()->nDataLength, *pbstr, nLen);
|
|
#endif
|
|
ATLASSERT(*pbstr != NULL);
|
|
return *pbstr;
|
|
}
|
|
#endif //!_ATL_NO_COM
|
|
|
|
#endif //!_WTL_NO_CSTRING
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CRecentDocumentList - MRU List Support
|
|
|
|
// forward declaration
|
|
inline bool AtlCompactPath(LPTSTR lpstrOut, LPCTSTR lpstrIn, int cchLen);
|
|
|
|
template <class T, int t_cchItemLen = MAX_PATH>
|
|
class CRecentDocumentListBase
|
|
{
|
|
public:
|
|
// Declarations
|
|
struct _DocEntry
|
|
{
|
|
TCHAR szDocName[t_cchItemLen];
|
|
bool operator==(const _DocEntry& de) const
|
|
{ return (lstrcmpi(szDocName, de.szDocName) == 0); }
|
|
};
|
|
|
|
enum
|
|
{
|
|
m_nMaxEntries_Min = 2,
|
|
m_nMaxEntries_Max = ID_FILE_MRU_LAST - ID_FILE_MRU_FIRST + 1,
|
|
m_cchMaxItemLen_Min = 6,
|
|
m_cchMaxItemLen_Max = t_cchItemLen
|
|
};
|
|
|
|
// Data members
|
|
CSimpleArray<_DocEntry> m_arrDocs;
|
|
int m_nMaxEntries; // default is 4
|
|
HMENU m_hMenu;
|
|
|
|
TCHAR m_szNoEntries[t_cchItemLen];
|
|
|
|
int m_cchMaxItemLen;
|
|
|
|
// Constructor
|
|
CRecentDocumentListBase() : m_hMenu(NULL), m_nMaxEntries(4), m_cchMaxItemLen(-1)
|
|
{
|
|
ATLASSERT(t_cchItemLen > m_cchMaxItemLen_Min);
|
|
}
|
|
|
|
// Attributes
|
|
HMENU GetMenuHandle() const
|
|
{
|
|
return m_hMenu;
|
|
}
|
|
void SetMenuHandle(HMENU hMenu)
|
|
{
|
|
ATLASSERT(hMenu == NULL || ::IsMenu(hMenu));
|
|
m_hMenu = hMenu;
|
|
if(m_hMenu == NULL || (::GetMenuString(m_hMenu, ID_FILE_MRU_FIRST, m_szNoEntries, t_cchItemLen, MF_BYCOMMAND) == 0))
|
|
lstrcpy(m_szNoEntries, _T("(empty)"));
|
|
}
|
|
int GetMaxEntries() const
|
|
{
|
|
return m_nMaxEntries;
|
|
}
|
|
void SetMaxEntries(int nMaxEntries)
|
|
{
|
|
ATLASSERT(nMaxEntries > m_nMaxEntries_Min && nMaxEntries < m_nMaxEntries_Max);
|
|
if(nMaxEntries < m_nMaxEntries_Min)
|
|
nMaxEntries = m_nMaxEntries_Min;
|
|
else if(nMaxEntries > m_nMaxEntries_Max)
|
|
nMaxEntries = m_nMaxEntries_Max;
|
|
m_nMaxEntries = nMaxEntries;
|
|
}
|
|
int GetMaxItemLength() const
|
|
{
|
|
return m_cchMaxItemLen;
|
|
}
|
|
void SetMaxItemLength(int cchMaxLen)
|
|
{
|
|
ATLASSERT((cchMaxLen >= m_cchMaxItemLen_Min && cchMaxLen <= m_cchMaxItemLen_Max) || cchMaxLen == -1);
|
|
if(cchMaxLen != -1)
|
|
{
|
|
if(cchMaxLen < m_cchMaxItemLen_Min)
|
|
cchMaxLen = m_cchMaxItemLen_Min;
|
|
else if(cchMaxLen > m_cchMaxItemLen_Max)
|
|
cchMaxLen = m_cchMaxItemLen_Max;
|
|
}
|
|
m_cchMaxItemLen = cchMaxLen;
|
|
T* pT = static_cast<T*>(this);
|
|
pT->UpdateMenu();
|
|
}
|
|
|
|
// Operations
|
|
BOOL AddToList(LPCTSTR lpstrDocName)
|
|
{
|
|
_DocEntry de;
|
|
if(lstrcpy(de.szDocName, lpstrDocName) == NULL)
|
|
return FALSE;
|
|
|
|
for(int i = 0; i < m_arrDocs.GetSize(); i++)
|
|
{
|
|
if(lstrcmpi(m_arrDocs[i].szDocName, lpstrDocName) == 0)
|
|
{
|
|
m_arrDocs.RemoveAt(i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(m_arrDocs.GetSize() == m_nMaxEntries)
|
|
m_arrDocs.RemoveAt(0);
|
|
|
|
BOOL bRet = m_arrDocs.Add(de);
|
|
if(bRet)
|
|
{
|
|
T* pT = static_cast<T*>(this);
|
|
bRet = pT->UpdateMenu();
|
|
}
|
|
return bRet;
|
|
}
|
|
BOOL GetFromList(int nItemID, LPTSTR lpstrDocName)
|
|
{
|
|
int nIndex = m_arrDocs.GetSize() - (nItemID - ID_FILE_MRU_FIRST) - 1;
|
|
if(nIndex < 0 || nIndex >= m_arrDocs.GetSize())
|
|
return FALSE;
|
|
return (lstrcpy(lpstrDocName, m_arrDocs[nIndex].szDocName) != NULL);
|
|
}
|
|
BOOL RemoveFromList(int nItemID)
|
|
{
|
|
int nIndex = m_arrDocs.GetSize() - (nItemID - ID_FILE_MRU_FIRST) - 1;
|
|
BOOL bRet = m_arrDocs.RemoveAt(nIndex);
|
|
if(bRet)
|
|
{
|
|
T* pT = static_cast<T*>(this);
|
|
bRet = pT->UpdateMenu();
|
|
}
|
|
return bRet;
|
|
}
|
|
BOOL MoveToTop(int nItemID)
|
|
{
|
|
int nIndex = m_arrDocs.GetSize() - 1 - (nItemID - ID_FILE_MRU_FIRST);
|
|
if(nIndex < 0 || nIndex >= m_arrDocs.GetSize())
|
|
return FALSE;
|
|
_DocEntry de;
|
|
de = m_arrDocs[nIndex];
|
|
m_arrDocs.RemoveAt(nIndex);
|
|
BOOL bRet = m_arrDocs.Add(de);
|
|
if(bRet)
|
|
{
|
|
T* pT = static_cast<T*>(this);
|
|
bRet = pT->UpdateMenu();
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
BOOL ReadFromRegistry(LPCTSTR lpstrRegKey)
|
|
{
|
|
CRegKey rkParent;
|
|
CRegKey rk;
|
|
|
|
LONG lRet = rkParent.Open(HKEY_CURRENT_USER, lpstrRegKey);
|
|
if(lRet != ERROR_SUCCESS)
|
|
return FALSE;
|
|
lRet = rk.Open(rkParent, _T("Recent Document List"));
|
|
if(lRet != ERROR_SUCCESS)
|
|
return FALSE;
|
|
|
|
DWORD dwRet;
|
|
lRet = rk.QueryValue(dwRet, _T("DocumentCount"));
|
|
if(lRet == ERROR_SUCCESS || dwRet > 0 && dwRet < (ID_FILE_MRU_LAST - ID_FILE_MRU_FIRST + 1))
|
|
m_nMaxEntries = dwRet;
|
|
|
|
m_arrDocs.RemoveAll();
|
|
|
|
TCHAR szRetString[t_cchItemLen];
|
|
_DocEntry de;
|
|
|
|
for(int nItem = m_nMaxEntries; nItem > 0; nItem--)
|
|
{
|
|
TCHAR szBuff[11];
|
|
wsprintf(szBuff, _T("Document%i"), nItem);
|
|
DWORD dwCount = t_cchItemLen * sizeof(TCHAR);
|
|
lRet = rk.QueryValue(szRetString, szBuff, &dwCount);
|
|
if(lRet == ERROR_SUCCESS && (lstrcpy(de.szDocName, szRetString) != NULL))
|
|
m_arrDocs.Add(de);
|
|
}
|
|
|
|
rk.Close();
|
|
rkParent.Close();
|
|
|
|
T* pT = static_cast<T*>(this);
|
|
return pT->UpdateMenu();
|
|
}
|
|
BOOL WriteToRegistry(LPCTSTR lpstrRegKey)
|
|
{
|
|
CRegKey rkParent;
|
|
CRegKey rk;
|
|
|
|
LONG lRet = rkParent.Create(HKEY_CURRENT_USER, lpstrRegKey);
|
|
if(lRet != ERROR_SUCCESS)
|
|
return FALSE;
|
|
lRet = rk.Create(rkParent, _T("Recent Document List"));
|
|
if(lRet != ERROR_SUCCESS)
|
|
return FALSE;
|
|
|
|
lRet = rk.SetValue(m_nMaxEntries, _T("DocumentCount"));
|
|
ATLASSERT(lRet == ERROR_SUCCESS);
|
|
|
|
// set new values
|
|
int nItem;
|
|
for(nItem = m_arrDocs.GetSize(); nItem > 0; nItem--)
|
|
{
|
|
TCHAR szBuff[11];
|
|
wsprintf(szBuff, _T("Document%i"), nItem);
|
|
TCHAR szDocName[t_cchItemLen];
|
|
GetFromList(ID_FILE_MRU_FIRST + nItem - 1, szDocName);
|
|
lRet = rk.SetValue(szDocName, szBuff);
|
|
ATLASSERT(lRet == ERROR_SUCCESS);
|
|
}
|
|
|
|
// delete unused keys
|
|
for(nItem = m_arrDocs.GetSize() + 1; nItem < (ID_FILE_MRU_LAST - ID_FILE_MRU_FIRST + 1); nItem++)
|
|
{
|
|
TCHAR szBuff[11];
|
|
wsprintf(szBuff, _T("Document%i"), nItem);
|
|
rk.DeleteValue(szBuff);
|
|
}
|
|
|
|
rk.Close();
|
|
rkParent.Close();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Implementation
|
|
BOOL UpdateMenu()
|
|
{
|
|
if(m_hMenu == NULL)
|
|
return FALSE;
|
|
ATLASSERT(::IsMenu(m_hMenu));
|
|
|
|
int nItems = ::GetMenuItemCount(m_hMenu);
|
|
int nInsertPoint;
|
|
for(nInsertPoint = 0; nInsertPoint < nItems; nInsertPoint++)
|
|
{
|
|
MENUITEMINFO mi;
|
|
mi.cbSize = sizeof(MENUITEMINFO);
|
|
mi.fMask = MIIM_ID;
|
|
::GetMenuItemInfo(m_hMenu, nInsertPoint, TRUE, &mi);
|
|
if (mi.wID == ID_FILE_MRU_FIRST)
|
|
break;
|
|
}
|
|
ATLASSERT(nInsertPoint < nItems && "You need a menu item with an ID = ID_FILE_MRU_FIRST");
|
|
|
|
int nItem;
|
|
for(nItem = ID_FILE_MRU_FIRST; nItem < ID_FILE_MRU_FIRST + m_nMaxEntries; nItem++)
|
|
{
|
|
// keep the first one as an insertion point
|
|
if (nItem != ID_FILE_MRU_FIRST)
|
|
::DeleteMenu(m_hMenu, nItem, MF_BYCOMMAND);
|
|
}
|
|
|
|
TCHAR szItemText[t_cchItemLen + 6]; // add space for &, 2 digits, and a space
|
|
int nSize = m_arrDocs.GetSize();
|
|
nItem = 0;
|
|
if(nSize > 0)
|
|
{
|
|
for(nItem = 0; nItem < nSize; nItem++)
|
|
{
|
|
if(m_cchMaxItemLen == -1)
|
|
{
|
|
wsprintf(szItemText, _T("&%i %s"), nItem + 1, m_arrDocs[nSize - 1 - nItem].szDocName);
|
|
}
|
|
else
|
|
{
|
|
TCHAR szBuff[t_cchItemLen];
|
|
T* pT = static_cast<T*>(this);
|
|
pT; // avoid level 4 warning
|
|
bool bRet = pT->CompactDocumentName(szBuff, m_arrDocs[nSize - 1 - nItem].szDocName, m_cchMaxItemLen);
|
|
bRet; // avoid level 4 warning
|
|
ATLASSERT(bRet);
|
|
wsprintf(szItemText, _T("&%i %s"), nItem + 1, szBuff);
|
|
}
|
|
::InsertMenu(m_hMenu, nInsertPoint + nItem, MF_BYPOSITION | MF_STRING, ID_FILE_MRU_FIRST + nItem, szItemText);
|
|
}
|
|
}
|
|
else // empty
|
|
{
|
|
::InsertMenu(m_hMenu, nInsertPoint, MF_BYPOSITION | MF_STRING, ID_FILE_MRU_FIRST, m_szNoEntries);
|
|
::EnableMenuItem(m_hMenu, ID_FILE_MRU_FIRST, MF_GRAYED);
|
|
nItem++;
|
|
}
|
|
::DeleteMenu(m_hMenu, nInsertPoint + nItem, MF_BYPOSITION);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Overrideable
|
|
// override to provide a different method of compacting document names
|
|
static bool CompactDocumentName(LPTSTR lpstrOut, LPCTSTR lpstrIn, int cchLen)
|
|
{
|
|
return AtlCompactPath(lpstrOut, lpstrIn, cchLen);
|
|
}
|
|
};
|
|
|
|
class CRecentDocumentList : public CRecentDocumentListBase<CRecentDocumentList>
|
|
{
|
|
public:
|
|
// nothing here
|
|
};
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CFindFile - file search helper class
|
|
|
|
class CFindFile
|
|
{
|
|
public:
|
|
// Data members
|
|
WIN32_FIND_DATA m_fd;
|
|
TCHAR m_lpszRoot[MAX_PATH];
|
|
TCHAR m_chDirSeparator;
|
|
HANDLE m_hFind;
|
|
BOOL m_bFound;
|
|
|
|
// Constructor/destructor
|
|
CFindFile() : m_hFind(NULL), m_chDirSeparator('\\'), m_bFound(FALSE)
|
|
{ }
|
|
|
|
~CFindFile()
|
|
{
|
|
Close();
|
|
}
|
|
|
|
// Attributes
|
|
ULONGLONG GetFileSize() const
|
|
{
|
|
ATLASSERT(m_hFind != NULL);
|
|
|
|
ULARGE_INTEGER nFileSize;
|
|
|
|
if(m_bFound)
|
|
{
|
|
nFileSize.LowPart = m_fd.nFileSizeLow;
|
|
nFileSize.HighPart = m_fd.nFileSizeHigh;
|
|
}
|
|
else
|
|
{
|
|
nFileSize.QuadPart = 0;
|
|
}
|
|
|
|
return nFileSize.QuadPart;
|
|
}
|
|
BOOL GetFileName(LPTSTR lpstrFileName, int cchLength) const
|
|
{
|
|
ATLASSERT(m_hFind != NULL);
|
|
if(lstrlen(m_fd.cFileName) >= cchLength)
|
|
return FALSE;
|
|
return (m_bFound && (lstrcpy(lpstrFileName, m_fd.cFileName) != NULL));
|
|
}
|
|
BOOL GetFilePath(LPTSTR lpstrFilePath, int cchLength) const
|
|
{
|
|
ATLASSERT(m_hFind != NULL);
|
|
|
|
int nLen = lstrlen(m_lpszRoot);
|
|
ATLASSERT(nLen > 0);
|
|
if(nLen == 0)
|
|
return FALSE;
|
|
bool bAddSep = (m_lpszRoot[nLen - 1] != '\\' && m_lpszRoot[nLen - 1] != '/');
|
|
|
|
if((lstrlen(m_lpszRoot) + (bAddSep ? 1 : 0)) >= cchLength)
|
|
return FALSE;
|
|
|
|
BOOL bRet = (lstrcpy(lpstrFilePath, m_lpszRoot) != NULL);
|
|
if(bRet)
|
|
{
|
|
TCHAR szSeparator[2] = { m_chDirSeparator, 0 };
|
|
bRet = (lstrcat(lpstrFilePath, szSeparator) != NULL);
|
|
}
|
|
return bRet;
|
|
}
|
|
BOOL GetFileTitle(LPTSTR lpstrFileTitle, int cchLength) const
|
|
{
|
|
ATLASSERT(m_hFind != NULL);
|
|
|
|
TCHAR szBuff[MAX_PATH];
|
|
if(!GetFileName(szBuff, MAX_PATH))
|
|
return FALSE;
|
|
TCHAR szNameBuff[_MAX_FNAME];
|
|
_tsplitpath(szBuff, NULL, NULL, szNameBuff, NULL);
|
|
if(lstrlen(szNameBuff) >= cchLength)
|
|
return FALSE;
|
|
return (lstrcpy(lpstrFileTitle, szNameBuff) != NULL);
|
|
}
|
|
BOOL GetFileURL(LPTSTR lpstrFileURL, int cchLength) const
|
|
{
|
|
ATLASSERT(m_hFind != NULL);
|
|
|
|
TCHAR szBuff[MAX_PATH];
|
|
if(!GetFilePath(szBuff, MAX_PATH))
|
|
return FALSE;
|
|
LPCTSTR lpstrFileURLPrefix = _T("file://");
|
|
if(lstrlen(szBuff) + lstrlen(lpstrFileURLPrefix) >= cchLength)
|
|
return FALSE;
|
|
if(lstrcpy(lpstrFileURL, lpstrFileURLPrefix) == NULL)
|
|
return FALSE;
|
|
return (lstrcat(lpstrFileURL, szBuff) != NULL);
|
|
}
|
|
BOOL GetRoot(LPTSTR lpstrRoot, int cchLength) const
|
|
{
|
|
ATLASSERT(m_hFind != NULL);
|
|
if(lstrlen(m_lpszRoot) >= cchLength)
|
|
return FALSE;
|
|
return (lstrcpy(lpstrRoot, m_lpszRoot) != NULL);
|
|
}
|
|
#ifndef _WTL_NO_CSTRING
|
|
CString GetFileName() const
|
|
{
|
|
ATLASSERT(m_hFind != NULL);
|
|
|
|
CString ret;
|
|
|
|
if(m_bFound)
|
|
ret = m_fd.cFileName;
|
|
return ret;
|
|
}
|
|
CString GetFilePath() const
|
|
{
|
|
ATLASSERT(m_hFind != NULL);
|
|
|
|
CString strResult = m_lpszRoot;
|
|
if(strResult[strResult.GetLength() - 1] != '\\' &&
|
|
strResult[strResult.GetLength() - 1] != '/')
|
|
strResult += m_chDirSeparator;
|
|
strResult += GetFileName();
|
|
return strResult;
|
|
}
|
|
CString GetFileTitle() const
|
|
{
|
|
ATLASSERT(m_hFind != NULL);
|
|
|
|
CString strFullName = GetFileName();
|
|
CString strResult;
|
|
|
|
_tsplitpath(strFullName, NULL, NULL, strResult.GetBuffer(MAX_PATH), NULL);
|
|
strResult.ReleaseBuffer();
|
|
return strResult;
|
|
}
|
|
CString GetFileURL() const
|
|
{
|
|
ATLASSERT(m_hFind != NULL);
|
|
|
|
CString strResult("file://");
|
|
strResult += GetFilePath();
|
|
return strResult;
|
|
}
|
|
CString GetRoot() const
|
|
{
|
|
ATLASSERT(m_hFind != NULL);
|
|
|
|
CString str = m_lpszRoot;
|
|
return str;
|
|
}
|
|
#endif //!_WTL_NO_CSTRING
|
|
BOOL GetLastWriteTime(FILETIME* pTimeStamp) const
|
|
{
|
|
ATLASSERT(m_hFind != NULL);
|
|
ATLASSERT(pTimeStamp != NULL);
|
|
|
|
if(m_bFound && pTimeStamp != NULL)
|
|
{
|
|
*pTimeStamp = m_fd.ftLastWriteTime;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
BOOL GetLastAccessTime(FILETIME* pTimeStamp) const
|
|
{
|
|
ATLASSERT(m_hFind != NULL);
|
|
ATLASSERT(pTimeStamp != NULL);
|
|
|
|
if(m_bFound && pTimeStamp != NULL)
|
|
{
|
|
*pTimeStamp = m_fd.ftLastAccessTime;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
BOOL GetCreationTime(FILETIME* pTimeStamp) const
|
|
{
|
|
ATLASSERT(m_hFind != NULL);
|
|
|
|
if(m_bFound && pTimeStamp != NULL)
|
|
{
|
|
*pTimeStamp = m_fd.ftCreationTime;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
BOOL MatchesMask(DWORD dwMask) const
|
|
{
|
|
ATLASSERT(m_hFind != NULL);
|
|
|
|
if(m_bFound)
|
|
return ((m_fd.dwFileAttributes & dwMask) != 0);
|
|
|
|
return FALSE;
|
|
}
|
|
BOOL IsDots() const
|
|
{
|
|
ATLASSERT(m_hFind != NULL);
|
|
|
|
// return TRUE if the file name is "." or ".." and
|
|
// the file is a directory
|
|
|
|
BOOL bResult = FALSE;
|
|
if(m_bFound && IsDirectory())
|
|
{
|
|
if(m_fd.cFileName[0] == '.' && (m_fd.cFileName[1] == '\0' || (m_fd.cFileName[1] == '.' && m_fd.cFileName[2] == '\0')))
|
|
bResult = TRUE;
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
BOOL IsReadOnly() const
|
|
{
|
|
return MatchesMask(FILE_ATTRIBUTE_READONLY);
|
|
}
|
|
BOOL IsDirectory() const
|
|
{
|
|
return MatchesMask(FILE_ATTRIBUTE_DIRECTORY);
|
|
}
|
|
BOOL IsCompressed() const
|
|
{
|
|
return MatchesMask(FILE_ATTRIBUTE_COMPRESSED);
|
|
}
|
|
BOOL IsSystem() const
|
|
{
|
|
return MatchesMask(FILE_ATTRIBUTE_SYSTEM);
|
|
}
|
|
BOOL IsHidden() const
|
|
{
|
|
return MatchesMask(FILE_ATTRIBUTE_HIDDEN);
|
|
}
|
|
BOOL IsTemporary() const
|
|
{
|
|
return MatchesMask(FILE_ATTRIBUTE_TEMPORARY);
|
|
}
|
|
BOOL IsNormal() const
|
|
{
|
|
return MatchesMask(FILE_ATTRIBUTE_NORMAL);
|
|
}
|
|
BOOL IsArchived() const
|
|
{
|
|
return MatchesMask(FILE_ATTRIBUTE_ARCHIVE);
|
|
}
|
|
|
|
// Operations
|
|
BOOL FindFile(LPCTSTR pstrName = NULL)
|
|
{
|
|
Close();
|
|
|
|
if(pstrName == NULL)
|
|
pstrName = _T("*.*");
|
|
lstrcpy(m_fd.cFileName, pstrName);
|
|
|
|
m_hFind = ::FindFirstFile(pstrName, &m_fd);
|
|
|
|
if(m_hFind == INVALID_HANDLE_VALUE)
|
|
return FALSE;
|
|
|
|
LPCTSTR pstr = _tfullpath(m_lpszRoot, pstrName, MAX_PATH);
|
|
|
|
// passed name isn't a valid path but was found by the API
|
|
ATLASSERT(pstr != NULL);
|
|
if(pstr == NULL)
|
|
{
|
|
Close();
|
|
::SetLastError(ERROR_INVALID_NAME);
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
// find the last forward or backward whack
|
|
LPTSTR pstrBack = _tcsrchr(m_lpszRoot, '\\');
|
|
LPTSTR pstrFront = _tcsrchr(m_lpszRoot, '/');
|
|
|
|
if(pstrFront != NULL || pstrBack != NULL)
|
|
{
|
|
if(pstrFront == NULL)
|
|
pstrFront = m_lpszRoot;
|
|
if(pstrBack == NULL)
|
|
pstrBack = m_lpszRoot;
|
|
|
|
// from the start to the last whack is the root
|
|
|
|
if(pstrFront >= pstrBack)
|
|
*pstrFront = '\0';
|
|
else
|
|
*pstrBack = '\0';
|
|
}
|
|
}
|
|
|
|
m_bFound = TRUE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL FindNextFile()
|
|
{
|
|
ATLASSERT(m_hFind != NULL);
|
|
|
|
if(m_hFind == NULL)
|
|
return FALSE;
|
|
|
|
if(!m_bFound)
|
|
return FALSE;
|
|
|
|
m_bFound = ::FindNextFile(m_hFind, &m_fd);
|
|
|
|
return m_bFound;
|
|
}
|
|
void Close()
|
|
{
|
|
m_bFound = FALSE;
|
|
|
|
if(m_hFind != NULL && m_hFind != INVALID_HANDLE_VALUE)
|
|
{
|
|
::FindClose(m_hFind);
|
|
m_hFind = NULL;
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Global functions for loading resources
|
|
|
|
inline HACCEL AtlLoadAccelerators(_U_STRINGorID table)
|
|
{
|
|
return ::LoadAccelerators(_Module.GetResourceInstance(), table.m_lpstr);
|
|
}
|
|
|
|
inline HMENU AtlLoadMenu(_U_STRINGorID menu)
|
|
{
|
|
return ::LoadMenu(_Module.GetResourceInstance(), menu.m_lpstr);
|
|
}
|
|
|
|
inline HBITMAP AtlLoadBitmap(_U_STRINGorID bitmap)
|
|
{
|
|
return ::LoadBitmap(_Module.GetResourceInstance(), bitmap.m_lpstr);
|
|
}
|
|
|
|
#ifdef OEMRESOURCE
|
|
inline HBITMAP AtlLoadSysBitmap(LPCTSTR lpBitmapName)
|
|
{
|
|
#ifdef _DEBUG
|
|
WORD wID = (WORD)lpBitmapName;
|
|
ATLASSERT(wID >= 32734 && wID <= 32767);
|
|
#endif //_DEBUG
|
|
return ::LoadBitmap(NULL, lpBitmapName);
|
|
}
|
|
#endif //OEMRESOURCE
|
|
|
|
inline HCURSOR AtlLoadCursor(_U_STRINGorID cursor)
|
|
{
|
|
return ::LoadCursor(_Module.GetResourceInstance(), cursor.m_lpstr);
|
|
}
|
|
|
|
inline HCURSOR AtlLoadSysCursor(LPCTSTR lpCursorName)
|
|
{
|
|
ATLASSERT(lpCursorName == IDC_ARROW || lpCursorName == IDC_IBEAM || lpCursorName == IDC_WAIT ||
|
|
lpCursorName == IDC_CROSS || lpCursorName == IDC_UPARROW || lpCursorName == IDC_SIZE ||
|
|
lpCursorName == IDC_ICON || lpCursorName == IDC_SIZENWSE || lpCursorName == IDC_SIZENESW ||
|
|
lpCursorName == IDC_SIZEWE || lpCursorName == IDC_SIZENS || lpCursorName == IDC_SIZEALL ||
|
|
lpCursorName == IDC_NO || lpCursorName == IDC_APPSTARTING || lpCursorName == IDC_HELP);
|
|
return ::LoadCursor(NULL, lpCursorName);
|
|
}
|
|
|
|
inline HICON AtlLoadIcon(_U_STRINGorID icon)
|
|
{
|
|
return ::LoadIcon(_Module.GetResourceInstance(), icon.m_lpstr);
|
|
}
|
|
|
|
inline HICON AtlLoadSysIcon(LPCTSTR lpIconName)
|
|
{
|
|
ATLASSERT(lpIconName == IDI_APPLICATION ||
|
|
lpIconName == IDI_ASTERISK ||
|
|
lpIconName == IDI_EXCLAMATION ||
|
|
lpIconName == IDI_HAND ||
|
|
lpIconName == IDI_QUESTION ||
|
|
lpIconName == IDI_WINLOGO);
|
|
return ::LoadIcon(NULL, lpIconName);
|
|
}
|
|
|
|
inline HBITMAP AtlLoadBitmapImage(_U_STRINGorID bitmap, UINT fuLoad = LR_DEFAULTCOLOR)
|
|
{
|
|
return (HBITMAP)::LoadImage(_Module.GetResourceInstance(), bitmap.m_lpstr, IMAGE_BITMAP, 0, 0, fuLoad);
|
|
}
|
|
|
|
inline HCURSOR AtlLoadCursorImage(_U_STRINGorID cursor, UINT fuLoad = LR_DEFAULTCOLOR | LR_DEFAULTSIZE, int cxDesired = 0, int cyDesired = 0)
|
|
{
|
|
return (HCURSOR)::LoadImage(_Module.GetResourceInstance(), cursor.m_lpstr, IMAGE_CURSOR, cxDesired, cyDesired, fuLoad);
|
|
}
|
|
|
|
inline HICON AtlLoadIconImage(_U_STRINGorID icon, UINT fuLoad = LR_DEFAULTCOLOR | LR_DEFAULTSIZE, int cxDesired = 0, int cyDesired = 0)
|
|
{
|
|
return (HICON)::LoadImage(_Module.GetResourceInstance(), icon.m_lpstr, IMAGE_ICON, cxDesired, cyDesired, fuLoad);
|
|
}
|
|
|
|
#ifdef OEMRESOURCE
|
|
inline HBITMAP AtlLoadSysBitmapImage(WORD wBitmapID, UINT fuLoad = LR_DEFAULTCOLOR)
|
|
{
|
|
ATLASSERT(wBitmapID >= 32734 && wBitmapID <= 32767);
|
|
ATLASSERT((fuLoad & LR_LOADFROMFILE) == 0); // this one doesn't load from a file
|
|
return (HBITMAP)::LoadImage(NULL, MAKEINTRESOURCE(wBitmapID), IMAGE_BITMAP, 0, 0, fuLoad);
|
|
}
|
|
#endif //OEMRESOURCE
|
|
|
|
inline HCURSOR AtlLoadSysCursorImage(_U_STRINGorID cursor, UINT fuLoad = LR_DEFAULTCOLOR | LR_DEFAULTSIZE, int cxDesired = 0, int cyDesired = 0)
|
|
{
|
|
#ifdef _DEBUG
|
|
WORD wID = (WORD)cursor.m_lpstr;
|
|
ATLASSERT((wID >= 32512 && wID <= 32516) || (wID >= 32640 && wID <= 32648) || (wID == 32650) || (wID == 32651));
|
|
ATLASSERT((fuLoad & LR_LOADFROMFILE) == 0); // this one doesn't load from a file
|
|
#endif //_DEBUG
|
|
return (HCURSOR)::LoadImage(NULL, cursor.m_lpstr, IMAGE_CURSOR, cxDesired, cyDesired, fuLoad);
|
|
}
|
|
|
|
inline HICON AtlLoadSysIconImage(_U_STRINGorID icon, UINT fuLoad = LR_DEFAULTCOLOR | LR_DEFAULTSIZE, int cxDesired = 0, int cyDesired = 0)
|
|
{
|
|
#ifdef _DEBUG
|
|
WORD wID = (WORD)icon.m_lpstr;
|
|
ATLASSERT(wID >= 32512 && wID <= 32517);
|
|
ATLASSERT((fuLoad & LR_LOADFROMFILE) == 0); // this one doesn't load from a file
|
|
#endif //_DEBUG
|
|
return (HICON)::LoadImage(NULL, icon.m_lpstr, IMAGE_ICON, cxDesired, cyDesired, fuLoad);
|
|
}
|
|
|
|
inline int AtlLoadString(UINT uID, LPTSTR lpBuffer, int nBufferMax)
|
|
{
|
|
return ::LoadString(_Module.GetResourceInstance(), uID, lpBuffer, nBufferMax);
|
|
}
|
|
|
|
inline bool AtlLoadString(UINT uID, BSTR& bstrText)
|
|
{
|
|
USES_CONVERSION;
|
|
ATLASSERT(bstrText == NULL);
|
|
|
|
LPTSTR lpstrText = NULL;
|
|
int nRes = 0;
|
|
for(int nLen = 256; ; nLen *= 2)
|
|
{
|
|
ATLTRY(lpstrText = new TCHAR[nLen]);
|
|
if(lpstrText == NULL)
|
|
break;
|
|
nRes = ::LoadString(_Module.GetResourceInstance(), uID, lpstrText, nLen);
|
|
if(nRes < nLen - 1)
|
|
break;
|
|
delete [] lpstrText;
|
|
lpstrText = NULL;
|
|
}
|
|
|
|
if(lpstrText != NULL)
|
|
{
|
|
if(nRes != 0)
|
|
bstrText = ::SysAllocString(T2OLE(lpstrText));
|
|
delete [] lpstrText;
|
|
}
|
|
|
|
return (bstrText != NULL) ? true : false;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Global functions for stock GDI objects
|
|
|
|
inline HPEN AtlGetStockPen(int nPen)
|
|
{
|
|
#if (_WIN32_WINNT >= 0x0500)
|
|
ATLASSERT(nPen == WHITE_PEN || nPen == BLACK_PEN || nPen == NULL_PEN || nPen == DC_PEN);
|
|
#else
|
|
ATLASSERT(nPen == WHITE_PEN || nPen == BLACK_PEN || nPen == NULL_PEN);
|
|
#endif //!(_WIN32_WINNT >= 0x0500)
|
|
return (HPEN)::GetStockObject(nPen);
|
|
}
|
|
|
|
inline HBRUSH AtlGetStockBrush(int nBrush)
|
|
{
|
|
#if (_WIN32_WINNT >= 0x0500)
|
|
ATLASSERT((nBrush >= WHITE_BRUSH && nBrush <= HOLLOW_BRUSH) || nBrush == DC_BRUSH);
|
|
#else
|
|
ATLASSERT(nBrush >= WHITE_BRUSH && nBrush <= HOLLOW_BRUSH);
|
|
#endif //!(_WIN32_WINNT >= 0x0500)
|
|
return (HBRUSH)::GetStockObject(nBrush);
|
|
}
|
|
|
|
inline HFONT AtlGetStockFont(int nFont)
|
|
{
|
|
ATLASSERT((nFont >= OEM_FIXED_FONT && nFont <= SYSTEM_FIXED_FONT) || nFont == DEFAULT_GUI_FONT);
|
|
return (HFONT)::GetStockObject(nFont);
|
|
}
|
|
|
|
inline HPALETTE AtlGetStockPalette(int nPalette)
|
|
{
|
|
ATLASSERT(nPalette == DEFAULT_PALETTE); // the only one supported
|
|
return (HPALETTE)::GetStockObject(nPalette);
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Global function for compacting a path by replacing parts with ellipsis
|
|
|
|
// helper for multi-byte character sets
|
|
inline bool _IsDBCSTrailByte(LPCTSTR lpstr, int nChar)
|
|
{
|
|
#ifndef _UNICODE
|
|
for(int i = nChar; i > 0; i--)
|
|
{
|
|
if(!::IsDBCSLeadByte(lpstr[i - 1]))
|
|
break;
|
|
}
|
|
return ((nChar > 0) && (((nChar - i) & 1) != 0));
|
|
#else //_UNICODE
|
|
lpstr; nChar;
|
|
return false;
|
|
#endif //_UNICODE
|
|
}
|
|
|
|
inline bool AtlCompactPath(LPTSTR lpstrOut, LPCTSTR lpstrIn, int cchLen)
|
|
{
|
|
ATLASSERT(lpstrOut != NULL);
|
|
ATLASSERT(lpstrIn != NULL);
|
|
ATLASSERT(cchLen > 0);
|
|
|
|
LPCTSTR szEllipsis = _T("...");
|
|
const int cchEndEllipsis = 3;
|
|
const int cchMidEllipsis = 4;
|
|
|
|
if(lstrlen(lpstrIn) + 1 < cchLen)
|
|
return (lstrcpy(lpstrOut, lpstrIn) != NULL);
|
|
|
|
// check if the separator is a slash or a backslash
|
|
TCHAR chSlash = _T('\\');
|
|
for(LPTSTR lpstr = (LPTSTR)lpstrIn; *lpstr != 0; lpstr = ::CharNext(lpstr))
|
|
{
|
|
if((*lpstr == _T('/')) || (*lpstr == _T('\\')))
|
|
chSlash = *lpstr;
|
|
}
|
|
|
|
// find the filename portion of the path
|
|
LPCTSTR lpstrFileName = lpstrIn;
|
|
for(LPCTSTR pPath = lpstrIn; *pPath; pPath = ::CharNext(pPath))
|
|
{
|
|
if((pPath[0] == _T('\\') || pPath[0] == _T(':') || pPath[0] == _T('/'))
|
|
&& pPath[1] && pPath[1] != _T('\\') && pPath[1] != _T('/'))
|
|
lpstrFileName = pPath + 1;
|
|
}
|
|
int cchFileName = lstrlen(lpstrFileName);
|
|
|
|
// handle just the filename without a path
|
|
if(lpstrFileName == lpstrIn && cchLen > cchEndEllipsis)
|
|
{
|
|
bool bRet = (lstrcpyn(lpstrOut, lpstrIn, cchLen - cchEndEllipsis) != NULL);
|
|
if(bRet)
|
|
{
|
|
#ifndef _UNICODE
|
|
if(_IsDBCSTrailByte(lpstrIn, cchLen - cchEndEllipsis))
|
|
lpstrOut[cchLen - cchEndEllipsis - 1] = 0;
|
|
#endif //_UNICODE
|
|
bRet = (lstrcat(lpstrOut, szEllipsis) != NULL);
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
// handle just ellipsis
|
|
if((cchLen < (cchMidEllipsis + cchEndEllipsis)))
|
|
{
|
|
for(int i = 0; i < cchLen - 1; i++)
|
|
lpstrOut[i] = ((i + 1) == cchMidEllipsis) ? chSlash : _T('.');
|
|
lpstrOut[i] = 0;
|
|
return true;
|
|
}
|
|
|
|
// calc how much we have to copy
|
|
int cchToCopy = cchLen - (cchMidEllipsis + cchFileName);
|
|
|
|
if(cchToCopy < 0)
|
|
cchToCopy = 0;
|
|
|
|
#ifndef _UNICODE
|
|
if(cchToCopy > 0 && _IsDBCSTrailByte(lpstrIn, cchToCopy))
|
|
cchToCopy--;
|
|
#endif //_UNICODE
|
|
|
|
bool bRet = (lstrcpyn(lpstrOut, lpstrIn, cchToCopy) != NULL);
|
|
if(!bRet)
|
|
return false;
|
|
|
|
// add ellipsis
|
|
bRet = (lstrcat(lpstrOut, szEllipsis) != NULL);
|
|
if(!bRet)
|
|
return false;
|
|
TCHAR szSlash[2] = { chSlash, 0 };
|
|
bRet = (lstrcat(lpstrOut, szSlash) != NULL);
|
|
if(!bRet)
|
|
return false;
|
|
|
|
// add filename (and ellipsis, if needed)
|
|
if(cchLen > (cchMidEllipsis + cchFileName))
|
|
{
|
|
bRet = (lstrcat(lpstrOut, lpstrFileName) != NULL);
|
|
}
|
|
else
|
|
{
|
|
cchToCopy = cchLen - cchMidEllipsis - cchEndEllipsis;
|
|
#ifndef _UNICODE
|
|
if(cchToCopy > 0 && _IsDBCSTrailByte(lpstrFileName, cchToCopy))
|
|
cchToCopy--;
|
|
#endif //_UNICODE
|
|
bRet = (lstrcpyn(&lpstrOut[cchMidEllipsis], lpstrFileName, cchToCopy) != NULL);
|
|
if(bRet)
|
|
bRet = (lstrcat(lpstrOut, szEllipsis) != NULL);
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
}; //namespace WTL
|
|
|
|
#endif // __ATLMISC_H__
|