//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//// PARTICULAR PURPOSE.
////
//// Copyright (c) Microsoft Corporation. All rights reserved
#include "SampleRichEditControl.h"
CSampleRichEditWindow::CSampleRichEditWindow() :
// Initialize scale factor to 100%
m_numerator(100),
m_denominator(100),
// Initialize for loading rich edit module
m_hmodRichEdit(nullptr),
m_hWnd(nullptr)
{
}
CSampleRichEditWindow::~CSampleRichEditWindow()
{
if (m_hWnd)
{
DestroyWindow(m_hWnd);
m_hWnd = nullptr;
}
if (m_hmodRichEdit)
{
FreeLibrary(m_hmodRichEdit);
m_hmodRichEdit = nullptr;
}
}
//
// This method creates and initializes a child window for the Rich Edit control.
// Notes:
// The rich edit window will be a child window to the hWnd passed in.
// The window will be positioned using default child window values,
// and rely on the caller to [re-]position the window as needed.
//
HRESULT
CSampleRichEditWindow::Initialize(
_In_ HWND parentHWD
)
{
HRESULT hr = S_OK;
BOOL fResult = FALSE;
// Load MSFTEDIT.dll. This library is required for Rich Edit v4.1 or higher.
m_hmodRichEdit = LoadLibrary(L"msftedit.dll");
if (!m_hmodRichEdit)
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
// Create the rich edit window.
if (SUCCEEDED(hr))
{
m_hWnd = CreateWindowEx(WS_EX_LAYERED, // Make this a layered window.
MSFTEDIT_CLASS, // Associate with the rich edit class.
// Default text to use in window.
L"Enter text here.",
// Define as a visible child window.
// Also set flags to provide a multi-line rich edit experience.
WS_VISIBLE | WS_CHILD |
WS_CLIPCHILDREN | WS_CLIPSIBLINGS |
WS_VSCROLL | ES_WANTRETURN | ES_MULTILINE,
// Use default x, y, width and height as these will be set later.
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
parentHWD, // Set parent as requested.
NULL, // Do not attach to a menu.
GetModuleHandleW(NULL), // Associate to the calling application.
NULL // No additional structure needed to create this window.
);
}
if (!m_hWnd)
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
// Set the rich edit control to be semi-transparent.
if (SUCCEEDED(hr))
{
fResult = SetLayeredWindowAttributes(m_hWnd, 0, 255, LWA_ALPHA);
if (!fResult)
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
}
// Set this window to be on top of its siblings.
if (SUCCEEDED(hr))
{
fResult = SetWindowPos(m_hWnd,
HWND_TOP,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE
);
if (!fResult)
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
}
// Populate the rich edit control.
if (SUCCEEDED(hr))
{
hr = ResetDefaultContent();
}
// Cleanup code.
if (FAILED(hr))
{
if (m_hWnd)
{
fResult = DestroyWindow(m_hWnd);
m_hWnd = nullptr;
}
if (m_hmodRichEdit)
{
fResult = FreeLibrary(m_hmodRichEdit);
m_hmodRichEdit = nullptr;
}
}
return hr;
}
//
// Position the rich edit control based on coordinates provided.
//
HRESULT
CSampleRichEditWindow::Position(
_In_ UINT x,
_In_ UINT y,
_In_ UINT Width,
_In_ UINT Height
)
{
HRESULT hr = S_OK;
BOOL fResult;
// Move the Rich Edit window to its location.
fResult = MoveWindow(m_hWnd, // Move myself
x, // New position of the left side of the window
y, // New position of the top side of the window
Width, // New width
Height, // New height
TRUE); // Repaint the window
if (!fResult)
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
return hr;
}
//
// Position the rich edit control relative to the dpi and RECT provided.
//
HRESULT
CSampleRichEditWindow::PositionRelativeToRect(
_In_ RECT relativeRect
)
{
// Store target dimenions.
UINT childX; // Position of the left side of the window.
UINT childY; // Position of the top side of the window.
UINT childcX; // Width of the window.
UINT childcY; // Height of the window.
// Define offsets relative to the RECT provided.
UINT parentHeight = 0;
UINT offsetTop = 5;
UINT offsetBottom = 5;
UINT offsetRight = 10;
UINT offsetLeft = 5;
// Set the bounds for the child window based on offsets from RECT.
parentHeight = relativeRect.bottom - relativeRect.top;
childX = relativeRect.left + offsetLeft;
childY = relativeRect.top + offsetTop;
childcX = relativeRect.right - offsetRight;
childcY = parentHeight - offsetTop - offsetBottom;
return Position(childX, childY, childcX, childcY);
}
//
// Respond to an explicit DPI change, including updates to the zoom factor as needed.
// As the rich edit control uses a numerator/denominator pair that must not exceed 64,
// this class will provide numerator/denominator that will generate an implied fraction
// between 0 and 1.
//
HRESULT
CSampleRichEditWindow::OnDPIChanged(
_In_ float dpi
)
{
HRESULT hr = S_OK;
// Only take action if DPI is different from what control has stored.
if (m_currentDpi != dpi)
{
m_numerator = (UINT) ((dpi / default_dpi) * 100.0F);
hr = ApplyZoomFactor();
}
if (SUCCEEDED(hr))
{
// Update current DPI.
m_currentDpi = dpi;
}
return hr;
}
void
CSampleRichEditWindow::SetDefaultDPI(float dpi)
{
m_currentDpi = dpi;
default_dpi = dpi;
}
HRESULT
CSampleRichEditWindow::IncrementFontSize()
{
return UpdateFontSize(CD_INCREMENT);
}
HRESULT
CSampleRichEditWindow::DecrementFontSize()
{
return UpdateFontSize(CD_DECREMENT);
}
//
// Enables caller to toggle the bold setting of the text in the control.
// In order to format the text in the control, we need to construct
// the parameters required to deliver the EM_SETCHARFORMAT message.
//
HRESULT
CSampleRichEditWindow::SetFormatBold()
{
HRESULT hr = S_OK;
LRESULT lr;
CHARFORMAT2 charFormat;
ZeroMemory(&charFormat, sizeof(charFormat));
charFormat.cbSize = sizeof(charFormat);
lr = SendMessage(m_hWnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &charFormat);
if (((charFormat.dwMask & CFM_BOLD) == CFM_BOLD) && ((charFormat.dwEffects & CFE_BOLD) == CFE_BOLD))
{
ZeroMemory(&charFormat, sizeof(charFormat));
charFormat.cbSize = sizeof(charFormat);
charFormat.dwMask = CFM_BOLD;
lr = SendMessage(m_hWnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &charFormat);
}
else
{
ZeroMemory(&charFormat, sizeof(charFormat));
charFormat.cbSize = sizeof(charFormat);
charFormat.dwMask = CFM_BOLD;
charFormat.dwEffects |= CFE_BOLD;
lr = SendMessage(m_hWnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &charFormat);
}
hr = HRESULT_FROM_WIN32(GetLastError());
return hr;
}
//
// Enables caller to toggle the italic setting of the text in the control.
// In order to format the text in the control, we need to construct
// the parameters required to deliver the EM_SETCHARFORMAT message.
//
HRESULT
CSampleRichEditWindow::SetFormatItalic()
{
HRESULT hr = S_OK;
LRESULT lr;
CHARFORMAT2 charFormat;
ZeroMemory(&charFormat, sizeof(charFormat));
charFormat.cbSize = sizeof(charFormat);
lr = SendMessage(m_hWnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &charFormat);
if (((charFormat.dwMask & CFM_ITALIC) == CFM_ITALIC) && ((charFormat.dwEffects & CFE_ITALIC) == CFE_ITALIC))
{
ZeroMemory(&charFormat, sizeof(charFormat));
charFormat.cbSize = sizeof(charFormat);
charFormat.dwMask = CFM_ITALIC;
lr = SendMessage(m_hWnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &charFormat);
}
else
{
ZeroMemory(&charFormat, sizeof(charFormat));
charFormat.cbSize = sizeof(charFormat);
charFormat.dwMask = CFM_ITALIC;
charFormat.dwEffects |= CFE_ITALIC;
lr = SendMessage(m_hWnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &charFormat);
}
hr = HRESULT_FROM_WIN32(GetLastError());
return hr;
}
//
// Enables caller to toggle the underline setting of the text in the control.
// In order to format the text in the control, we need to construct
// the parameters required to deliver the EM_SETCHARFORMAT message.
//
HRESULT
CSampleRichEditWindow::SetFormatUnderline()
{
HRESULT hr = S_OK;
LRESULT lr;
CHARFORMAT2 charFormat;
ZeroMemory(&charFormat, sizeof(charFormat));
charFormat.cbSize = sizeof(charFormat);
lr = SendMessage(m_hWnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &charFormat);
if (((charFormat.dwMask & CFM_UNDERLINE) == CFM_UNDERLINE) && ((charFormat.dwEffects & CFE_UNDERLINE) == CFE_UNDERLINE))
{
ZeroMemory(&charFormat, sizeof(charFormat));
charFormat.cbSize = sizeof(charFormat);
charFormat.dwMask = CFM_UNDERLINE;
lr = SendMessage(m_hWnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &charFormat);
}
else
{
ZeroMemory(&charFormat, sizeof(charFormat));
charFormat.cbSize = sizeof(charFormat);
charFormat.dwMask = CFM_UNDERLINE;
charFormat.dwEffects |= CFE_UNDERLINE;
lr = SendMessage(m_hWnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &charFormat);
}
hr = HRESULT_FROM_WIN32(GetLastError());
return hr;
}
//
// Enables caller to set the color of the selected text in the control.
// In order to format the text in the control, we need to construct
// the parameters required to deliver the EM_SETCHARFORMAT message.
//
HRESULT
CSampleRichEditWindow::SetFormatColor()
{
HRESULT hr = S_OK;
LRESULT lr;
CHARFORMAT2 charFormat;
ZeroMemory(&charFormat, sizeof(charFormat));
charFormat.cbSize = sizeof(charFormat);
lr = SendMessage(m_hWnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &charFormat);
if (((charFormat.dwMask & CFM_COLOR) == CFM_COLOR) && (((charFormat.dwEffects & CFE_AUTOCOLOR) != CFE_AUTOCOLOR) && (charFormat.crTextColor==RGB(255,0,0))))
{
ZeroMemory(&charFormat, sizeof(charFormat));
charFormat.cbSize = sizeof(charFormat);
charFormat.dwMask = CFM_COLOR;
lr = SendMessage(m_hWnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &charFormat);
}
else
{
ZeroMemory(&charFormat, sizeof(charFormat));
charFormat.cbSize = sizeof(charFormat);
charFormat.dwMask = CFM_COLOR;
charFormat.crTextColor = RGB(255, 0, 0);
lr = SendMessage(m_hWnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &charFormat);
}
hr = HRESULT_FROM_WIN32(GetLastError());
return hr;
}
//
// Enables caller to set the background color of the selected text in the control.
// In order to format the text in the control, we need to construct
// the parameters required to deliver the EM_SETCHARFORMAT message.
//
HRESULT
CSampleRichEditWindow::SetFormatBackgroundColor()
{
HRESULT hr = S_OK;
LRESULT lr;
CHARFORMAT2 charFormat;
ZeroMemory(&charFormat, sizeof(charFormat));
charFormat.cbSize = sizeof(charFormat);
lr = SendMessage(m_hWnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &charFormat);
if (((charFormat.dwMask & CFM_BACKCOLOR) == CFM_BACKCOLOR) && (((charFormat.dwEffects & CFE_AUTOCOLOR) != CFE_AUTOCOLOR) && (charFormat.crBackColor == RGB(255, 255, 0))))
{
ZeroMemory(&charFormat, sizeof(charFormat));
charFormat.cbSize = sizeof(charFormat);
charFormat.dwMask = CFM_BACKCOLOR;
charFormat.crBackColor = RGB(255, 255, 255);
lr = SendMessage(m_hWnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &charFormat);
}
else
{
ZeroMemory(&charFormat, sizeof(charFormat));
charFormat.cbSize = sizeof(charFormat);
charFormat.dwMask = CFM_BACKCOLOR;
charFormat.crBackColor = RGB(255, 255, 0);
lr = SendMessage(m_hWnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &charFormat);
}
hr = HRESULT_FROM_WIN32(GetLastError());
return hr;
}
//
// Enables caller to set the alignment of the selected paragraph.
// In order to format the text in the control, we need to construct
// the parameters required to deliver the EM_SETCHARFORMAT message.
//
HRESULT
CSampleRichEditWindow::SetAlignment(
_In_ WORD wAlignment
)
{
HRESULT hr = S_OK;
LRESULT lr;
PARAFORMAT2 paraFormat;
ZeroMemory(¶Format, sizeof(paraFormat));
paraFormat.cbSize = sizeof(paraFormat);
paraFormat.dwMask = PFM_ALIGNMENT;
paraFormat.wAlignment = wAlignment;
lr = SendMessage(m_hWnd, EM_SETPARAFORMAT, 0, (LPARAM) ¶Format);
hr = HRESULT_FROM_WIN32(GetLastError());
return hr;
}
//
// Enables caller to set the selected paragraph(s) as bulleted.
// In order to format the text in the control, we need to construct
// the parameters required to deliver the EM_SETPARAFORMAT message.
//
HRESULT
CSampleRichEditWindow::SetBulleted()
{
HRESULT hr = S_OK;
LRESULT lr;
PARAFORMAT2 paraFormat;
ZeroMemory(¶Format, sizeof(paraFormat));
paraFormat.cbSize = sizeof(paraFormat);
lr = SendMessage(m_hWnd, EM_GETPARAFORMAT, 0, (LPARAM) ¶Format);
if (((paraFormat.dwMask & PFM_NUMBERING) == PFM_NUMBERING) && (paraFormat.wNumbering == PFN_BULLET))
{
paraFormat.wNumbering = 0;
}
else
{
paraFormat.wNumbering = PFN_BULLET;
}
paraFormat.dwMask = PFM_NUMBERING;
lr = SendMessage(m_hWnd, EM_SETPARAFORMAT, 0, (LPARAM) ¶Format);
hr = HRESULT_FROM_WIN32(GetLastError());
return hr;
}
//
// Enables caller to set the selected paragraph(s) as numbered.
// In order to format the text in the control, we need to construct
// the parameters required to deliver the EM_SETPARAFORMAT message.
//
HRESULT
CSampleRichEditWindow::SetNumbered()
{
HRESULT hr = S_OK;
LRESULT lr;
PARAFORMAT2 paraFormat;
ZeroMemory(¶Format, sizeof(paraFormat));
paraFormat.cbSize = sizeof(paraFormat);
lr = SendMessage(m_hWnd, EM_GETPARAFORMAT, 0, (LPARAM) ¶Format);
if (((paraFormat.dwMask & PFM_NUMBERING) == PFM_NUMBERING) && (paraFormat.wNumbering == 7))
{
paraFormat.wNumbering = 0;
}
else
{
paraFormat.wNumbering = 7;
}
paraFormat.dwMask = PFM_NUMBERING;
lr = SendMessage(m_hWnd, EM_SETPARAFORMAT, 0, (LPARAM) ¶Format);
hr = HRESULT_FROM_WIN32(GetLastError());
return hr;
}
//
// Enables caller to set the alignment of the selected paragraph.
// In order to format the text in the control, we need to construct
// the parameters required to deliver the EM_SETCHARFORMAT message.
//
HRESULT
CSampleRichEditWindow::IncrementIndent(
_In_ INT lIncrement
)
{
HRESULT hr = S_OK;
LRESULT lr;
PARAFORMAT2 paraFormat;
ZeroMemory(¶Format, sizeof(paraFormat));
paraFormat.cbSize = sizeof(paraFormat);
lr = SendMessage(m_hWnd, EM_GETPARAFORMAT, 0, (LPARAM) ¶Format);
paraFormat.dwMask = PFM_STARTINDENT;
paraFormat.dxStartIndent = (paraFormat.dxStartIndent + lIncrement < 0) ? 0 : paraFormat.dxStartIndent + lIncrement;
lr = SendMessage(m_hWnd, EM_SETPARAFORMAT, 0, (LPARAM) ¶Format);
hr = HRESULT_FROM_WIN32(GetLastError());
return hr;
}
//
// This method restores the contents of the rich edit control to defaults.
// Default configuration includes text content, as well as formatting.
//
HRESULT
CSampleRichEditWindow::ResetDefaultContent()
{
HRESULT hr = S_OK;
PCWSTR richEditText = NULL;
// Set text to display in rich edit control from resource file.
if (SUCCEEDED(hr))
{
hr = GetResourceString(IDS_LOREM_IPSUM, &richEditText);
}
if (SUCCEEDED(hr))
{
hr = SetText(richEditText);
}
// Initialize the default format for the rich edit control.
if (SUCCEEDED(hr))
{
hr = SetDefaultFormat();
}
// Make sure control is formatted correctly for current DPI.
if (SUCCEEDED(hr))
{
hr = ApplyZoomFactor();
}
return hr;
}
//
// This method retrieves a string based on the requested resource ID.
// Once the string is converted from resource ID to string, this method calls
// the overload to apply the text to the rich edit control.
//
HRESULT
CSampleRichEditWindow::GetResourceString(
_In_ UINT stringID,
_Outptr_ PCWSTR * stringText
)
{
HRESULT hr = S_OK;
BOOL fResult = FALSE;
int stringSize = 0;
HMODULE hMod = nullptr;
// Get a non-ref counted handle to the current module.
// Module handle is required to use LoadString.
fResult = GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
NULL,
&hMod);
if (!fResult)
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
// Per MSDN documentation, we can retrieve a read-only pointer by
// passing in a PWSTR and a zero-size buffer.
if (fResult)
{
stringSize = LoadStringW(hMod, stringID, (PWSTR)stringText, 0);
}
if (stringSize <= 0)
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
return hr;
}
//
// This method explictly applies the text requested to the
// rich edit control.
//
HRESULT
CSampleRichEditWindow::SetText(
_In_ PCWSTR stringText
)
{
HRESULT hr = S_OK;
LRESULT lr;
ASSERT(m_hWnd);
ASSERT(stringText);
lr = SendMessage(m_hWnd, WM_SETTEXT, (WPARAM) NULL, (LPARAM) stringText);
if (!lr)
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
return hr;
}
//
// This method resets the rich edit control to default values
// for text formatting. This method will use the default values for rich edit,
// except for bold and font color, which will be set explicitly.
//
HRESULT
CSampleRichEditWindow::SetDefaultFormat()
{
HRESULT hr = S_OK;
LRESULT lr;
CHARFORMAT2 charFormat;
ZeroMemory(&charFormat, sizeof(charFormat));
// Gather the default settings that exist on the rich edit control.
charFormat.cbSize = sizeof(CHARFORMAT2) ;
lr = SendMessage(m_hWnd, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &charFormat);
if (lr)
{
// Set text to be bold.
charFormat.dwMask = CFM_BOLD;
// Set text color to be black.
charFormat.dwMask |= CFM_COLOR;
charFormat.crTextColor = RGB(0, 0, 0);
// Set font size using inline conversion to twips.
charFormat.dwMask |= CFM_SIZE;
charFormat.yHeight = (LONG) (DEFAULT_FONT_PT * TWIPS_PER_PT);
// Notify the rich edit control of the requested format.
lr = SendMessage(m_hWnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &charFormat);
}
else
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
return hr;
}
//
// This method provides an internal implementation to explicitly
// increment or decrement font size.
//
HRESULT
CSampleRichEditWindow::UpdateFontSize(
_In_ CHANGE_DIRECTION direction
)
{
HRESULT hr = S_OK;
LRESULT lr;
CHARFORMAT2 charFormat;
ZeroMemory(&charFormat, sizeof(charFormat));
// Gather the settings that exist on the rich edit control.
charFormat.cbSize = sizeof(charFormat);
lr = SendMessageW(m_hWnd, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &charFormat);
// Note that only the font size is changing.
if (lr)
{
charFormat.dwMask = CFM_SIZE;
if (direction == CD_INCREMENT)
{
charFormat.yHeight = (LONG) (charFormat.yHeight * FONT_STEP);
}
else
{
charFormat.yHeight = (LONG) (charFormat.yHeight / FONT_STEP);
}
// Notify rich edit control of new format.
lr = SendMessage(m_hWnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &charFormat);
}
else
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
return hr;
}
//
// Set the zoom factor based on values stored in rich edit member variables.
// Once the zoom factor is applied, update the window to redraw.
//
HRESULT
CSampleRichEditWindow::ApplyZoomFactor()
{
HRESULT hr = S_OK;
LRESULT lr;
lr = SendMessage(m_hWnd, EM_SETZOOM, (WPARAM) m_numerator, (LPARAM) m_denominator);
if (!lr)
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
return hr;
}