//----------------------------------------------------------------------------
// ObjectWindows
// Copyright (c) 1995, 1996 by Borland International, All Rights Reserved
//
/// \file
/// Implementation of class TRichEdit.
//----------------------------------------------------------------------------
#include <owl/pch.h>
#include <owl/richedit.h>
#include <owl/filename.h>
#if defined(__BORLANDC__)
# pragma option -w-ccc // Disable "Condition is always true/false"
#endif
using namespace std;
namespace owl {
OWL_DIAGINFO;
// Range of the editor's font size
//
const int MinPointSize = 6;
const int MaxPointSize = 128;
// System DLL providing underlying support for RichEdit controls
//
const tchar RichEditDllName[] = _T("RICHED32.DLL");
const tchar RichEdit20DllName[] = _T("RICHED20.DLL");
#if defined(UNICODE)
# if defined(MSFTEDIT_CLASS)
# define OWL_USERICHEDIT41
# endif
#endif
#if defined(OWL_USERICHEDIT41)
const tchar RichEdit41DllName[] = _T("Msftedit.dll");
#endif
////////////////////////////////////////////////////////////////////////////////
//
//
//
/// Constructs a CharacterFormat structure from the current character attributes of
/// a RICHEDIT control.
/// \note Specifying 'true' for selection will return the attribute of the character
/// at the current location if there are no block of data selected in the control.
//
TCharFormat::TCharFormat(const TRichEdit& edit, bool selection, ulong mask)
{
PRECONDITION((HWND)edit);
memset(this, 0, sizeof(CHARFORMAT2));
cbSize = sizeof(CHARFORMAT);
if(TRichEditDll::Dll()->GetVersion() >= 2)
cbSize = sizeof(CHARFORMAT2);
dwMask = mask;
edit.GetCharFormat(*this, selection);
}
//
/// Toggles the bold character attribute according to the boolean parameter specified.
//
void
TCharFormat::EnableBold(bool flag)
{
dwMask |= CFM_BOLD;
if (flag)
dwEffects |= CFE_BOLD;
else
dwEffects &= ~CFE_BOLD;
}
//
/// Toggles italic character attribute based on the boolean parameter specified.
//
void
TCharFormat::EnableItalic(bool flag)
{
dwMask |= CFM_ITALIC;
if (flag)
dwEffects |= CFE_ITALIC;
else
dwEffects &= ~CFE_ITALIC;
}
//
/// Toggles the underline character attribute based on the boolean parameter specified.
//
void
TCharFormat::EnableUnderline(bool flag)
{
dwMask |= CFM_UNDERLINE;
if (flag)
dwEffects |= CFE_UNDERLINE;
else
dwEffects &= ~CFE_UNDERLINE;
}
//
/// Toggles the strike-out character attribute based on the boolean parameter specified.
//
void
TCharFormat::EnableStrikeOut(bool flag)
{
dwMask |= CFM_STRIKEOUT;
if (flag)
dwEffects |= CFE_STRIKEOUT;
else
dwEffects &= ~CFE_STRIKEOUT;
}
//
/// Toggles the protected character attribute based on the boolean parameter specified.
//
void
TCharFormat::EnableProtected(bool flag)
{
dwMask |= CFM_PROTECTED;
if (flag)
dwEffects |= CFE_PROTECTED;
else
dwEffects &= ~CFE_PROTECTED;
}
//
/// Retrieves the character color stored in the CHARFORMAT structure.
/// \note Default to system text color of no explicit color was set in the
/// CHARFORMAT structure.
//
TColor
TCharFormat::GetTextColor() const
{
if ((dwMask & CFM_COLOR) && !(dwEffects & CFE_AUTOCOLOR))
return TColor(crTextColor);
return TColor::SysWindowText;
}
//
/// Updates the CHARFORMAT structure with the specified color.
/// \note If 'TColor::None' is specified, enable the flag specifying that the color
/// should default to the system text color.
//
void
TCharFormat::SetTextColor(const TColor& color)
{
dwMask |= CFM_COLOR;
if (color == TColor::None)
dwEffects |= CFE_AUTOCOLOR;
else {
dwEffects &= ~CFE_AUTOCOLOR;
crTextColor = color;
}
}
//
/// Sets the face name of the font.
//
void
TCharFormat::SetFaceName(LPCTSTR name)
{
// !BB No unicode support!!!!!!!!!!
::_tcscpy(szFaceName, name);
}
//
/// Sets the character set of the font. Valid values include the following:
/// ANSI_CHARSET, OEM_CHARSET, and SYMBOL_CHARSET.
//
void
TCharFormat::SetCharSet(uint8 charSet)
{
bCharSet = charSet;
}
//
/// Sets the pitch and family of the font. The two lower-bits specify the pitch of
/// the font and can be one of the following values:
/// DEFAULT_PITCH, FIXED_PITCH, or VARIABLE_PITCH.
///
/// Bits 4 through 7 of the member specify the font family and can be one of the
/// following values:
/// FF_DECORATIVE, FF_DONTCARE, FF_MODERN, FF_ROMAN, FF_SCRIPT or FF_SWISS.
//
void
TCharFormat::SetPitchAndFamily(uint8 pitchFam)
{
bPitchAndFamily = pitchFam;
}
//
/// Sets the character height.
//
void
TCharFormat::SetHeight(long height)
{
dwMask |= CFM_SIZE;
yHeight = height;
}
//
/// Sets the character offset from the baseline. If the parameter is positive, the
/// character is a superscript; if it is negative, the character is a subscript.
//
void
TCharFormat::SetOffset(long offset)
{
dwMask |= CFM_OFFSET;
yOffset = offset;
}
//
/// Transfers the information currently in the underlying CHARFORMAT structure to a
/// LOGFONT structure. This is useful when changing the editor's font, as
/// initialized LOGFONT structure can subsequently be used when invoking the FONT
/// Common Dialog (i.e., TChooseFontDialog).
//
void
TCharFormat::SetFontInfo(LOGFONT& lf) const
{
memset(&lf, 0, sizeof(LOGFONT));
if (dwMask & CFM_SIZE) // (1 Point == 20 twips)
lf.lfHeight = yHeight/-20;
if (dwMask & CFM_BOLD)
lf.lfWeight = dwEffects & CFE_BOLD ? FW_BOLD : FW_NORMAL;
if (dwMask & CFM_ITALIC)
lf.lfItalic = (uint8)(dwEffects & CFE_ITALIC ? TRUE : FALSE);
if (dwMask & CFM_UNDERLINE)
lf.lfUnderline = (uint8)(dwEffects & CFE_UNDERLINE ? TRUE : FALSE);
if (dwMask & CFM_FACE) {
lf.lfPitchAndFamily = bPitchAndFamily;
::_tcscpy(lf.lfFaceName, szFaceName);
}
lf.lfCharSet = DEFAULT_CHARSET;
lf.lfQuality = DEFAULT_QUALITY;
}
//
/// Initializes the underlying CHARFORMAT structure using the information stored in
/// a LOGFONT structure.
//
void
TCharFormat::GetFontInfo(const LOGFONT& lf)
{
dwMask = (CFM_SIZE | CFM_BOLD | CFM_ITALIC | CFM_OFFSET|
CFM_STRIKEOUT | CFM_UNDERLINE | CFM_FACE);
yHeight = lf.lfHeight * -20;
if (FW_BOLD == lf.lfWeight)
dwEffects |= CFE_BOLD;
if (lf.lfItalic)
dwEffects |= CFE_ITALIC;
if (lf.lfStrikeOut)
dwEffects |= CFE_STRIKEOUT;
if (lf.lfUnderline)
dwEffects |= CFE_UNDERLINE;
bPitchAndFamily = lf.lfPitchAndFamily;
::_tcscpy(szFaceName, lf.lfFaceName);
}
//
/// Sets Font weight (LOGFONT value).
/// \note RichEdit 2.0 specific
void
TCharFormat::SetWeight(uint16 weigh)
{
dwMask |= CFM_WEIGHT;
wWeight = weigh;
}
//
/// Sets Amount to space between letters.
/// \note RichEdit 2.0 specific
void
TCharFormat::SetSpacing(int16 spacing)
{
dwMask |= CFM_SPACING;
wWeight = spacing;
}
//
/// Sets Background color.
/// \note RichEdit 2.0 specific
void
TCharFormat::SetBkColor(const TColor& clr)
{
dwMask |= CFM_COLOR;
crBackColor = clr;
}
//
// Locale ID
// RichEdit 2.0 specific
void
TCharFormat::SetLCID(LCID _lcid)
{
dwMask |= CFM_LCID;
lcid = _lcid;
}
//
/// Sets Style handle.
/// \note RichEdit 2.0 specific
void
TCharFormat::SetStyle(int16 style)
{
dwMask |= CFM_STYLE;
sStyle = style;
}
//
/// Twip size above which to kern char pair.
/// \note RichEdit 2.0 specific
void
TCharFormat::SetKerning(uint16 kern)
{
dwMask |= CFM_STYLE;
wKerning = kern;
}
//
/// Set underline type.
/// \note RichEdit 2.0 specific
void
TCharFormat::SetUnderlineType(uint8 utype)
{
dwMask |= CFM_UNDERLINETYPE;
bUnderlineType = utype;
}
//
/// Animated text like marching ants.
/// \note RichEdit 2.0 specific
void
TCharFormat::SetAnimation(uint8 anim)
{
dwMask |= CFM_ANIMATION;
bAnimation = anim;
}
//
/// Sets Revision author index.
/// \note RichEdit 2.0 specific
void
TCharFormat::SetRevAuthor(uint8 revav)
{
dwMask |= CFM_REVAUTHOR;
bRevAuthor = revav;
}
////////////////////////////////////////////////////////////////////////////////
//
//
//
/// Constructs a TFormatRange object initializing data members with the specified parameters.
//
TFormatRange::TFormatRange(HDC renderDC, HDC devDC, const TRect& renderArea,
const TRect& entireArea, const TCharRange& range)
{
SetRenderDC(renderDC);
SetTargetDC(devDC);
SetRenderRect(renderArea);
SetPageRect(entireArea);
SetRange(range);
}
//
/// Sets the device context of the device to render to.
//
void
TFormatRange::SetRenderDC(HDC renderDC)
{
hdc = renderDC;
}
//
/// Sets the device context of the target device to format for.
//
void
TFormatRange::SetTargetDC(HDC targetDC)
{
hdcTarget = targetDC;
}
//
/// Sets the area to render to.
/// \note The specified units are in TWIPS.
//
void
TFormatRange::SetRenderRect(const TRect& renderRect)
{
rc = renderRect;
}
//
/// Sets the entire area of the rendering device.
/// \note The specified units are in TWIPS.
//
void
TFormatRange::SetPageRect(const TRect& pgRect)
{
rcPage = pgRect;
}
//
/// Sets the range of text to format.
//
void
TFormatRange::SetRange(const TCharRange& charRange)
{
chrg = charRange;
}
//
/// Sets the range of text to format, specifying the starting and ending character offsets.
//
void
TFormatRange::SetRange(long start, long end)
{
chrg.cpMin = start;
chrg.cpMax = end;
}
////////////////////////////////////////////////////////////////////////////////
//
/// Constructs a default TParaFormat structure.
//
TParaFormat::TParaFormat(ulong mask)
{
memset(this, 0, sizeof(PARAFORMAT2));
cbSize = sizeof(PARAFORMAT);
if(TRichEditDll::Dll()->GetVersion() >= 2)
cbSize = sizeof(PARAFORMAT2);
dwMask = mask;
}
//
/// Constructs a TParaFormat structure whose members are initialized with the
/// paragraph formatting attributes of the current selection of a rich edit control.
//
TParaFormat::TParaFormat(const TRichEdit& edit, ulong mask)
{
PRECONDITION((HWND)edit);
memset(this, 0, sizeof(PARAFORMAT2));
cbSize = sizeof(PARAFORMAT);
if(TRichEditDll::Dll()->GetVersion() >= 2)
cbSize = sizeof(PARAFORMAT2);
dwMask = mask;
edit.GetParaFormat(*this);
}
//
/// Toggles the specified flag in the member which describes which information of
/// the PARAFORMAT structures is valid.
//
void
TParaFormat::ToggleMaskBit(ulong flag)
{
dwMask ^= flag;
}
//
/// Sets the numbering options. The only valid parameter is '0' or PFN_BULLET.
//
void
TParaFormat::SetNumbering(uint16 opt)
{
PRECONDITION(opt == 0 || opt == PFN_BULLET);
dwMask |= PFM_NUMBERING;
wNumbering = opt;
}
//
/// Sets the indentation of the first line in the paragraph. If the paragraph
/// formatting is being set and the 'relative' parameter is true, the 'start' value
/// is treated as a relative value that is added to the starting indentation of each
/// affected paragraph.
//
void
TParaFormat::SetStartIndent(long start, bool relative)
{
dwMask |= PFM_STARTINDENT;
if (relative)
dwMask |= PFM_OFFSETINDENT;
else
dwMask &= ~PFM_OFFSETINDENT;
dxStartIndent = start;
}
//
/// Sets the size of the right identation, relative to the right margin.
//
void
TParaFormat::SetRightIndent(long indent)
{
dwMask |= PFM_RIGHTINDENT;
dxRightIndent = indent;
}
//
/// Sets the indentation of the second line and subsequent lines, relative to the
/// starting indentation. The first line is indented if the 'offset' parameter is
/// negative, or outdented if it is positive.
//
void
TParaFormat::SetOffset(long offset)
{
dwMask |= PFM_OFFSET;
dxOffset = offset;
}
//
/// Sets the alignment option. The 'opt' parameter can be one of the following values:
/// - \c \b PFA_LEFT Paragraphs are aligned with the left margin.
/// - \c \b PFA_RIGHT Paragraphs are aligned with the right margin.
/// - \c \b PFA_CENTER Paragraphs are centered.
//
void
TParaFormat::SetAlignment(uint16 opt)
{
dwMask |= PFM_ALIGNMENT;
wAlignment = opt;
}
//
/// Sets the number and absolute positions of the tab stops.
//
void
TParaFormat::SetTabCount(short cnt, long* tabs)
{
PRECONDITION(tabs || !cnt);
dwMask |= PFM_TABSTOPS;
cTabCount = cnt;
for (int i=0; i<cnt; i++)
rgxTabs[i] = *tabs++;
}
//
// Vertical spacing before para
// RichEdit 2.0 specific
void
TParaFormat::SetSpaceBefore(long space)
{
dwMask |= PFM_SPACEBEFORE;
dySpaceBefore = space;
}
//
// Vertical spacing after para
// RichEdit 2.0 specific
void
TParaFormat::SetSpaceAfter(long space)
{
dwMask |= PFM_SPACEAFTER;
dySpaceAfter = space;
}
//
// Line spacing depending on Rule
// RichEdit 2.0 specific
void
TParaFormat::SetLineSpacing(long space, uint8 rule)
{
dwMask |= PFM_LINESPACING;
dyLineSpacing = space;
bLineSpacingRule = rule;
}
//
// Style handle
// RichEdit 2.0 specific
void
TParaFormat::SetStyle(int16 style)
{
dwMask |= PFM_STYLE;
sStyle = style;
}
//
// Shading in hundredths of a per cent
// Nibble 0: style, 1: cfpat, 2: cbpat
// RichEdit 2.0 specific
void
TParaFormat::SetShading(uint16 wight,uint16 style)
{
dwMask |= PFM_SHADING;
wShadingWeight = wight;
wShadingStyle = style;
}
//
// Starting value for numbering
// RichEdit 2.0 specific
void
TParaFormat::SetNumStart(uint16 start)
{
dwMask |= PFM_NUMBERINGSTART;
wNumberingStart = start;
}
//
// Alignment, roman/arabic, (), ), ., etc.
// RichEdit 2.0 specific
void
TParaFormat::SetNumStyle(uint16 style)
{
dwMask |= PFM_NUMBERINGSTYLE;
wNumberingStyle = style;
}
//
// Space bet 1st indent and 1st-line text
// RichEdit 2.0 specific
void
TParaFormat::SetNumTab(uint16 tab)
{
dwMask |= PFM_NUMBERINGTAB;
wNumberingTab = tab;
}
//
// Space between border and text (twips)
// RichEdit 2.0 specific
void
TParaFormat::SetBorder(uint16 space, uint16 width, uint16 border)
{
dwMask |= PFM_BORDER;
wBorderSpace = space;
wBorderWidth = width;
wBorders = border;
}
////////////////////////////////////////////////////////////////////////////////
//
//
DEFINE_RESPONSE_TABLE1(TRichEdit, TEditFile)
EV_COMMAND(CM_EDITCUT, CmEditCut),
EV_COMMAND(CM_EDITCOPY, CmEditCopy),
EV_COMMAND(CM_EDITPASTE, CmEditPaste),
EV_COMMAND(CM_EDITDELETE, CmEditDelete),
EV_COMMAND(CM_EDITCLEAR, CmEditClear),
EV_COMMAND(CM_EDITUNDO, CmEditUndo),
EV_COMMAND_ENABLE(CM_EDITCUT, CeHasSelect),
EV_COMMAND_ENABLE(CM_EDITCOPY, CeHasSelect),
EV_COMMAND_ENABLE(CM_EDITDELETE, CeHasSelect),
EV_COMMAND_ENABLE(CM_EDITPASTE, CeEditPaste),
EV_COMMAND_ENABLE(CM_EDITCLEAR, CeEditClear),
EV_COMMAND_ENABLE(CM_EDITUNDO, CeEditUndo),
EV_WM_CHAR,
EV_WM_KEYDOWN,
EV_WM_GETDLGCODE,
EV_WM_SETFOCUS,
EV_WM_KILLFOCUS,
END_RESPONSE_TABLE;
//
/// Constructor for a TRichEdit object. By default, edit control has a border and
/// its text is left-justified. Multiline edit control has horizontal vertical
/// scroll bars.
//
TRichEdit::TRichEdit(TWindow* parent,
int id,
LPCTSTR text,
int x, int y, int w, int h,
LPCTSTR fileName,
TModule* module)
:
TEditFile(parent, id, text, x, y, w, h, fileName, module)
{
// Make sure the RichEdit DLL is available
//
if (!TRichEditDll::IsAvailable())
TXCommCtrl::Raise(); // !BB Do we need a richedit-specific exception
// Default to RTF data format
//
SetFormat(SF_RTF);
// hide stuff that RichEdit doesn't support
// Richedit support it now
//GetSearchData().Flags |= FR_HIDEUPDOWN;
// Undo the styles specific to "EDIT" controls; add richedit styles
//
ModifyStyle(ES_LOWERCASE|ES_PASSWORD|ES_OEMCONVERT|ES_UPPERCASE|ES_AUTOHSCROLL,
ES_LEFT|WS_BORDER|WS_TABSTOP);
}
//
/// String-aware overload
//
TRichEdit::TRichEdit(
TWindow* parent,
int id,
const tstring& text,
int x, int y, int w, int h,
const tstring& fileName,
TModule* module
)
: TEditFile(parent, id, text, x, y, w, h, fileName, module)
{
// Make sure the RichEdit DLL is available
//
if (!TRichEditDll::IsAvailable())
TXCommCtrl::Raise(); // !BB Do we need a richedit-specific exception
// Default to RTF data format
//
SetFormat(SF_RTF);
// hide stuff that RichEdit doesn't support
// Richedit support it now
//GetSearchData().Flags |= FR_HIDEUPDOWN;
// Undo the styles specific to "EDIT" controls; add richedit styles
//
ModifyStyle(ES_LOWERCASE|ES_PASSWORD|ES_OEMCONVERT|ES_UPPERCASE|ES_AUTOHSCROLL,
ES_LEFT|WS_BORDER|WS_TABSTOP);
}
//
/// Constructor for TRichEdit associated with a MS-Windows interface element
/// created by MS-Windows from a resource definition.
///
/// By default, data transfer is enabled
//
TRichEdit::TRichEdit(TWindow* parent,
int resourceId,
TModule* module)
:
TEditFile(parent, resourceId, module ? *module : *parent->GetModule())
{
// Make sure the RichEdit DLL is available
//
if (!TRichEditDll::IsAvailable())
TXCommCtrl::Raise(); // !BB Do we need a richedit-specific exception
}
//
/// Retrieves the current character formatting in an edit control. If 'selection'
/// parameter is 'true', the attribute of the current selection is retrieved.
/// Otherwise, the default formatting attribute is retrieved.
//
ulong
TRichEdit::GetCharFormat(TCharFormat & cf, bool selection) const
{
TParam1 a1 = selection ? SCF_SELECTION : 0;
TParam2 a2 = reinterpret_cast<TParam2>(&cf);
return static_cast<ulong>(SendMessage(EM_GETCHARFORMAT, a1, a2));
}
//
/// Retrieves the paragraph formatting of the current selection of the rich edit control.
/// \note If more than one paragraph is selected, the structure receives the
/// attributes of the first paragraph, and the dwMask member specifies which
/// attributes are consistent throughout the entire selection.
//
ulong
TRichEdit::GetParaFormat(TParaFormat & pf) const
{
TParam2 a2 = reinterpret_cast<TParam2>(&pf);
return static_cast<ulong>(SendMessage(EM_GETPARAFORMAT, 0, a2));
}
//
/// Sets the character formatting of a rich edit control. The 'flags' parameter can
/// be one of the following:
/// - \c \b SCF_SELECTION Applies the formatting to the current selection, or sets the
/// default formatting if the selection is empty.
/// - \c \b SCF_WORD Applies the formatting to the selected word or words. If the
/// selection is empty but the insertion point is inside a word, the formatting is
/// applied to the word. This value must be used in conjunction with the
/// SCF_SELECTION value.
//
bool
TRichEdit::SetCharFormat(const TCharFormat & cf, uint flags)
{
return SendMessage(EM_SETCHARFORMAT, TParam1(flags), TParam2(&cf)) != 0;
}
//
/// Sets the paragraph formatting of the current selection of the rich edit control.
//
bool
TRichEdit::SetParaFormat(const TParaFormat & pf)
{
return SendMessage(EM_SETPARAFORMAT, 0, TParam2(&pf)) != 0;
}
//
/// Sets the background color of the rich edit control.
/// \note If 'TColor::None' is specified, the color is set to the window background
/// system color.
//
TColor
TRichEdit::SetBkgndColor(const TColor& bkColor)
{
TParam1 p1 = bkColor == TColor::None ? true : false;
TParam2 p2 = bkColor == TColor::None ? 0 : (COLORREF)bkColor;
return TColor((COLORREF)SendMessage(EM_SETBKGNDCOLOR, p1, p2));
}
//
/// Function returns whether or not the current selection has a particular
/// attribute. The 'mask' identifies the attribute of interest. The 'effects'
/// contains the state of the attributes. The function returns
/// - \c \b TFmtStatus::Yes : if the attribute is enabled.
/// - \c \b TFmtStatus::No: if the attribute is absent.
/// - \c \b TFmtStatus::Partly: if the attribute is partly present.
//
uint
TRichEdit::HasCharAttribute(ulong mask, uint32 effects)
{
TCharFormat cf(*this);
if (cf.dwMask & mask) {
if (cf.dwEffects & effects)
return Yes;
else
return No;
}
else
return Partly;
}
//
/// Toggles a set of character attributes. The 'mask' identifies the attributes of
/// interest while 'effects' identifies the state of the attributes.
//
bool
TRichEdit::ToggleCharAttribute(ulong mask, uint32 effects)
{
TCharFormat cf(*this);
cf.dwMask = mask;
cf.ToggleEffectsBit(effects);
return SetCharFormat(cf);
}
//
/// Increases or decreases (using a positive or negative value respectively) the
/// point size of the current selection.
//
bool
TRichEdit::ChangeCharPointSize(int pointSizeDelta)
{
TCharFormat cf(*this);
cf.dwMask = CFM_SIZE;
if (((cf.yHeight + 20*pointSizeDelta) <= (MaxPointSize*20)) &&
((cf.yHeight + 20*pointSizeDelta) >= (MinPointSize*6))) {
cf.yHeight += 20*pointSizeDelta;
return SetCharFormat(cf);
}
return false;
}
//
/// Returns true if the rich edit control has an active selection. Returns false
/// otherwise.
//
bool
TRichEdit::HasSelection() const
{
TRange r = GetSelection();
return r.cpMin != r.cpMax;
}
//
/// Retrieves the starting and ending character position of the selection in the
/// rich edit control.
//
void
TRichEdit::GetSelection(int& startPos, int& endPos) const
{
TCharRange cr;
CONST_CAST(TRichEdit*,this)->SendMessage(EM_EXGETSEL, 0, TParam2(&cr));
startPos = cr.cpMin;
endPos = cr.cpMax;
}
//
/// Retrieves the starting and ending character positions of the selection of the
/// richedit control.
//
void
TRichEdit::GetSelRange(TCharRange & cr) const
{
WARN(true, _T("TRichEdit::GetSelRange is deprecated. Use GetSelection instead."));
CONST_CAST(TRichEdit*,this)->SendMessage(EM_EXGETSEL, 0, TParam2(&cr));
}
//
/// Selects a range of characters in the rich edit control.
//
bool
TRichEdit::SetSelection(int startPos, int endPos)
{
TCharRange cr(startPos, endPos);
return SetSelRange(cr) >= 0;
}
//
/// Selects a range of characters in the rich edit control.
//
int
TRichEdit::SetSelRange(const TCharRange & cr)
{
WARN(true, _T("TRichEdit::SetSelRange is deprecated. Use SetSelection instead."));
return (int)SendMessage(EM_EXSETSEL, 0, TParam2(&cr));
}
//
/// Shows or hides the selection in the rich edit control. The 'hide' parameter
/// specifies whether to hide or show the selection. If it is 'false' the selection
/// is shown. Otherwise, the selection is hidden. The 'changeStyle' parameter
/// specifies whether to change the control's ES_NOHIDESEL window style. If this
/// parameter is 'false', the selection is temporarily shown or hidden. Otherwise,
/// the style is changed. If this parameter is 'true' and the control has the focus,
/// the selection is hidden or shown as appropriate.
//
void
TRichEdit::HideSelection(bool hide, bool changeStyle)
{
SendMessage(EM_HIDESELECTION, TParam1(hide), TParam2(changeStyle));
}
//
/// Returns the selection type of the rich edit control. Returns SEL_EMPTY if the
/// selection is empty, or one or more of the following values:
/// - \c \b SEL_TEXT Text
/// - \c \b SEL_OBJECT At least one OLE object
/// - \c \b SEL_MULTICHAR More than one character of text
/// - \c \b SEL_MULTIOBJECT More than one OLE object
//
ulong
TRichEdit::GetSelectionType() const
{
return static_cast<ulong>(SendMessage(EM_SELECTIONTYPE));
}
//
// Return the lenght of the text in the richedit control
// for RichEdit 2.0 uses EM_GETTEXTLENGTH
//
int
TRichEdit::GetTextLength() const
{
if (TRichEditDll::Dll()->GetVersion() >= 2)
{
GETTEXTLENGTHEX gtl;
gtl.flags = GTL_DEFAULT;
//Jogy ??? From MSDN: Code page used in the translation. It is CP_ACP for ANSI Code Page and 1200 for Unicode.
#if defined(UNICODE)
gtl.codepage = 1200;
#else
gtl.codepage = CP_ACP;
#endif
return static_cast<int>(SendMessage(EM_GETTEXTLENGTHEX, TParam1(>l), TParam2(0)));
}
else
{
return GetWindowTextLength();
}
}
//
/// Retrieves a specified range of text from the rich edit control.
/// NB! Deprecated. Use GetTextRange(const TRange&) instead.
//
int
TRichEdit::GetTextRange(TTextRange & tr) const
{
WARN(true, _T("TRichEdit::GetTextRange(TTextRange&) is deprecated. Use GetTextRange(const TRange&) instead."));
return (int)CONST_CAST(TRichEdit*,this)->SendMessage(EM_GETTEXTRANGE, 0, TParam2(&tr));
}
//
/// Retrieves a specified range of text from the rich edit control.
/// NB! Deprecated. Use GetTextRange(const TRange&) instead.
//
int
TRichEdit::GetTextRange(const TCharRange & cr, LPTSTR buffer) const
{
WARN(true, _T("TRichEdit::GetTextRange(const TCharRange&, LPTSTR) is deprecated. Use GetTextRange(const TRange&) instead."));
TTextRange tr(cr, buffer);
return GetTextRange(tr);
}
//
/// For use with CopyText
//
struct TRichEditGetTextRange
{
const TRichEdit& edit;
const TCharRange& range;
TRichEditGetTextRange(const TRichEdit& e, const TCharRange& r) : edit(e), range(r) {}
int operator()(LPTSTR buf, int)
{
TTextRange tr(range, buf);
return (int)CONST_CAST(TRichEdit*, &edit)->SendMessage(EM_GETTEXTRANGE, 0, TParam2(&tr));
}
};
//
/// Retrieves a specified range of text from the rich edit control.
/// The range is half-open; i.e. range [0,2) for "abc" returns "ab".
/// An empty string is returned if either
///
/// - the control is empty (no text), or
/// - the range is invalid (empty or inverted), or
/// - both startPos and endPos is beyond the extents of the actual text.
///
/// If endPos is beyond the valid range then it is limited to the valid range.
/// A special case is the range [0, -1). It will return the full text.
///
/// \todo Must be tested.
//
tstring
TRichEdit::GetTextRange(const TRange& r) const
{
const int begin = r.cpMin;
int end = r.cpMax; // May be adjusted.
const int n = GetTextLength();
// Check input arguments against EM_GETTEXTRANGE requirements.
// Note that [0, -1) for an empty control is not valid for EM_GETTEXTRANGE,
// but we'll ignore that, since no buffer issues are involved here.
// Otherwise we reject negative positions, as well as empty and inverted ranges.
// EM_GETTEXTRANGE will not null-terminate the result in these cases.
if (begin == 0 && end == -1)
{
end = n;
}
else if (begin < 0 || end < 0)
{
WARN(true, _T("Arguments out of range"));
return tstring();
}
else if (begin == end)
{
WARN(true, _T("Empty range"));
return tstring();
}
else if (begin > end)
{
WARN(true, _T("Inverted range"));
return tstring();
}
// Return empty if the entire range is outside the extents of the actual text.
// This is valid for EM_GETTEXTRANGE so we wont complain.
if (begin >= n)
return tstring();
// Limit end to the actual text and calculate the resulting range length.
end = std::min(end, n);
const int range_length = end - begin; // length of half-open range
// Do a sanity check, in case someone changes the code, etc,
// then finally copy!
CHECK(begin >= 0 && end >= 0 && begin < n && end <= n);
TCharRange adjusted_range(begin, end);
return CopyText(range_length, TRichEditGetTextRange(*this, adjusted_range));
}
//
/// Retrieves a specified range of text from the rich edit control.
/// NB! Deprecated. Use GetTextRange instead.
//
void
TRichEdit::GetSubText(LPTSTR str, int startPos, int endPos) const
{
WARN(true, _T("TRichEdit::GetSubText is deprecated. Use GetTextRange instead."));
TTextRange tr(startPos, endPos, str);
GetTextRange(tr);
}
//
/// Retrieves the currently-selected text of the rich edit control.
//
int
TRichEdit::GetSelectedText(LPTSTR buffer) const
{
WARN(true, _T("TRichEdit::GetSelectedText(LPTSTR) is deprecated. Use GetSelectedText() instead."));
return (int)CONST_CAST(TRichEdit*,this)->SendMessage(EM_GETSELTEXT, 0, TParam2(buffer));
}
//
/// For use with CopyText
//
struct TRichEditGetSelectedText
{
const TRichEdit& edit;
TRichEditGetSelectedText(const TRichEdit& e) : edit(e){}
int operator()(LPTSTR buf, int)
{return (int)CONST_CAST(TRichEdit*, &edit)->SendMessage(EM_GETSELTEXT, 0, TParam2(buf));}
};
//
/// String-aware overload
//
tstring
TRichEdit::GetSelectedText() const
{
const int n = GetSelection().GetSize();
return (n > 0) ? CopyText(n, TRichEditGetSelectedText(*this)) : tstring();
}
//
/// Returns the current caret position.
///
/// \return the index of the text input-caret, counting zero-based
/// from the first character in the text of the control.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/em-getseltext">EM_GETSELTEXT message</a>
/// in the Windows API for more technical insight, or
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/bumper-rich-edit-control-reference-messages">
/// Rich Edit Messages</a> for a more general overview.
///
/// \exception TXOwl is thrown on failure.
//
auto TRichEdit::GetCurrentPosition() const -> int
{
// similar to TEdit::GetCurrentPosition ... but with less limits on CharFromPos
PRECONDITION(GetHandle());
TRange selection = GetSelection();
if (selection.IsValidRange()) {
/* we don't know which end of the range is the caret-position,
so we have to ask explicitly for it... */
TPoint caretpos = GetCaretPos();
// CharFromPos wouldn't be able to handle positions above that range
if ( caretpos.x > SHRT_MAX || caretpos.x < SHRT_MIN
|| caretpos.y > SHRT_MAX || caretpos.y < SHRT_MIN)
throw TXOwl{_T("Character position is out of range")}; // Windows-API limit
auto cfp = CharFromPos(static_cast<int16>(caretpos.x), static_cast<int16>(caretpos.y));
CHECK(-1 != cfp); // (65535, 65535) error - position out of bounds - shouldn't happen
CHECK(cfp <= INT_MAX); // basically our own limit here ;)
return cfp;
}
return selection.cpMin;
}
//
/// Sets an upper limit to the amount of text in the richedit control.
//
void
TRichEdit::LimitText(int maxValue)
{
SendMessage(EM_EXLIMITTEXT, 0, TParam2(maxValue));
}
//
/// Finds text within the rich edit control. The 'flags' parameter can be a
/// combination of the following values:
/// - \c \b FT_MATCHCASE Performs a case sensitiv search.
/// - \c \b FT_MATCHWORD Matches whole words.
//
int
TRichEdit::FindText(uint flags, const TFindText & ft)
{
return (int)SendMessage(EM_FINDTEXT, TParam1(flags), TParam2(&ft));
}
//
/// Finds text within the rich edit control. The 'flags' parameter can be a
/// combination of the following values:
/// - \c \b FT_MATCHCASE Performs a case sensitiv search.
/// - \c \b FT_MATCHWORD Matches whole words.
//
int
TRichEdit::FindText(uint flags, const TCharRange & cr, LPCTSTR text)
{
TFindText ft(cr, text);
return FindText(flags, ft);
}
//
// Search for the specified text in the rich edit control. If found, select
// the text and return the offset of the text. Otherwise, return -1.
//
// NOTE: If the 'startPos' is -1, it is assumed that the starting position is
// the end [or beginning, depending on the direction parameter, 'up'] of the
// current selection
//
int
TRichEdit::Search(int startPos, LPCTSTR text, bool caseSensitive,
bool wholeWord, bool up)
{
if (!text || !text[0])
return -1;
if (startPos == -1) {
int sBeg, sEnd;
GetSelection(sBeg, sEnd);
startPos = up ? sBeg : sEnd;
}
// The current docs. mention the FT_MATCHCASE and FT_WHOLEWORD flags which
// are not defined currently. I suspect they meant the FR_xxxx flags (used
// in CommDlg API).
// Yes it is FR_MATCHCASE and FR_WHOLEWORD
TFindText findText(startPos, -1, text);
uint flags = (caseSensitive ? FR_MATCHCASE : 0) |
(wholeWord ? FR_WHOLEWORD : 0) |
(up ? 0 : FR_DOWN);
int index = FindText(flags, findText);
//
// If we've got a match, select the text
//
if (index >= 0)
{
size_t end = index + ::_tcslen(text);
if (!IsRepresentable<int>(end))
return -1;
SetSelection(index, static_cast<int>(end));
}
return index;
}
//
// Find the next work break before or after the specified character position,
// or retrieve information about the character at that position. The 'code'
// parameter can be one of the following:
//
// WB_CLASSIFY Returns the character class and word break flags of the
// character at the specified position.
// WB_ISDELIMITER Returns TRUE if the character at the specified position
// is a delimiter, or FALSE otherwise.
// WB_LEFT Finds the nearest character before the specified
// position that begins a word.
// WB_LEFTBREAK Finds the next word end before the specified position.
// WB_MOVEWORDLEFT Finds the next character that begins a word before the
// specified position. This value is used during CTRL+LEFT key processing.
// WB_MOVEWORDRIGHT Finds the next character that begins a word after the
// specified position. This value is used during
// CTRL+RIGHT key processing.
// WB_RIGHT Finds the next character that begins a word after the
// specified position.
// WB_RIGHTBREAK Finds the next end-of-word delimiter after the
// specified position.
//
// The return value is the character index of the word break, unless the
// 'code' parameter is WB_CLASSIFY or WB_ISDELIMETER
int
TRichEdit::FindWordBreak(uint code, int start)
{
return (int)SendMessage(EM_FINDWORDBREAK, TParam1(code), TParam2(start));
}
//
// Determine which line contains the specified character in the richedit
// control.
// NOTE: The return value is zero-based.
//
int
TRichEdit::GetLineFromPos(int charPos) const
{
return (int)CONST_CAST(TRichEdit*,this)->SendMessage(EM_EXLINEFROMCHAR,
0, TParam2(charPos));
}
//
// Enable/Disable Auto URL detection
// RichEdit 2.0 specific
bool
TRichEdit::EnableAutoURL(bool enable)
{
if(TRichEditDll::Dll()->GetVersion() >= 2)
return ToBool(SendMessage(EM_AUTOURLDETECT, TParam1(enable)));
return false;
}
//
//
// RichEdit 2.0 specific
int
TRichEdit::GetIMEMode() const
{
return TRichEditDll::Dll()->GetVersion() >= 2 ?
static_cast<int>(SendMessage(EM_GETIMECOMPMODE)) :
ICM_NOTOPEN;
}
//
//
// RichEdit 2.0 specific
void
TRichEdit::SetLangOptions(int options)
{
if(TRichEditDll::Dll()->GetVersion() >= 2)
SendMessage(EM_SETLANGOPTIONS,0,TParam2(options));
}
//
//
// RichEdit 2.0 specific
int
TRichEdit::GetLangOptions() const
{
return TRichEditDll::Dll()->GetVersion() >= 2 ?
static_cast<int>(SendMessage(EM_GETLANGOPTIONS)) :
0;
}
//
//
// RichEdit 2.0 specific
void
TRichEdit::SetTextMode(int mode)
{
if(TRichEditDll::Dll()->GetVersion() >= 2)
SendMessage(EM_SETTEXTMODE,TParam1(mode));
if(mode & TM_PLAINTEXT)
Format = SF_TEXT;
else if(mode & TM_RICHTEXT)
Format = SF_RTF;
}
//
//
// RichEdit 2.0 specific
int
TRichEdit::GetTextMode() const
{
return TRichEditDll::Dll()->GetVersion() >= 2 ?
static_cast<int>(SendMessage(EM_GETTEXTMODE)) :
IsRTF() ? TM_RICHTEXT : TM_PLAINTEXT;
}
//
//
// RichEdit 2.0 specific
void
TRichEdit::SetUndoLimit(int maxnum)
{
if(TRichEditDll::Dll()->GetVersion() >= 2)
SendMessage(EM_SETUNDOLIMIT,TParam1(maxnum));
}
//
//
// RichEdit 2.0 specific
void
TRichEdit::StopGroupTyping()
{
if(TRichEditDll::Dll()->GetVersion() >= 2)
SendMessage(EM_STOPGROUPTYPING);
}
//
// Return true if the richedit can paste the specified clipboard format, or
// false otherwise.
//
bool
TRichEdit::CanPaste(uint format) const
{
return ToBool(CONST_CAST(TRichEdit*,this)->SendMessage(EM_CANPASTE,TParam1(format)));
}
//
// RichEdit 2.0 specific
//
bool
TRichEdit::CanRedo() const
{
if(TRichEditDll::Dll()->GetVersion() >= 2)
return ToBool(CONST_CAST(TRichEdit*,this)->SendMessage(EM_CANREDO));
return CanUndo();
}
//
// RichEdit 2.0 specific
// The value returned is an UNDONAMEID enumeration
int
TRichEdit::GetUndoName() const
{
return TRichEditDll::Dll()->GetVersion() >= 2 ?
static_cast<int>(SendMessage(EM_GETUNDONAME)) :
0;
}
//
// RichEdit 2.0 specific
// The value returned is an UNDONAMEID enumeration
int
TRichEdit::GetRedoName() const
{
return TRichEditDll::Dll()->GetVersion() >= 2 ?
static_cast<int>(SendMessage(EM_GETREDONAME)) :
0;
}
//
// RichEdit 2.0 specific
//
void
TRichEdit::Redo()
{
if(TRichEditDll::Dll()->GetVersion() >= 2)
SendMessage(EM_REDO);
else
Undo();
}
//
// Paste the specified clipboard format in the rich edit control.
//
void
TRichEdit::PasteSpecial(uint format)
{
SendMessage(EM_PASTESPECIAL, TParam1(format));
}
//
// Paste a compatible clipboard format in the rich edit control.
//
void
TRichEdit::Paste()
{
SendMessage(WM_PASTE);
// !BB //
// !BB // Iterator through clipboard to locate 'pastable' format
// !BB //
// !BB TClipboard clip(*this);
// !BB for (TClipboardFormatIterator iter(clip); iter; iter++) {
// !BB if (CanPaste(iter.Current())) {
// !BB PasteSpecial(iter.Current());
// !BB return;
// !BB }
// !BB }
}
//
// Replace the contents of the rich edit control with the specified data
// stream. The 'format' parameter can be one of the following data formats,
// optionally combined with the SFF_SELECTION flag:
//
// Value Meaning
// ----- -------
// SF_TEXT Text
// SF_RTF Rich-text format
//
// If the SFF_SELECTION flag is specified, the stream replaces the contents of
// the current selection. Otherwise, the stream replaces the entire contents
// of the control.
//
ulong
TRichEdit::StreamIn(uint format, TEditStream & es)
{
return static_cast<ulong>(SendMessage(EM_STREAMIN, TParam1(format), TParam2(&es)));
}
//
// Write the contents of the rich edit control to the specified data stream.
// The 'format' parameter can be one of the following values, optionally
// combined with the SFF_SELECTION flag:
//
// Value Meaning
// ----- -------
// SF_TEXT Text with spaces in place of OLE objects
// SF_RTF Rich-text format (RTF)
// SF_RTFNOOBJS RTF with spaces in place of OLE object.
// SF_TEXTIZED Text with a text representation of OLE objects.
//
// NOTE: The SF_RTFNOOBJS option is useful if an application stores OLE
// objects itself, as RTF representation of OLE objects is not very
// compact.
// If the SFF_SELECTION flag is specified, only the contents of the
// current selection are streamed out. Otherwise, the entire contents of
// the control are streamed out.
//
ulong
TRichEdit::StreamOut(uint format, TEditStream & es)
{
return static_cast<ulong>(SendMessage(EM_STREAMOUT, TParam1(format), TParam2(&es)));
}
//
// Display a portion of the richedit control's content within the specified
// rectangle.
// NOTE: The content of the control must first be formatted via a call to the
// 'FormatRange' method.
//
bool
TRichEdit::DisplayBand(TRect & rc)
{
return SendMessage(EM_DISPLAYBAND, 0, TParam2(&rc)) != 0;
}
//
// Formats a range of text (specified via the 'chrg' member of the
// specified TFormatRange) for the device(s) specified via the 'hdcTarget'
// and 'hdc' members of the TFormatRange structure.
//
int
TRichEdit::FormatRange(const TFormatRange & fr, bool render)
{
return (int)SendMessage(EM_FORMATRANGE, TParam1(render), TParam2(&fr));
}
//
// Frees the Formatting information cached by the RichEdit control...
//
int
TRichEdit::FormatRange()
{
return (int)SendMessage(EM_FORMATRANGE, TParam1(TRUE), 0);
}
//
// Set the target device and line width used for WYSIWYG (what you see is
// what you get) formatting of the rich edit control.
//
bool
TRichEdit::SetTargetDevice(HDC dc, int lineWidth)
{
return SendMessage(EM_SETTARGETDEVICE, TParam1(dc),
TParam2(lineWidth)) != 0;
}
//
// Force the rich edit control to send an EN_REQUESTRESIZE notification
// message to its parent window.
//
// NOTE: This message is useful during WM_SIZE processing for the parent of a
// bottomless rich edit control.
//
void
TRichEdit::RequestResize()
{
SendMessage(EM_REQUESTRESIZE);
}
//
// Retrieve an IRichEditOle object that a client can use to access a rich edit
// control's OLE functionality. Returns 'true' if successful, or false
// otherwise.
//
bool
TRichEdit::GetOleInterface(IRichEditOle * &pInterface) const
{
return CONST_CAST(TRichEdit*,this)->SendMessage(EM_GETOLEINTERFACE,
0, TParam2((void * *)&pInterface)) != 0;
}
//
// Set an IRichEditOleCallback object that the rich edit control uses to get
// OLE-related resources and information from the client. Returns 'true' if
// successful, or false otherwise.
//
bool
TRichEdit::SetOleInterface(IRichEditOleCallback * pInterface)
{
// Y.B 06/16/98 MFC uses EM_SETOLECALLBACK not EM_SETOLEINTERFACE ?????
// afxxmn.inl line 620. func CRichEditCtrl::SetOLECallback(...)
// #define EM_SETOLECALLBACK (WM_USER + 70) // not documented !!!!!!
return SendMessage(EM_SETOLECALLBACK, 0, TParam2(pInterface)) != 0;
}
//
// Retrieve the event mask for the rich edit control. The event mask specifies
// which notification messages the control sends to its parent window.
//
ulong
TRichEdit::GetEventMask() const
{
return static_cast<ulong>(SendMessage(EM_GETEVENTMASK));
}
//
// Set the event mask for a rich edit control. The event mask specifies which
// notification messages the control sends to its parent window. The 'msk'
// parameter can be zero or more of the following values:
//
// Value Meaning
// ----- -------
// ENM_CHANGE Sends EN_CHANGE notifications.
// ENM_CORRECTTEXT Sends EN_CORRECTTEXT notifications.
// ENM_DROPFILES Sends EN_DROPFILES notifications.
// ENM_KEYEVENTS Sends EN_MSGFILTER notifications for keyboard events.
// ENM_MOUSEEVENTS Sends EN_MSGFILTER notifications for mouse events.
// ENM_PROTECTED Sends EN_PROTECTED notifications.
// ENM_RESIZEREQUEST Sends EN_REQUESTRESIZE notifications.
// ENM_SCROLL Sends EN_HSCROLL notifications.
// ENM_SELCHANGE Sends EN_SELCHANGE notifications.
// ENM_UPDATE Sends EN_UPDATE notifications
//
ulong
TRichEdit::SetEventMask(ulong msk)
{
return static_cast<ulong>(SendMessage(EM_SETEVENTMASK, 0, TParam2(msk)));
}
//
/// WM_GETDLGCODE handler to bypass TEdit's handler (which caters to validators).
//
uint
TRichEdit::EvGetDlgCode(const MSG* msg)
{
return TWindow::EvGetDlgCode(msg);
}
//
/// WM_CHAR handler to bypass TEdit's handler (which caters to validators).
//
void
TRichEdit::EvChar(uint /*key*/, uint /*repeatCount*/, uint /*flags*/)
{
DefaultProcessing();
}
//
/// WM_GETDLGCODE handler to bypass TEdit's handler (which caters to validators).
//
void
TRichEdit::EvKeyDown(uint /*key*/, uint /*repeatCount*/, uint /*flags*/)
{
DefaultProcessing();
}
//
/// WM_KILLFOCUS handler to bypass TEdit's handler (which caters to validators).
//
void
TRichEdit::EvKillFocus(THandle hGetFocus)
{
TControl::EvKillFocus(hGetFocus);
}
//
/// WM_SETFOCUS handler to bypass TEdit's handler (which caters to validators).
//
void
TRichEdit::EvSetFocus(THandle /*hLostFocus*/)
{
DefaultProcessing();
}
//
// This function is called for Cut/Copy/Delete menu items to determine
// whether or not the item is enabled.
//
void
TRichEdit::CeHasSelect(TCommandEnabler& commandHandler)
{
int sPos, ePos;
GetSelection(sPos, ePos);
commandHandler.Enable(sPos != ePos);
}
//
// This function is called for the Paste menu item to determine whether or
// not the item is enabled.
//
void
TRichEdit::CeEditPaste(TCommandEnabler& ce)
{
/*
return (CountClipboardFormats() != 0) &&
(IsClipboardFormatAvailable(CF_TEXT) ||
IsClipboardFormatAvailable(_oleData.cfRichTextFormat) ||
IsClipboardFormatAvailable(_oleData.cfEmbedSource) ||
IsClipboardFormatAvailable(_oleData.cfEmbeddedObject) ||
IsClipboardFormatAvailable(_oleData.cfFileName) ||
IsClipboardFormatAvailable(_oleData.cfFileNameW) ||
IsClipboardFormatAvailable(CF_METAFILEPICT) ||
IsClipboardFormatAvailable(CF_DIB) ||
IsClipboardFormatAvailable(CF_BITMAP) ||
GetRichEditCtrl().CanPaste());
*/
TClipboard clip(*this, false);
if (clip &&
(clip.CountClipboardFormats()!=0) &&
( clip.IsClipboardFormatAvailable(CF_TEXT) ||
clip.IsClipboardFormatAvailable(CF_OEMTEXT) ||
clip.IsClipboardFormatAvailable(CF_UNICODETEXT)
// || clip.IsClipboardFormatAvailable(CF_METAFILEPICT) || //?? check
// clip.IsClipboardFormatAvailable(CF_ENHMETAFILE) || //??
// clip.IsClipboardFormatAvailable(CF_DIB) //???
)
){
ce.Enable(true);
}
else
ce.Enable(false);
}
//
// This function is called for the Clear menu item to determine whether or
// not the item is enabled.
//
void
TRichEdit::CeEditClear(TCommandEnabler& commandHandler)
{
commandHandler.Enable(!(GetNumLines() == 1 && GetLineLength(0) == 0));
}
//
// This function is called for the Undo menu item to determine whether or
// not the item is enabled.
//
void
TRichEdit::CeEditUndo(TCommandEnabler& commandHandler)
{
commandHandler.Enable(CanUndo());
}
//
/// Returns name of predefined Windows edit class.
//
auto TRichEdit::GetWindowClassName() -> TWindowClassName
{
#if defined(OWL_USERICHEDIT41)
if(TRichEditDll::Dll()->GetVersion() >= 4)
return TWindowClassName{MSFTEDIT_CLASS};
#endif
if(TRichEditDll::Dll()->GetVersion() >= 2)
return TWindowClassName{RICHEDIT_CLASS};
else
return TWindowClassName{_T("RICHEDIT")};
}
//
/// Updates the list of filters describing files which can be opened by
/// the rich edit control.
//
//
void
TRichEdit::SetupWindow()
{
TEditFile::SetupWindow();
//Load RichEdit's filter
//
GetFileData().SetFilter(LoadString(IDS_RICHEDITFILTER).c_str());
}
uint32
TRichEdit::CharFromPos(int16 x, int16 y) const
{
PRECONDITION(TWindow::GetHandle());
POINTL pt;
pt.x = x;
pt.y = y;
return (uint32)SendMessage(EM_CHARFROMPOS, 0, TParam2(&pt));
}
//
uint32
TRichEdit::PosFromChar(uint charIndex) const
{
PRECONDITION(TWindow::GetHandle());
if (TRichEditDll::Dll()->GetVersion() == 2)
return (uint32)SendMessage(EM_POSFROMCHAR, charIndex);
else
{
POINTL pt;
SendMessage(EM_POSFROMCHAR, (TParam1)&pt, charIndex);
return MkUint32((uint16)pt.y, (uint16)pt.x);
}
}
//
// Callback used when reading data from a stream into a rich edit control.
//
DWORD CALLBACK
RichEditStrmInWithIStream(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb)
{
PRECONDITION(dwCookie);
tistream& is = *reinterpret_cast<tistream*>(dwCookie);
// Return 0 if transfer is complete.
//
if (is.eof())
{
*pcb = 0;
return 0;
}
// Read data in buffer.
// Note: We know that the gcount cannot exceed cb, so we can cast unchecked.
//
#if defined(UNICODE)
tstring tempBuff(cb, _T('\0'));
is.read(&tempBuff[0], cb);
LONG n = static_cast<LONG>(is.gcount());
::WideCharToMultiByte(CP_ACP, 0, &tempBuff[0], n, reinterpret_cast<char*>(pbBuff), cb, NULL, NULL);
#else
is.read(reinterpret_cast<char*>(pbBuff), cb);
LONG n = static_cast<LONG>(is.gcount());
#endif
// Indicate amount of data read.
//
*pcb = n;
return 0;
}
//
// Callback used when writing out the contents of a rich edit control to
// a data stream.
//
DWORD CALLBACK
RichEditStrmOutWithOstream(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb)
{
PRECONDITION(dwCookie);
tostream& os = *reinterpret_cast<tostream*>(dwCookie);
// Save current stream location and write data to buffer
//
streampos pCnt = os.tellp();
#if defined(UNICODE)
tchar * tempBuff = new tchar[cb];
::MultiByteToWideChar(CP_ACP, 0, (char*)pbBuff, cb, tempBuff, cb);
os.write(tempBuff, cb);
delete[] tempBuff;
#else
os.write((char*)pbBuff, cb);
#endif
// Indicate the number of bytes written to the file
//
*pcb = (LONG)(os.tellp() - pCnt);
return 0;
}
//
// Overriden to bypass TEdit's 'Transfer' method.
// NOTE: There's no transfer-support for rich edit controls.
//
uint
TRichEdit::Transfer(void* /*buffer*/, TTransferDirection /*direction*/)
{
// NOTE: No transfer support for rich edit control
//
return 0;
}
//
// Read the data from the specified stream into the rich edit control. The
// 'fmt' parameter can be one of the following data formats, optionally
// combined with the SFF_SELECTION flag:
//
// Value Meaning
// ----- -------
// SF_TEXT Text
// SF_RTF Rich-text format
//
// If the SFF_SELECTION flag is specified, the stream replaces the contents of
// the current selection. Otherwise, the stream replaces the entire contents
// of the control.
//
bool
TRichEdit::ReadFromStream(tistream& is, uint fmt)
{
DWORD_PTR cookie = reinterpret_cast<DWORD_PTR>(&is);
TEditStream edStrm(cookie, RichEditStrmInWithIStream);
StreamIn(fmt, edStrm);
return edStrm.dwError == 0;
}
//
// Write the contents of the rich edit control to the specified data stream.
// The 'fmt' parameter can be one of the following values, optionally
// combined with the SFF_SELECTION flag:
//
// Value Meaning
// ----- -------
// SF_TEXT Text with spaces in place of OLE objects
// SF_RTF Rich-text format (RTF)
// SF_RTFNOOBJS RTF with spaces in place of OLE object.
// SF_TEXTIZED Text with a text representation of OLE objects.
//
// NOTE: The SF_RTFNOOBJS option is useful if an application stores OLE
// objects itself, as RTF representation of OLE objects is not very
// compact.
// If the SFF_SELECTION flag is specified, only the contents of the
// current selection are streamed out. Otherwise, the entire contents of
// the control are streamed out.
//
bool
TRichEdit::WriteToStream(tostream& os, uint fmt)
{
DWORD_PTR cookie = reinterpret_cast<DWORD_PTR>(&os);
TEditStream edStrm(cookie, RichEditStrmOutWithOstream);
StreamOut(fmt, edStrm);
return edStrm.dwError == 0;
}
//
// Read the contents of the specified file in the rich edit control. Returns
// 'true' if successful, or false otherwise.
//
bool
TRichEdit::Read(LPCTSTR fileName)
{
if (!fileName)
{
if (GetFileName())
fileName = GetFileName();
else
return false;
}
_USES_CONVERSION;
tifstream ifs(_W2A(fileName), ios::in|ios::binary);
if (ifs) {
// Could check for a valid file (eg. FileSize != 0)
// before proceeding with a call to Clear() here.
//
Clear();
// Stream in data from file
//
if (ReadFromStream(ifs, Format)) {
ClearModify();
return true;
}
}
return false;
}
//
// Write the contents of the edit control to the specified file. Returns
// 'true' if successful, or false otherwise.
//
bool
TRichEdit::Write(LPCTSTR fileName)
{
if (!fileName)
{
if (GetFileName())
fileName = GetFileName();
else
return false;
}
_USES_CONVERSION;
tofstream ofs(_W2A(fileName), ios::out|ios::binary);
if (ofs) {
if (WriteToStream(ofs, Format)) {
ClearModify();
return true;
}
}
return false;
}
IMPLEMENT_STREAMABLE1(TRichEdit, TEditFile);
#if OWL_PERSISTENT_STREAMS
//
//
void*
TRichEdit::Streamer::Read(ipstream& is, uint32 /*version*/) const
{
ReadBaseObject((TEditFile*)GetObject(), is);
return GetObject();
}
//
//
//
void
TRichEdit::Streamer::Write(opstream& os) const
{
WriteBaseObject((TEditFile*)GetObject(), os);
}
#endif
//
// Object wrapper which loads the RichEdit DLL
//
TRichEditModule::TRichEditModule()
:
TModule(
#if defined(OWL_USERICHEDIT41)
GetVersion() >= 4 ? RichEdit41DllName :
#endif
(GetVersion() >= 2 ? RichEdit20DllName : RichEditDllName),
true, true,false
)
{
}
//
// check new rich edit library
//
static int CheckREVersion()
{
TErrorMode loadMode(SEM_NOOPENFILEERRORBOX|SEM_FAILCRITICALERRORS);
#if defined(OWL_USERICHEDIT41)
HINSTANCE Handle41 = ::LoadLibrary(RichEdit41DllName);
if (Handle41 > HINSTANCE(HINSTANCE_ERROR))
{
::FreeLibrary(Handle41);
return 4;
}
#endif
HINSTANCE Handle = ::LoadLibrary(RichEdit20DllName);
if (Handle <= HINSTANCE(HINSTANCE_ERROR))
return 1;
::FreeLibrary(Handle);
return 2;
}
//
//
//
int TRichEditModule::GetVersion(bool force_old)
{
static int REVersion = force_old ? 1 : CheckREVersion();
return REVersion;
}
#if defined(_BUILDOWLDLL)
// The template instances only need to be generated when building the
// ObjectWindows DLL - These instances are exported by OWL and imported
// by user code.
//
// Export template of TDllLoader<TRichEditModule> when building ObjectWindows
// DLL and provide import declaration of DLL instance for users of the class.
//
template class _OWLCLASS TDllLoader<TRichEditModule>;
#endif
} // OWL namespace
/* ========================================================================== */
↑ V560 A part of conditional expression is always true: begin < n.
↑ V560 A part of conditional expression is always true: begin >= 0.
↑ V1004 The 'tabs' pointer was used unsafely after it was verified against nullptr. Check lines: 553, 558.