//----------------------------------------------------------------------------
// ObjectWindows
// Copyright (c) 1992, 1996 by Borland International, All Rights Reserved
//
/// \file
/// Implementation of class TDC
//----------------------------------------------------------------------------
#include <owl/pch.h>
#include <owl/dc.h>
namespace owl {
OWL_DIAGINFO;
DIAG_DECLARE_GROUP(OwlGDI); // General GDI diagnostic group
// ---- class TDC::TFontUnicodeRanges
auto TDC::TFontUnicodeRanges::IsUnicodeCharSupported(wchar_t c) const -> bool {
for (auto const &range : GetRangeView()) {
if ( c >= range.wcLow
&& c < (range.wcLow + range.cGlyphs))
return true;
}
return false;
}
//
/// Sets OrgBrush, OrgPen, OrgFont, OrgBitmap, and OrgPalette to 0, and sets
/// ShouldDelete to true. This function is for internal use by the TDC constructors.
//
void
TDC::Init()
{
OrgBrush = 0;
OrgPen = 0;
OrgFont = 0;
OrgPalette = 0;
OrgBitmap = 0;
}
//
/// Creates a DC object "borrowing" the handle of an existing DC. The Handle data
/// member is set to the given handle argument.
//
TDC::TDC(HDC handle)
:
TGdiBase(handle, NoAutoDelete)
{
Init();
TRACEX(OwlGDI, OWL_CDLEVEL, _T("TDC constructed @") << (void*)this <<
_T(" from handle ") << static_cast<void*>(handle));
}
//
// Following two ctors are for use by derived classes only
/// Constructor for use by derived classes only
//
TDC::TDC()
{
Init();
TRACEX(OwlGDI, OWL_CDLEVEL, _T("TDC constructed @") << (void*)this);
}
//
/// Constructor for use by derived classes only.
//
TDC::TDC(HDC handle, TAutoDelete autoDelete)
:
TGdiBase(handle, autoDelete)
{
Init();
TRACEX(OwlGDI, OWL_CDLEVEL, _T("TDC constructed @") << (void*)this <<
_T(" from handle") << static_cast<void*>(handle));
}
//
/// Default dtor does not delete Handle
/// Calls RestoreObjects.
//
TDC::~TDC()
{
RestoreObjects();
TRACEX(OwlGDI, OWL_CDLEVEL, _T("TDC destructed @") << (void*)this);
}
//
/// Returns the attributes of the DC object.
//
HDC
TDC::GetAttributeHDC() const
{
return HDC(Handle);
}
//
/// Selects the given GDI pen object into this DC. The previously selected pen is saved in
/// the protected data member OrgPen, and can be restored by a call to RestorePen.
//
void
TDC::SelectObject(const TPen& pen)
{
TRACEX(OwlGDI, 1, _T("TDC::SelectPen @") << (void*)this <<
_T(" pen @") << (void*)&pen);
HPEN oldPen = (HPEN)::SelectObject(GetHDC(), pen);
if (oldPen) {
TGdiObject::RefInc(pen);
if (reinterpret_cast<UINT_PTR>(oldPen) > 1)
{
if (!OrgPen)
OrgPen = oldPen;
else
TGdiObject::RefDec(oldPen, false);
}
}
}
//
/// Selects the given GDI brush object into this DC. The previously selected brush is saved in
/// the protected data member OrgBrush, and can be restored by a call to RestoreBrush.
//
void
TDC::SelectObject(const TBrush& brush)
{
TRACEX(OwlGDI, 1, _T("TDC::SelectBrush @") << (void*)this <<
_T(" brush @") << (void*)&brush);
HBRUSH oldBrush = (HBRUSH)::SelectObject(GetHDC(), brush);
if (oldBrush) {
TGdiObject::RefInc(brush);
if (reinterpret_cast<UINT_PTR>(oldBrush) > 1)
{
if (!OrgBrush)
OrgBrush = oldBrush;
else
TGdiObject::RefDec(oldBrush, false);
}
}
}
//
/// Selects the given GDI font object into this DC. The previously selected font is saved in
/// the protected data member OrgFont, and can be restored by a call to RestoreFont.
//
void
TDC::SelectObject(const TFont& font)
{
TRACEX(OwlGDI, 1, _T("TDC::SelectFont @") << (void*)this <<
_T(" font @") << (void*)&font);
HFONT oldFont = (HFONT)::SelectObject(GetHDC(), font);
if (oldFont) {
TGdiObject::RefInc(font);
if (reinterpret_cast<UINT_PTR>(oldFont) > 1)
{
if (!OrgFont)
OrgFont = oldFont;
else
TGdiObject::RefDec(oldFont, false);
}
}
}
//
/// Selects the given GDI palette object into this DC. The previously selected palette is saved in
/// the protected data member OrgPalette, and can be restored by a call to RestorePalette.
///
/// If `forceBackgound` is set to `false` (the default), the selected logical palette is a
/// foreground palette when the window has input focus. If `forceBackground` is `true`, the
/// selected palette is always a background palette, whether the window has focus or not.
//
void
TDC::SelectObject(const TPalette& palette, bool forceBackground)
{
TRACEX(OwlGDI, 1, _T("TDC::SelectPalette @") << (void*)this <<
_T(" palette @") << (void*)&palette);
HPALETTE oldPalette = ::SelectPalette(GetHDC(), palette, forceBackground);
if (oldPalette) {
TGdiObject::RefInc(palette);
if (reinterpret_cast<UINT_PTR>(oldPalette) > 1)
{
if (!OrgPalette)
OrgPalette = oldPalette;
else
TGdiObject::RefDec(oldPalette, false);
}
}
}
//
/// Selects the given GDI bitmap object into this DC. The previously selected bitmap is saved in
/// the protected data member OrgBitmap, and can be restored by a call to RestoreBitmap.
//
void
TDC::SelectObject(const TBitmap& bitmap)
{
TRACEX(OwlGDI, 1, _T("TDC::SelectBitmap @") << (void*)this <<
_T(" bitmap @") << (void*)&bitmap);
HBITMAP oldBitmap = (HBITMAP)::SelectObject(GetHDC(), bitmap);
if (oldBitmap) {
TGdiObject::RefInc(bitmap);
if (reinterpret_cast<UINT_PTR>(oldBitmap) > 1)
{
if (!OrgBitmap)
OrgBitmap = oldBitmap;
else
TGdiObject::RefDec(oldBitmap, false);
}
}
}
//
/// Selects into the DC a predefined stock pen, brush, font, or palette.
/// For more information about the available stock objects and their indexes, see MSDN:
/// http://msdn.microsoft.com/en-us/library/windows/desktop/dd144925.aspx
//
void
TDC::SelectStockObject(int index)
{
PRECONDITION(::GetStockObject(index));
TRACEX(OwlGDI, 1, _T("TDC::SelectStockObject @") << (void*)this <<
_T(" index ") << index);
HANDLE oldObj = ::SelectObject(GetHDC(), ::GetStockObject(index));
if (reinterpret_cast<UINT_PTR>(oldObj) > 1)
TGdiObject::RefDec(oldObj, false);
}
//
/// Restores the original GDI pen object to this DC.
//
void
TDC::RestorePen()
{
TRACEX(OwlGDI, 1, _T("TDC::RestorePen @") << (void*)this);
if (OrgPen) {
TGdiObject::RefDec(::SelectObject(GetHDC(), OrgPen), false);
OrgPen = 0;
}
}
//
/// Restores the original GDI brush object to this DC.
//
void
TDC::RestoreBrush()
{
TRACEX(OwlGDI, 1, _T("TDC::RestoreBrush @") << (void*)this);
if (OrgBrush) {
TGdiObject::RefDec(::SelectObject(GetHDC(), OrgBrush), false);
OrgBrush = 0;
}
}
//
/// Restores the original GDI font object to this DC.
//
void
TDC::RestoreFont()
{
TRACEX(OwlGDI, 1, _T("TDC::RestoreFont @") << (void*)this);
if (OrgFont) {
TGdiObject::RefDec(::SelectObject(GetHDC(), OrgFont), false);
OrgFont = 0;
}
}
//
/// Restores the original GDI palette object to this DC.
//
void
TDC::RestorePalette()
{
TRACEX(OwlGDI, 1, _T("TDC::RestorePalette @") << (void*)this);
if (OrgPalette) {
TGdiObject::RefDec(::SelectPalette(GetHDC(), OrgPalette, false), false);
OrgPalette = 0;
}
}
//
/// Restores the original GDI bitmap object into this DC.
//
void
TDC::RestoreBitmap()
{
TRACEX(OwlGDI, 1, _T("TDC::RestoreBitmap @") << (void*)this);
if (OrgBitmap) {
TGdiObject::RefDec(::SelectObject(GetHDC(), OrgBitmap), false);
OrgBitmap = 0;
}
}
//
/// Restores all the original GDI objects to this DC.
//
void
TDC::RestoreObjects()
{
if (Handle) {
RestorePen();
RestoreBrush();
RestoreFont();
RestorePalette();
RestoreBitmap();
}
}
//
/// Returns a handle to the currently selected object of the given objectType
/// associated with this DC. Returns 0 if the call fails. objectType can be OBJ_PEN,
/// OBJ_BRUSH, OBJ_PAL, OBJ_FONT, or OBJ_BITMAP.
//
/// Subset of Win32 GetCurrentObject , just a straight
/// call for normal win32
//
HANDLE
TDC::GetCurrentObject(uint objectType) const
{
return ::GetCurrentObject(GetHDC(), objectType);
}
// !CQ add ROP arg to allow this to be used by docking drag frame painting
//
/// Draws a frame of the specified size and thickness with the given brush. The old
/// brush is restored after completion.
//
void
TDC::OWLFastWindowFrame(const TBrush& brush, const TRect& r, int xThick, int yThick, uint32 rop)
{
SelectObject(brush);
int width = r.Width() - xThick;
int height = r.Height() - yThick;
PatBlt(r.left, r.top, xThick, height, rop); // left
PatBlt(r.left+xThick, r.top, width, yThick, rop); // top
PatBlt(r.left, r.top+height, width, yThick, rop); // bottom
PatBlt(r.left+width, r.top+yThick, xThick, height, rop); // right
RestoreBrush();
}
//
/// Saves the current state of this DC on a context stack. The saved state can be
/// restored later with RestoreDC. Returns a value specifying the saved DC or 0 if
/// the call fails.
//
int
TDC::SaveDC() const
{
return ::SaveDC(GetHDC());
}
//
/// Restores the given savedDC. Returns true if the context is successfully
/// restored; otherwise, returns false.
//
bool
TDC::RestoreDC(int savedIndex)
{
return ::RestoreDC(GetHDC(), savedIndex);
}
//
/// Used under WIN3.1 or later, GetDeviceCaps returns capability information about
/// this DC. The index argument specifies the type of information required.
/// \todo Document the possible values for the index.
//
int
TDC::GetDeviceCaps(int index) const
{
return ::GetDeviceCaps(GetAttributeHDC(), index);
}
//
/// Functional style overload
//
TEXTMETRIC
TDC::GetTextMetrics() const
{
TEXTMETRIC t;
bool r = GetTextMetrics(t);
if (!r) throw TXGdi(IDS_GDIFAILURE, GetHDC());
return t;
}
//
/// Updates this DC using data in the given devMode structure. Returns true if the
/// call is successful; otherwise, returns false.
//
bool
TDC::ResetDC(const DEVMODE& devMode)
{
return ::ResetDC(GetHDC(), &devMode) != 0;
}
//
/// Sets the current background color of this DC to the given color value or the
/// nearest available. Returns 0x80000000 if the call fails.
//
TColor
TDC::SetBkColor(const TColor& color)
{
if (GetHDC() != GetAttributeHDC())
::SetBkColor(GetHDC(), color);
return ::SetBkColor(GetAttributeHDC(), color);
}
//
/// Sets the current text color of this DC to the given color value. The text color
/// determines the color displayed by TDC::TextOut and TDC::ExtTextOut.
//
TColor
TDC::SetTextColor(const TColor& color)
{
if (GetHDC() != GetAttributeHDC())
::SetTextColor(GetHDC(), color);
return ::SetTextColor(GetAttributeHDC(), color);
}
//
/// Sets the current window mapping mode of this DC to mode. Returns the previous
/// mapping mode value. The mapping mode defines how logical coordinates are mapped
/// to device coordinates. It also controls the orientation of the device's x- and
/// y-axes. See TDC::GetMapMode for a complete list of mapping modes.
//
int
TDC::SetMapMode(int mode)
{
if (GetHDC() != GetAttributeHDC())
::SetMapMode(GetHDC(), mode);
return ::SetMapMode(GetAttributeHDC(), mode);
}
//
/// Sets this DC's viewport origin to the given origin value, and saves the previous
/// origin in oldOrg. Returns true if the call is successful; otherwise, returns
/// false.
//
bool
TDC::SetViewportOrg(const TPoint& point, TPoint * oldOrg)
{
if (GetHDC() != GetAttributeHDC())
::SetViewportOrgEx(GetHDC(), point.x, point.y, oldOrg);
return ::SetViewportOrgEx(GetAttributeHDC(), point.x, point.y, oldOrg);
}
//
/// Modifies this DC's viewport origin relative to the current values. The delta x-
/// and y-components are added to the previous origin and the resulting point
/// becomes the new viewport origin. The previous origin is saved in oldOrg. Returns
/// true if the call is successful; otherwise, returns false.
//
bool
TDC::OffsetViewportOrg(const TPoint& delta, TPoint * oldOrg)
{
if (GetHDC() != GetAttributeHDC())
::OffsetViewportOrgEx(GetHDC(), delta.x, delta.y, oldOrg);
return ::OffsetViewportOrgEx(GetAttributeHDC(), delta.x, delta.y, oldOrg);
}
//
/// Sets this DC's viewport x- and y-extents to the given extent values. The
/// previous extents are saved in oldExtent. Returns true if the call is successful;
/// otherwise, returns false. The extent value determines the amount of stretching
/// or compression needed in the logical coordinate system to fit the device
/// coordinate system. extent also determines the relative orientation of the two
/// coordinate systems.
//
bool
TDC::SetViewportExt(const TSize& extent, TSize * oldExtent)
{
if (GetHDC() != GetAttributeHDC())
::SetViewportExtEx(GetHDC(), extent.cx, extent.cy, oldExtent);
return ::SetViewportExtEx(GetAttributeHDC(), extent.cx, extent.cy, oldExtent);
}
//
/// Modifies this DC's viewport extents relative to the current values. The new
/// extents are derived as follows:
/// \code
/// xNewVE = (xOldVE * xNum)/ xDenom
/// yNewVE = (I * yNum)/ yDenom
/// \endcode
/// The previous extents are saved in oldExtent. Returns true if the call is
/// successful; otherwise, returns false.
//
bool
TDC::ScaleViewportExt(int xNum, int xDenom, int yNum, int yDenom,
TSize * oldExtent)
{
if (GetHDC() != GetAttributeHDC())
::ScaleViewportExtEx(GetHDC(), xNum, xDenom, yNum, yDenom, oldExtent);
return ::ScaleViewportExtEx(GetAttributeHDC(), xNum, xDenom, yNum, yDenom, oldExtent);
}
//
/// Sets the origin of the window associated with this DC to the given origin value,
/// and saves the previous origin in oldOrg. Returns true if the call is successful;
/// otherwise, returns false.
//
bool
TDC::SetWindowOrg(const TPoint& point, TPoint * oldOrg)
{
if (GetHDC() != GetAttributeHDC())
::SetWindowOrgEx(GetHDC(), point.x, point.y, oldOrg);
return ::SetWindowOrgEx(GetAttributeHDC(), point.x, point.y, oldOrg);
}
//
/// Modifies this DC's window origin relative to the current values. The delta x-
/// and y-components are added to the previous origin and the resulting point
/// becomes the new window origin. The previous origin is saved in oldOrg. Returns
/// true if the call is successful; otherwise, returns false.
//
bool
TDC::OffsetWindowOrg(const TPoint& delta, TPoint * oldOrg)
{
if (GetHDC() != GetAttributeHDC())
::OffsetWindowOrgEx(GetHDC(), delta.x, delta.y, oldOrg);
return ::OffsetWindowOrgEx(GetAttributeHDC(), delta.x, delta.y, oldOrg);
}
//
/// Sets this DC's window x- and y-extents to the given extent values. The previous
/// extents are saved in oldExtent. Returns true if the call is successful;
/// otherwise, returns false. The extent value determines the amount of stretching
/// or compression needed in the logical coordinate system to fit the device
/// coordinate system. extent also determines the relative orientation of the two
/// coordinate systems.
//
bool
TDC::SetWindowExt(const TSize& extent, TSize * oldExtent)
{
if (GetHDC() != GetAttributeHDC())
::SetWindowExtEx(GetHDC(), extent.cx, extent.cy, oldExtent);
return ::SetWindowExtEx(GetAttributeHDC(), extent.cx, extent.cy, oldExtent);
}
//
/// Modifies this DC's window extents relative to the current values. The new
/// extents are derived as follows:
/// \code
/// xNewWE = (xOldWE * xNum)/ xDenom
/// yNewWE = (yOldWE * yNum)/ yDenom
/// \endcode
/// The previous extents are saved in oldExtent. Returns true if the call is
/// successful; otherwise, returns false.
//
bool
TDC::ScaleWindowExt(int xNum, int xDenom, int yNum, int yDenom, TSize * oldExtent)
{
if (GetHDC() != GetAttributeHDC())
::ScaleWindowExtEx(GetHDC(), xNum, xDenom, yNum, yDenom, oldExtent);
return ::ScaleWindowExtEx(GetAttributeHDC(), xNum, xDenom, yNum, yDenom, oldExtent);
}
//
/// Returns information about which Unicode characters are supported
/// by a font.
///
/// \param reuse (optional) existing TFontUnicodeRanges object for reusing the internal buffer.
///
/// \note because wchar_t/WCHAR is only 16bit wide this function can only
/// retrieve information for glyphs in the range 0 - 2^16. For glyphs
/// above that you need a different approach.
///
/// \returns TFontUnicodeRanges, which contains information about the glyphsets
/// of the selected font in the device context.
///
/// \exception TXGdi is thrown on failure.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-getfontunicoderanges">
/// GetFontUnicodeRanges</a> in the Windows API.
//
auto TDC::GetFontUnicodeRanges(TFontUnicodeRanges&& reuse) -> TDC::TFontUnicodeRanges {
auto glyphBlobByteSize = ::GetFontUnicodeRanges(GetHDC(), nullptr);
if (glyphBlobByteSize < sizeof(GLYPHSET))
throw TXGdi{IDS_GDIFAILURE, GetHDC()};
CHECK(0 == (glyphBlobByteSize - sizeof(GLYPHSET)) % sizeof(WCRANGE));
reuse.GlyphMem.resize(glyphBlobByteSize);
auto r = ::GetFontUnicodeRanges(GetHDC(), reinterpret_cast<LPGLYPHSET>(reuse.GlyphMem.data()));
if (r != glyphBlobByteSize)
throw TXGdi{IDS_GDIFAILURE, GetHDC()};
return std::move(reuse);
}
//
/// Draws up to count characters of the given null-terminated string in the current
/// font on this DC. If count is -1 (the default), the entire string is written.
/// The (x, y) or p arguments specify the logical coordinates of the reference point
/// that is used to align the first character, depending on the current
/// text-alignment mode. This mode can be inspected with TDC::GetTextAlign and
/// changed with TDC::SetTextAlign. By default, the current position is neither used
/// nor altered by TextOut. However, the align mode can be set to TA_UPDATECP, which
/// makes Windows use and update the current position. In this mode, TextOut ignores
/// the reference point argument(s).
/// TextOut returns true if the call is successful; otherwise, it returns false.
//
bool
TDC::TextOut(int x, int y, const tstring& str, int count)
{
return ::TextOut(GetHDC(), x, y, str.c_str(), count>=0 ? count : static_cast<int>(str.length()));
}
//
/// Draws up to count characters of the given null-terminated string in the current
/// font on this DC. If count is -1, the whole string is written.
/// The count (or the string length, if count is -1) may not exceed 8192.
/// An optional rectangle r can be specified for clipping, opaquing, or both, as
/// determined by the options value. If options is set to ETO_CLIPPED, the rectangle
/// is used for clipping the drawn text. If options is set to ETO_OPAQUE, the
/// current background color is used to fill the rectangle. Both options can be used
/// if ETO_CLIPPED is OR'd with ETO_OPAQUE.
/// The (x, y) orp arguments specify the logical coordinates of the reference point
/// that is used to align the first character. The current text-alignment mode can
/// be inspected with TDC::GetTextAlign and changed with TDC::SetTextAlign. By
/// default, the current position is neither used nor altered by ExtTextOut.
/// However, if the align mode is set to TA_UPDATECP, ExtTextOut ignores the
/// reference point argument(s) and uses or updates the current position as the
/// reference point.
/// The dx argument is an optional array of values used to set the distances between
/// the origins (upper left corners) of adjacent character cells. For example, dx[i]
/// represents the number of logical units separating the origins of character cells
/// i and i+1. If dx is 0, ExtTextOut uses the default inter-character spacings.
/// ExtTextOut returns true if the call is successful; otherwise, it returns false.
/// \see http://msdn.microsoft.com/en-us/library/dd162713.aspx
//
bool
TDC::ExtTextOut(int x, int y, uint16 options, const TRect* rect,
const tstring& str, int count, const int * dx)
{
const int strLength = static_cast<int>(str.length());
const int realCount = count >= 0 ? count : strLength;
PRECONDITION(realCount <= std::min(strLength, 8192));
return ::ExtTextOut(GetHDC(), x, y, options, rect, str.c_str(), realCount, dx);
}
//
/// Draws up to count characters of the given null-terminated string in the current
/// font on this DC. If count is -1, the whole string is written.
/// Tabs are expanded according to the given arguments. The positions array
/// specifies numPositions tab stops given in device units. The tab stops must have
/// strictly increasing values in the array. If numPositions and positions are both
/// 0, tabs are expanded to eight times the average character width. If numPositions
/// is 1, all tab stops are taken to be positions[0] apart. tabOrigin specifies the
/// x-coordinate in logical units from which tab expansion will start.
/// The p argument specifies the logical coordinates of the reference point that is
/// used to align the first character depending on the current text-alignment mode.
/// This mode can be inspected with TDC::GetTextAlign and changed with
/// TDC::SetTextAlign. By default, the current position is neither used nor altered
/// by TabbedTextOut. However, if the align mode is set to TA_UPDATECP,
/// TabbedTextOut ignores the reference point argument(s) and uses/updates the
/// current position as the reference point.
/// The size argument in the second version of TabbedTextOut reports the dimensions
/// (size.y = height and size.x = width) of the string in logical units.
/// TabbedTextOut returns true if the call is successful; otherwise, it returns
/// false.
//
bool
TDC::TabbedTextOut(const TPoint& p, const tstring& str, int count,
int numPositions, const int* positions, int tabOrigin, TSize& size)
{
LONG r = ::TabbedTextOut(GetHDC(), p.x, p.y, str.c_str(),
count >= 0 ? count : static_cast<int>(str.length()),
numPositions,
const_cast<int*>(positions), // Cast for legacy compatibility.
tabOrigin);
size = TSize::CreateFromPackedExtents(r);
return r != 0;
}
//
/// Overload for const TRect&
/// For obvious reasons, this overload does not support the DT_CALCRECT format flag.
/// If the given format contains the DT_CALCRECT flag, the function returns 0.
/// Otherwise, see the documentation for the overload for non-const TRect.
//
int
TDC::DrawText(const tstring& str, int count, const TRect& rect, uint16 format)
{
if ((format & DT_CALCRECT) != 0) return 0;
TRect r = rect;
return DrawText(str, count, r, format); // Forward to virtual overload for non-const TRect.
}
//
/// Formats and draws in the given rectangle, r, up to count characters of the
/// null-terminated string using the current font for this DC. If count is -1, the
/// whole string is written. The rectangle must be specified in logical units.
/// Formatting is controlled with the format argument, which can be various
/// combinations of the following values:
/// - \c \b DT_BOTTOM Specifies bottom-justified text. This value must be combined (bitwise
/// OR'd) with DT_SINGLELINE.
/// - \c \b DT_CALCRECT Determines the width and height of the rectangle. If there are
/// multiple lines of text, DrawText uses the width of r (the rectangle argument)
/// and extends the base of the rectangle to bound the last line of text. If there
/// is only one line of text, DrawText uses a modified value for the right side of r
/// so that it bounds the last character in the line. In both cases, DrawText
/// returns the height of the formatted text but does not draw the text.
/// - \c \b DT_CENTER Centers text horizontally.
/// - \c \b DT_EXPANDTABS Expands tab characters. The default number of characters per tab
/// is eight.
/// - \c \b DT_EXTERNALLEADING Includes the font external leading in line height. Normally,
/// external leading is not included in the height of a line of text.
/// - \c \b DT_LEFT Aligns text flush-left.
/// - \c \b DT_NOCLIP Draws without clipping. DrawText is somewhat faster when DT_NOCLIP is
/// used.
/// - \c \b DT_NOPREFIX Turns off processing of prefix characters. Normally, DrawText
/// interprets the prefix character & as a directive to underscore the character
/// that follows, and the prefix characters && as a directive to print a single &.
/// By specifying DT_NOPREFIX, this processing is turned off.
/// - \c \b DT_RIGHT Aligns text flush-right.
/// - \c \b DT_SINGLELINE Specifies single line only. Carriage returns and linefeeds do not
/// break the line.
/// - \c \b DT_TABSTOP Sets tab stops. Bits 15-8 (the high-order byte of the low-order
/// word) of the format argument are the number of characters for each tab. The
/// default number of characters per tab is eight.
/// - \c \b DT_TOP Specifies top-justified text (single line only).
/// - \c \b DT_VCENTER Specifies vertically centered text (single line only).
/// - \c \b DT_WORDBREAK Specifies word breaking. Lines are automatically broken between
/// words if a word would extend past the edge of the rectangle specified by r. A
/// carriage return/line sequence will also break the line.
///
/// \note Note that the DT_CALCRECT, DT_EXTERNALLEADING, DT_INTERNAL, DT_NOCLIP, and
/// DT_NOPREFIX values cannot be used with the DT_TABSTOP value.
/// DrawText uses this DC's currently selected font, text color, and background
/// color to draw the text. Unless the DT_NOCLIP format is used, DrawText clips the
/// text so that it does not appear outside the given rectangle. All formatting is
/// assumed to have multiple lines unless the DT_SINGLELINE format is given.
/// If the selected font is too large for the specified rectangle, DrawText does not
/// attempt to substitute a smaller font.
/// If the function succeeds, the return value is the height of the text. If the
/// function fails, the return value is zero.
//
int
TDC::DrawText(const tstring& str, int count, TRect& rect, uint16 format)
{
return ::DrawText(GetHDC(), str.c_str(), count, &rect, format);
}
//
/// Formats and draws in the given rectangle, r, up to count characters of the
/// null-terminated string using the current font for this DC. If count is -1, the
/// whole string is written. The rectangle must be specified in logical units.
/// Formatting is controlled with the format argument, which can be various
/// combinations of the following values:
/// - \c \b DT_BOTTOM Specifies bottom-justified text. This value must be combined (bitwise
/// OR'd) with DT_SINGLELINE.
/// - \c \b DT_CALCRECT Determines the width and height of the rectangle. If there are
/// multiple lines of text, DrawText uses the width of r (the rectangle argument)
/// and extends the base of the rectangle to bound the last line of text. If there
/// is only one line of text, DrawText uses a modified value for the right side of r
/// so that it bounds the last character in the line. In both cases, DrawText
/// returns the height of the formatted text but does not draw the text.
/// - \c \b DT_CENTER Centers text horizontally.
/// - \c \b DT_EDITCONTROL Duplicates the text-displaying characteristics of a multiline
/// edit control. Specifically, the average character width is calculated in the
/// same manner as for an edit control, and the function does not display a
/// partially visible last line.
/// - \c \b DT_END_ELLIPSIS or DT_PATH_ELLIPSIS Replaces part of the given string with
/// ellipses, if necessary, so that the result fits in the specified rectangle. The
/// given string is not modified unless the DT_MODIFYSTRING flag is specified.You
/// can specify DT_END_ELLIPSIS to replace characters at the end of the string, or
/// - \c \b DT_PATH_ELLIPSIS to replace characters in the middle of the string. If the
/// string contains backslash (\) characters, DT_PATH_ELLIPSIS preserves as much as
/// possible of the text after the last backslash.
/// - \c \b DT_EXPANDTABS Expands tab characters. The default number of characters per tab
/// is eight.
/// - \c \b DT_EXTERNALLEADING Includes the font external leading in line height. Normally,
/// external leading is not included in the height of a line of text.
/// - \c \b DT_INTERNAL Uses the system font to calculate text metrics.
/// - \c \b DT_LEFT Aligns text flush-left.
/// - \c \b DT_MODIFYSTRING Modifies the given string to match the displayed text. This flag
/// has no effect unless the DT_END_ELLIPSIS or DT_PATH_ELLIPSIS flag is specified.
/// - \c \b DT_NOCLIP Draws without clipping. DrawText is somewhat faster when DT_NOCLIP is
/// used.
/// - \c \b DT_NOPREFIX Turns off processing of prefix characters. Normally, DrawText
/// interprets the prefix character & as a directive to underscore the character
/// that follows, and the prefix characters && as a directive to print a single &.
/// By specifying DT_NOPREFIX, this processing is turned off.
/// - \c \b DT_RIGHT Aligns text flush-right.
/// - \c \b DT_RTLREADING Layout in right to left reading order for bi-directional text when
/// the font selected into the hdc is a Hebrew or Arabic font. The default reading
/// order for all text is left to right.
/// - \c \b DT_SINGLELINE Specifies single line only. Carriage returns and linefeeds do not
/// break the line.
/// - \c \b DT_TABSTOP Sets tab stops. Bits 15-8 (the high-order byte of the low-order
/// word) of the format argument are the number of characters for each tab. The
/// default number of characters per tab is eight.
/// - \c \b DT_TOP Specifies top-justified text (single line only).
/// - \c \b DT_VCENTER Specifies vertically centered text (single line only).
/// - \c \b DT_WORDBREAK Specifies word breaking. Lines are automatically broken between
/// words if a word would extend past the edge of the rectangle specified by r. A
/// carriage return/line sequence will also break the line.
/// - \c \b DT_WORD_ELLIPSIS Truncates text that does not fit in the rectangle and adds
/// ellipses.
///
/// \note Note that the DT_CALCRECT, DT_EXTERNALLEADING, DT_INTERNAL, DT_NOCLIP, and
/// DT_NOPREFIX values cannot be used with the DT_TABSTOP value.
/// DrawText uses this DC's currently selected font, text color, and background
/// color to draw the text. Unless the DT_NOCLIP format is used, DrawText clips the
/// text so that it does not appear outside the given rectangle. All formatting is
/// assumed to have multiple lines unless the DT_SINGLELINE format is given.
/// If the selected font is too large for the specified rectangle, DrawText does not
/// attempt to substitute a smaller font.
/// If the function succeeds, the return value is the height of the text. If the
/// function fails, the return value is zero.
/// Windows NT: To get extended error information, call GetLastError.
//
int
TDC::DrawTextEx(LPTSTR str, int count, TRect* rect, uint format,
LPDRAWTEXTPARAMS params)
{
if (params)
params->cbSize = sizeof(DRAWTEXTPARAMS);
return ::DrawTextEx(GetHDC(), str, count, rect, format, params);
}
//
//
//
bool //JBC
TDC::DrawFrameControl(TRect lpRect, UINT nType, UINT nState)
{
RECT rect;
rect.left = lpRect.Left();
rect.top = lpRect.Top();
rect.right = lpRect.Right();
rect.bottom = lpRect.Bottom();
return ::DrawFrameControl((HDC)Handle, &rect, nType, nState);
}
//
/// Draws in the given rectangle (r) up to count characters of gray text from string
/// using the given brush, brush, and the current font for this DC. If count is -1
/// and string is null-terminated, the whole string is written. The rectangle must
/// be specified in logical units. If brush is 0, the text is grayed with the same
/// brush used to draw window text on this DC. Gray text is primarily used to
/// indicate disabled commands and menu items.
/// GrayString writes the selected text to a memory bitmap, grays the bitmap, then
/// displays the result. The graying is performed regardless of the current brush
/// and background color.
/// The outputFunc pointer to a function can specify the procedure instance of an
/// application-supplied drawing function and is defined as
/// \code
/// typedef BOOL (CALLBACK* GRAYSTRINGPROC)(HDC, LPARAM, int);
/// \endcode
/// If outputFunc is 0, GrayString uses TextOut and string is assumed to be a
/// normal, null-terminated character string. If string cannot be handled by TextOut
/// (for example, the string is stored as a bitmap), you must provide a suitable
/// drawing function via outputFunc.
/// If the device supports a solid gray color, it is possible to draw gray strings
/// directly without using GraySring. Call GetSysColor to find the color value; for
/// example, G of COLOR_GRAYTEXT. If G is nonzero (non-black), you can set the text
/// color with SetTextColor(G) and then use any convenient text-drawing function.
/// GrayString returns true if the call is successful; otherwise, it returns false.
/// Failure can result if TextOut or outputFunc return false, or if there is
/// insufficient memory to create the bitmap.
//
bool
TDC::GrayString(const TBrush& brush, GRAYSTRINGPROC outputFunc,
const tstring& str, int count, const TRect& rect)
{
return ::GrayString(GetHDC(), brush, outputFunc, reinterpret_cast<LPARAM>(str.c_str()),
count >= 0 ? count : 0, rect.left, rect.top, rect.Width(), rect.Height());
}
//
// For use with CopyText.
//
struct TGetTextFace
{
const TDC& dc;
TGetTextFace(const TDC& d) : dc(d) {}
int operator() (LPTSTR buf, int buf_size)
{return dc.GetTextFace(buf_size - 1, buf);}
};
tstring
TDC::GetTextFace() const
{
return CopyText(GetTextFaceLength(), TGetTextFace(*this));
}
// DLN (stripped from TUIHandle)
// Draws an edge using system ::DrawEdge if available, otherwise
// does it the hard way
bool
TDC::DrawEdge(const TRect& frame, uint edge, uint flags)
{
static int hasDrawEdge = true;
// Try once to see if the API call is available. If not, do ourselves.
//
if (hasDrawEdge) {
if (::DrawEdge(*this, (LPRECT)&frame, edge, flags))
return true;
if (::GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
hasDrawEdge = false;
else
return false;
}
// ::DrawEdge is not available, do the drawing ourselves
//
TRect f(frame); // working frame rectangle
// If mono is set, draw a thin, flat, black (windowFrame) frame
//
if (flags & Mono)
{
if (edge & EdgeOuter)
{
// KSM PaintFrame(*this, f, flags, TColor::SysWindowFrame, TColor::SysWindowFrame);
PaintFrame(f, flags, TColor::SysWindowFrame, TColor::SysWindowFrame);
f.Inflate(-1,-1);
}
if (flags & Fill)
{ // !CQ repeated code--nest else?
TBrush brsh(TColor::SysWindow);
SelectObject(brsh);
PatBlt(f);
RestoreBrush();
}
return true;
}
// If flat is set, draw a thin, flat, shadow frame
//
if (flags & Flat)
{
if (edge & EdgeOuter)
{
PaintFrame(f, flags, TColor::Sys3dShadow, TColor::Sys3dShadow);
f.Inflate(-1,-1);
}
if (flags & Fill)
{ // !CQ repeated code--nest else?
TBrush brsh(TColor::Sys3dFace);
SelectObject(brsh);
PatBlt(f);
RestoreBrush();
}
return true;
}
// Draw outer edge if indicated, adjusting rect afterwards
//
if (edge & EdgeOuter) {
static TColor tlColors[] = {
TColor::Sys3dLight, // EdgeRaised
TColor::Sys3dHilight, // EdgeRaised + Soft
TColor::Sys3dShadow, // EdgeSunken
TColor::Sys3dDkShadow, // EdgeSunken + Soft
};
static TColor brColors[] = {
TColor::Sys3dDkShadow, // EdgeRaised
TColor::Sys3dDkShadow, // EdgeRaised + Soft
TColor::Sys3dHilight, // EdgeSunken
TColor::Sys3dHilight, // EdgeSunken + Soft
};
int ci = ((edge & SunkenOuter) ? 2 : 0) | ((flags & Soft) ? 1 : 0);
//KSM PaintFrame(*this, f, flags, tlColors[ci], brColors[ci]);
PaintFrame(f, flags, tlColors[ci], brColors[ci]);
f.Inflate(-1,-1);
}
// Draw inner edge if indicated, adjusting rect afterwards
//
if (edge & EdgeInner) {
static TColor tlColors[] = {
TColor::Sys3dHilight, // EdgeRaised
TColor::Sys3dLight, // EdgeRaised + Soft
TColor::Sys3dDkShadow, // EdgeSunken
TColor::Sys3dShadow, // EdgeSunken + Soft
};
static TColor brColors[] = {
TColor::Sys3dShadow, // EdgeRaised
TColor::Sys3dShadow, // EdgeRaised + Soft
TColor::Sys3dLight, // EdgeSunken
TColor::Sys3dLight, // EdgeSunken + Soft
};
int ci = ((edge & SunkenOuter) ? 2 : 0) | ((flags & Soft) ? 1 : 0);
//KSM PaintFrame(*this, f, flags, tlColors[ci], brColors[ci]);
PaintFrame(f, flags, tlColors[ci], brColors[ci]);
f.Inflate(-1,-1);
}
// Fill interior if indicated
//
if (flags & Fill) {
TBrush brsh(TColor::Sys3dFace);
SelectObject(brsh);
PatBlt(f);
RestoreBrush();
}
// !CQ not really usefull since frame is not returned
// if (flags & Adjust)
// frame = f;
return true;
}
// DLN ripped from TUIHandle
// Paint a 2-color single pixel thick frame, bevel corners get their own color
//
/*void
TDC::PaintFrame(const TRect& fr, uint flags, const TColor& tlColor, const TColor& brColor)
{
if (flags & (Top | Left)) {
TBrush brsh(tlColor);
dc.SelectObject(brsh);
if (flags & Top)
dc.PatBlt(fr.left, fr.top, fr.Width()-1, 1);
if (flags & Left)
dc.PatBlt(fr.left, fr.top+1, 1, fr.Height()-2);
dc.RestoreBrush();
}
if (flags & (Bottom | Right)) {
TBrush brsh(brColor);
dc.SelectObject(brsh);
if (flags & Bottom)
dc.PatBlt(fr.left, fr.bottom-1, fr.Width(), 1);
if (flags & Right)
dc.PatBlt(fr.right-1, fr.top, 1, fr.Height()-1);
dc.RestoreBrush();
}
}
*/
void
TDC::PaintFrame(const TRect& fr, uint flags, const TColor& tlColor, const TColor& brColor)
{
if (flags & (Top | Left)) {
TBrush brsh(tlColor);
SelectObject(brsh);
if (flags & Top)
PatBlt(fr.left, fr.top, fr.Width()-1, 1);
if (flags & Left)
PatBlt(fr.left, fr.top+1, 1, fr.Height()-2);
RestoreBrush();
}
if (flags & (Bottom | Right)) {
TBrush brsh(brColor);
SelectObject(brsh);
if (flags & Bottom)
PatBlt(fr.left, fr.bottom-1, fr.Width(), 1);
if (flags & Right)
PatBlt(fr.right-1, fr.top, 1, fr.Height()-1);
RestoreBrush();
}
}
} // OWL namespace
/* ========================================================================== */
↑ V524 It is odd that the body of 'GetAttributeHDC' function is fully equivalent to the body of 'GetHDC' function (dc.h, line 1274).
↑ V601 The 'true' value is implicitly cast to the integer type.
↑ V601 The 'false' value is implicitly cast to the integer type.
↑ V813 Decreased performance. The 'lpRect' argument should probably be rendered as a constant reference.