I'm using Objective Grid and wanted to have a grid cell control that can show an icon and text. So I took RogueWave's example (https://rwkbp.makekb.com/entry/466/) and modified it as below.
Initially the text renders correctly, but if I do anything in the grid that refreshes the relevant cell, the text disappears. If I update the whole grid, or even refresh the cell by dragging it off screen and back again, the text reappears - until it disappears again. Seems like a paint or InvalidateRect problem - but I can't figure out how to fix it. Any ideas?
Thanks!
//------------------------------------------------------------------------------
void CGXIconControl::Draw(CDC* pDC, CRect rect, ROWCOL nRow, ROWCOL nCol, const CGXStyle& style, const CGXStyle* pStandardStyle)
//------------------------------------------------------------------------------
{
BOOL b;
ASSERT(pDC != NULL && pDC->IsKindOf(RUNTIME_CLASS(CDC)));
// ASSERTION-> Invalid Device Context ->END
ASSERT(nRow <= Grid()->GetRowCount() && nCol <= Grid()->GetColCount());
// ASSERTION-> Cell coordinates out of range ->END
ASSERT_VALID(pDC);
DrawBackground(pDC, rect, style);
pDC->SetBkMode(TRANSPARENT);
if (rect.right <= rect.left || rect.Width() <= 1 || rect.Height() <= 1)
{
return;
}
CString str = style.GetIncludeValue() ? style.GetValue() : _T("");
CString sTxtBefore = _T("");
CString sTxtAfter = _T("");
int nHAlign = style.GetHorizontalAlignment();
int nVAlign = style.GetVerticalAlignment();
// Save these value to restore them when done drawing
COLORREF crOldTextColor = pDC->GetTextColor();
COLORREF crOldBkColor = pDC->GetBkColor();
if (!style.GetEnabled())
{
pDC->SetTextColor(::GetSysColor(COLOR_GRAYTEXT));
}
CBrush Brush;
Brush.CreateSolidBrush(style.GetEnabled() ? ::GetSysColor(COLOR_WINDOWTEXT) : ::GetSysColor(COLOR_GRAYTEXT));
LOGBRUSH lBrush = { 0 };
Brush.GetLogBrush(&lBrush);
CBrush* pOldBrush = (CBrush*)pDC->SelectStockObject(NULL_BRUSH);
CPen linePen;
linePen.CreatePen(PS_SOLID, 1, &lBrush);
CPen* pOldPen = pDC->SelectObject(&linePen);
// Set font bold if necessary
CFont* pCurfont = pDC->GetCurrentFont();
LOGFONT lf;
CFont font;
if (pCurfont)
{
pCurfont->GetLogFont(&lf);
}
if (style.GetFont().GetBold())
{
lf.lfWeight = FW_BOLD;
}
font.CreateFontIndirect(&lf);
CFont* pOldFont = pDC->SelectObject(&font);
int nIcoStart = str.Find(_T("#ICO"));
if (nIcoStart == -1)
{
// We didn't find an icon indicator, so just draw the text
pDC->DrawText(str, rect, DT_SINGLELINE|nHAlign|nVAlign);
}
else
{
sTxtBefore = str.Left(nIcoStart);
CSize szBefore = pDC->GetTextExtent(sTxtBefore);
int nIconEnd = str.Find(_T(")"), nIcoStart);
CString strIDResource = str.Mid(nIcoStart + 5, nIconEnd - (nIcoStart + 5));
UINT nIDResource = _ttoi(strIDResource);
// Load the highest bit-depth available
HICON hIcon = NULL;
hIcon = LoadResourceIcon(nIDResource, m_nIconSize, m_nIconSize, 32);
hIcon == NULL ? LoadResourceIcon(nIDResource, m_nIconSize, m_nIconSize, 24) : NULL;
hIcon == NULL ? LoadResourceIcon(nIDResource, m_nIconSize, m_nIconSize, 16) : NULL;
hIcon == NULL ? LoadResourceIcon(nIDResource, m_nIconSize, m_nIconSize, 8) : NULL;
sTxtAfter = str.Right(str.GetLength() - nIconEnd - 1);
CSize szAfter = pDC->GetTextExtent(sTxtAfter);
CRect rectCell = CGXControl::GetCellRect(nRow, nCol, rect, &style);
CRect rectBefore = rectCell;
CRect rectAfter = rectCell;
int nTotalWidth = szBefore.cx + m_nIconSize + szAfter.cx;
// Calculate positions
int nTop, nLeft;
switch (nHAlign)
{
case DT_LEFT:
{
rectBefore.right = rectBefore.left + szBefore.cx;
nLeft = rectBefore.right;
rectAfter.left = nLeft + m_nIconSize;
rectAfter.right = rectAfter.left + szAfter.cx;
} break;
case DT_CENTER:
{
rectBefore.left = (rectCell.right - rectCell.Width() / 2) - nTotalWidth / 2;
rectBefore.right = rectBefore.left + szBefore.cx;
nLeft = rectBefore.right;
rectAfter.left = nLeft + m_nIconSize;
rectAfter.right = rectAfter.left + szAfter.cx;
} break;
case DT_RIGHT:
{
// Work from the right
rectAfter.right = rectCell.right;
rectAfter.left = rectAfter.right - szAfter.cx;
nLeft = rectAfter.left - m_nIconSize;
rectBefore.right = nLeft;
rectBefore.left = rectBefore.right - szBefore.cx;
} break;
}
switch (nVAlign)
{
case DT_TOP:
nTop = rectCell.top;
break;
case DT_VCENTER:
nTop = rectCell.top + (rectCell.Height() / 2 - m_nIconSize / 2);
break;
case DT_BOTTOM:
nTop = rectCell.bottom - m_nIconSize;
break;
}
if (!sTxtBefore.IsEmpty())
{
pDC->DrawText(sTxtBefore, rectBefore, DT_SINGLELINE|nHAlign|nVAlign);
}
b = ::DrawIconEx(pDC->m_hDC, nLeft, nTop, hIcon, m_nIconSize, m_nIconSize, 0, NULL, DI_NORMAL);
b = ::DestroyIcon(hIcon);
if (!sTxtAfter.IsEmpty())
{
pDC->DrawText(sTxtAfter, rectAfter, DT_SINGLELINE|nHAlign|nVAlign);
}
}
// Reset original values
pDC->SetTextColor(crOldTextColor);
pDC->SetBkColor(crOldBkColor);
pDC->SelectObject(pOldBrush);
pDC->SelectObject(pOldPen);
pDC->SelectObject(pOldFont);
// Child Controls: spin-buttons, hotspot, combobox btn, ...
CGXControl::Draw(pDC, rect, nRow, nCol, style, pStandardStyle);
}
Related
I have old MFC based application. Recently we moved from Trébuchent to Roboto font. From that time onwards on some machines we are finding weird characters in the application.
I am using the CreateFontIndirect function to create the font. The IDE that I am using is Visual studio 2008.
This issue is not affecting the entire application. It is only affecting a few places.
I have observed a pattern in those junk characters. Is appears that it is adding 2 to every ASCII value. For example: if I want to display ABCD it is showing CDEF.
Please help me out in finding this. Thanks in Advance.
Here is my code sample :
void Render (CDC *pDC)
{
CString strText;
CFont *pFont = GetLocalFontStore()->LoadFont(_T("Roboto"), (int)(26 *fSizeFactor) , FW_EXTRABOLD, false, false);
COLORREF txtColor =RGB(255,0,0);
CRect rcTobeDrawn = CRect( (CPoint(177,172)), (CPoint(636,215)));
strText = _T(“This is my string”);
CFont *pOldfont = pDC->SelectObject(pFont);
COLORREF oldTxtColor = pDC->SetTextColor(txtColor);
int oldBkMode = pDC->SetBkMode(TRANSPARENT);
pDC->DrawText(strText, rcTobeDrawn, DT_CENTER|DT_NOCLIP|DT_VCENTER);
pDC->SelectObject(pOldfont);
pDC->SetBkMode(oldBkMode);
pDC->SetTextColor(oldTxtColor);
}
CFont* CLocalFontStore::LoadFont(CString strFaceName,int height,int weight/*=FW_REGULAR*/,
bool bUnderLine/*=false*/,bool bItalics/* = false*/, BYTE charSet /*= DEFAULT_CHARSET*/,bool bHeightFlag/* = false*/)
{
if(m_nCharSet == -1)
m_nCharSet = GetCharset();
LOGFONT lf;
CFont* pFont = NULL;
CString strFontText;
memset(&lf, 0, sizeof(LOGFONT));
swprintf_s(lf.lfFaceName,GetCommonLobby()->GetSupportedFontName(strFaceName));
double d72DPIHeight = (STANDARD_DPI * (float)height) / 72.0;
lf.lfHeight = (long) (d72DPIHeight < 0.0 ? ceil(d72DPIHeight - 0.5) : floor(d72DPIHeight + 0.5));
lf.lfWeight = weight >0 ? weight : FW_DONTCARE;
lf.lfUnderline = bUnderLine ? true :false;
lf.lfItalic = bItalics;
lf.lfCharSet = m_nCharSet;
strFontText.Format(_T("%s~%d~%d~%d~%d~%d"), lf.lfFaceName, lf.lfHeight, lf.lfWeight, lf.lfUnderline, lf.lfItalic, m_nCharSet);
if(m_FontMap.size() != 0)
pFont = GetFont(strFontText);
if(pFont == NULL)
{
HFONT hFont = GetIFontStore()->LoadFont(strFaceName, height, weight, bUnderLine, bItalics, charSet,bHeightFlag);
if(hFont != NULL)
{
pFont = new CFont;
pFont->Attach(hFont);
m_FontMap[strFontText] = pFont;
}
}
return pFont;
}
CFont* CLocalFontStore::LoadFont(CString strFaceName,int height,int weight/*=FW_REGULAR*/,
bool bUnderLine/*=false*/,bool bItalics/* = false*/, BYTE charSet /*= DEFAULT_CHARSET*/,bool bHeightFlag/* = false*/)
{
if(m_nCharSet == -1)
m_nCharSet = GetCharset();
LOGFONT lf;
CFont* pFont = NULL;
CString strFontText;
memset(&lf, 0, sizeof(LOGFONT));
swprintf_s(lf.lfFaceName,GetCommonLobby()->GetSupportedFontName(strFaceName));
double d72DPIHeight = (STANDARD_DPI * (float)height) / 72.0;
lf.lfHeight = (long) (d72DPIHeight < 0.0 ? ceil(d72DPIHeight - 0.5) : floor(d72DPIHeight + 0.5));
lf.lfWeight = weight >0 ? weight : FW_DONTCARE;
lf.lfUnderline = bUnderLine ? true :false;
lf.lfItalic = bItalics;
lf.lfCharSet = m_nCharSet;
strFontText.Format(_T("%s~%d~%d~%d~%d~%d"), lf.lfFaceName, lf.lfHeight, lf.lfWeight, lf.lfUnderline, lf.lfItalic, m_nCharSet);
if(m_FontMap.size() != 0)
pFont = GetFont(strFontText);
if(pFont == NULL)
{
HFONT hFont = GetIFontStore()->LoadFont(strFaceName, height, weight, bUnderLine, bItalics, charSet,bHeightFlag);
if(hFont != NULL)
{
pFont = new CFont;
pFont->Attach(hFont);
m_FontMap[strFontText] = pFont;
}
}
return pFont;
}
HFONT CFontStore::LoadPointFont(CString strFaceName,int height,int weight/*=FW_REGULAR*/,
bool bUnderLine/*=false*/,bool bItalics/* = false*/, BYTE charSet /*= DEFAULT_CHARSET*/,bool bHeightFlag/* = false*/, bool bIsEnglish/* = false*/)
{
LOGFONT lf;
CString strFontText;
memset(&lf, 0, sizeof(LOGFONT));
if (!_tcsicmp(strFaceName, _T("Small Fonts")))
{
_stprintf(lf.lfFaceName, strFaceName);
}
else
{
_stprintf(lf.lfFaceName,GetCommonLobby()->GetSupportedFontName(strFaceName));
}
double d72DPIHeight = (STANDARD_DPI * (float)height) / 72.0;
lf.lfHeight = (long) (d72DPIHeight < 0.0 ? ceil(d72DPIHeight - 0.5) : floor(d72DPIHeight + 0.5));
lf.lfWeight = weight >0 ? weight : FW_DONTCARE;
lf.lfUnderline = bUnderLine ? true :false;
lf.lfItalic = bItalics;
lf.lfCharSet = GetCharset();
if(!bIsEnglish)
{
ChangeFont(lf, 10);
}
strFontText.Format(_T("%s~%d~%d~%d~%d~%d"),lf.lfFaceName,lf.lfHeight,lf.lfWeight,lf.lfUnderline,lf.lfItalic, GetCharset());
HFONT hFont = NULL;
if(m_FontMap.size() != 0)
{
hFont = GetFont(strFontText);
}
if(hFont == NULL)
{
lf.lfHeight = -lf.lfHeight;
HFONT hf = CreateFontIndirect(&lf);
if(NULL != hf)
{
hFont = hf;
m_FontMap[strFontText] = hFont;
}
}
return hFont;
}
basically I have a scrollbar in a custom window where we could add other controls like buttons, textboxes and so and so forth, now here is the problem I have created the scrollbar alright it scrolls down and up, but the problem is that the maximum scroll should be specified which then blocks it to scroll more than that, but there could be as much control as the user want, which for me it would not be known, I mean like to tell the problem to be more like a textbox's built-in scroll bar when you specify it with WM_VSCROLL OR HSCROLL, and then give the user the ability to scroll as much as you type on the textbox, that's exactly what I want.
here is the code so far I have been able to go through:-
Code for Scrollbar:-
case WM_LBUTTONDOWN:
{
SCROLLINFO si = { 0 };
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_POS;
si.nPos = 0;
si.nTrackPos = 0;
GetScrollInfo(hwnd, SB_VERT, &si);
break;
}
case WM_VSCROLL:
{
auto action = LOWORD(wParam);
HWND hScroll = (HWND)lParam;
int pos = -1;
if (action == SB_THUMBPOSITION || action == SB_THUMBTRACK) {
pos = HIWORD(wParam);
} else if (action == SB_LINEDOWN) {
pos = g_scrollY + 50;
} else if (action == SB_LINEUP) {
pos = g_scrollY - 50;
} else if (action == SB_PAGEUP) {
GetClientRect(hwnd, &r);
pos = g_scrollY - r.bottom;
} else if (action == SB_PAGEDOWN){
GetClientRect(hwnd, &r);
pos = g_scrollY + r.bottom;
}
if (pos == -1)
break;
SCROLLINFO si = { 0 };
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_POS;
si.nPos = pos;
si.nTrackPos = 0;
SetScrollInfo(hwnd, SB_VERT, &si, true);
GetScrollInfo(hwnd, SB_VERT, &si);
pos = si.nPos;
POINT pt;
pt.x = 0;
pt.y = pos - g_scrollY;
auto hdc = GetDC(hwnd);
LPtoDP(hdc, &pt, 1);
ReleaseDC(hwnd, hdc);
ScrollWindow(hwnd, 0, -pt.y, NULL, NULL);
g_scrollY = pos;
return 0;
}
case WM_SIZE:
{
RECT rc = { 0 };
GetClientRect(hwnd, &rc);
SCROLLINFO si = { 0 };
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_ALL;
si.nMin = 0;
si.nMax = MAX_RANGE;
if(si.nPos > 100)
si.nMax = 5000;
if(SB_THUMBPOSITION == 100){
si.nMax = MAX_RANGE;}
si.nPage = (rc.bottom - rc.top);
si.nPos = 0;
si.nTrackPos = 0;
SetScrollInfo(hwnd, SB_VERT, &si, true);
break;
}
as you can see si.nMax value is the maximum amount of lines it is going to be able to scroll, but I want it to be as much as there are controls.
You must know how many controls are in your window, so you can set nMax member in the SCROLLINFO structure to whatever value you want and call SetScrollInfo() whenever new controls are added.
I know that there is a lot questions about how to set console size. But all found solutions are the same to my and my code doesn't works for me.
Ok, so for setting console window size, I need two functions. They are SetConsoleScreenBufferSize() and SetConsoleWindowInfo(). First version of my function:
bool SetWindowSize(size_t width, size_t height)
{
HANDLE output_handle = ::GetStdHandle(STD_OUTPUT_HANDLE);
if(output_handle == INVALID_HANDLE_VALUE)
return false;
COORD coord = {};
coord.X = static_cast<SHORT>(width);
coord.Y = static_cast<SHORT>(height);
if(::SetConsoleScreenBufferSize(output_handle, coord) == FALSE)
return false;
SMALL_RECT rect = {};
rect.Bottom = coord.X - 1;
rect.Right = coord.Y - 1;
return (::SetConsoleWindowInfo(output_handle, TRUE, &rect) != FALSE);
}
SetConsoleScreenBufferSize() will work not for all values. From documentation:
The specified width and height cannot be less than the width and
height of the console screen buffer's window
Lets try to get current window's size and call our function. To get window size, I need GetConsoleScreenBufferInfo() function. main() test code:
HANDLE output_handle = ::GetStdHandle(STD_OUTPUT_HANDLE);
if(output_handle == INVALID_HANDLE_VALUE)
return 0;
CONSOLE_SCREEN_BUFFER_INFO info = {};
if(::GetConsoleScreenBufferInfo(output_handle, &info) == FALSE)
return 0;
size_t width = info.srWindow.Right - info.srWindow.Left;
size_t height = info.srWindow.Bottom - info.srWindow.Top;
bool suc = SetWindowSize(width + 1, height + 1);
In this case SetConsoleScreenBufferSize() works fine. Next function is SetConsoleWindowInfo(). This function will work in case:
The function fails if the specified window rectangle extends beyond
the boundaries of the console screen buffer. This means that the Top
and Left members of the lpConsoleWindow rectangle (or the calculated
top and left coordinates, if bAbsolute is FALSE) cannot be less than
zero. Similarly, the Bottom and Right members (or the calculated
bottom and right coordinates) cannot be greater than (screen buffer
height – 1) and (screen buffer width – 1), respectively. The function
also fails if the Right member (or calculated right coordinate) is
less than or equal to the Left member (or calculated left coordinate)
or if the Bottom member (or calculated bottom coordinate) is less than
or equal to the Top member (or calculated top coordinate).
In our case, the values of rectangle are the same (because Left and Top are zeroes) as values of info.srWindow rectangle after call of GetConsoleScreenBufferInfo(). But! SetConsoleWindowInfo() fails with next ::GetLastError()
#err,hr ERROR_INVALID_PARAMETER : The parameter is incorrect. unsigned int
If I swap calls of this two functions:
bool SetWindowSize(size_t width, size_t height)
{
HANDLE output_handle = ::GetStdHandle(STD_OUTPUT_HANDLE);
if(output_handle == INVALID_HANDLE_VALUE)
return false;
SMALL_RECT rect = {};
rect.Bottom = static_cast<SHORT>(width);
rect.Right = static_cast<SHORT>(height);
if(::SetConsoleWindowInfo(output_handle, TRUE, &rect) == FALSE)
return false;
COORD coord = {};
coord.X = rect.Bottom + 1;
coord.Y = rect.Right + 1;
return (::SetConsoleScreenBufferSize(output_handle, coord) != FALSE);
}
then I will have the same error.
So, how can I use SetConsoleScreenBufferSize() and SetConsoleWindowInfo() correctly ?
SetConsoleWindowInfo() does not reposition the console window on the screen. The name of this function is misleading. It rather scrolls the current visible portion inside the console window. See this sample here.
If you want to set the position of a console window that runs your programm, use code such as:
HWND hwnd = GetConsoleWindow();
RECT rect = {100, 100, 300, 500};
MoveWindow(hwnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,TRUE);
From the TurboVision port:
void TDisplay::setCrtMode( ushort mode )
{
int oldr = getRows();
int oldc = getCols();
int cols = uchar(mode >> 8);
int rows = uchar(mode);
if ( cols == 0 ) cols = oldc;
if ( rows == 0 ) rows = oldr;
checksize(rows, cols);
COORD newSize = { cols, rows };
SMALL_RECT rect = { 0, 0, cols-1, rows-1 };
if ( oldr <= rows )
{
if ( oldc <= cols )
{ // increasing both dimensions
BUFWIN:
SetConsoleScreenBufferSize( TThreads::chandle[cnOutput], newSize );
SetConsoleWindowInfo( TThreads::chandle[cnOutput], True, &rect );
}
else
{ // cols--, rows+
SMALL_RECT tmp = { 0, 0, cols-1, oldr-1 };
SetConsoleWindowInfo( TThreads::chandle[cnOutput], True, &tmp );
goto BUFWIN;
}
}
else
{
if ( oldc <= cols )
{ // cols+, rows--
SMALL_RECT tmp = { 0, 0, oldc-1, rows-1 };
SetConsoleWindowInfo( TThreads::chandle[cnOutput], True, &tmp );
goto BUFWIN;
}
else
{ // cols--, rows--
SetConsoleWindowInfo( TThreads::chandle[cnOutput], True, &rect );
SetConsoleScreenBufferSize( TThreads::chandle[cnOutput], newSize );
}
}
GetConsoleScreenBufferInfo( TThreads::chandle[cnOutput], &TThreads::sbInfo );
}
ushort TDisplay::getRows()
{
GetConsoleScreenBufferInfo( TThreads::chandle[cnOutput], &TThreads::sbInfo );
return TThreads::sbInfo.dwSize.Y;
}
ushort TDisplay::getCols()
{
GetConsoleScreenBufferInfo( TThreads::chandle[cnOutput], &TThreads::sbInfo );
return TThreads::sbInfo.dwSize.X;
}
I solved this issue by making these functions which can get/set the console window/buffer sizes in characters taking into account increasing the buffer size if needed, console font size, window borders and all that jazz.
The variables at play here to understand:
Windows have a client area, which is the coordinates (in pixels) excluding the borders
Windows have a window area, which is the coordinates (in pixels) including the borders
Console has a view area, which is the window size in characters
Console has a screen buffer, which is the scrollable buffer size in characters
Console has a font size, which is the character size in coordinates (pixels)
Console's screen buffer cannot be smaller than the view area
You need to correctly mix and match these around to achieve the desired result.
These functions are plug-n-play so you don't need to worry about none of that though.
All functions return TRUE (1) on success and FALSE (0) on error.
Set Console Window Size
static BOOL SetConsoleSize(int cols, int rows) {
HWND hWnd;
HANDLE hConOut;
CONSOLE_FONT_INFO fi;
CONSOLE_SCREEN_BUFFER_INFO bi;
int w, h, bw, bh;
RECT rect = {0, 0, 0, 0};
COORD coord = {0, 0};
hWnd = GetConsoleWindow();
if (hWnd) {
hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hConOut && hConOut != (HANDLE)-1) {
if (GetCurrentConsoleFont(hConOut, FALSE, &fi)) {
if (GetClientRect(hWnd, &rect)) {
w = rect.right-rect.left;
h = rect.bottom-rect.top;
if (GetWindowRect(hWnd, &rect)) {
bw = rect.right-rect.left-w;
bh = rect.bottom-rect.top-h;
if (GetConsoleScreenBufferInfo(hConOut, &bi)) {
coord.X = bi.dwSize.X;
coord.Y = bi.dwSize.Y;
if (coord.X < cols || coord.Y < rows) {
if (coord.X < cols) {
coord.X = cols;
}
if (coord.Y < rows) {
coord.Y = rows;
}
if (!SetConsoleScreenBufferSize(hConOut, coord)) {
return FALSE;
}
}
return SetWindowPos(hWnd, NULL, rect.left, rect.top, cols*fi.dwFontSize.X+bw, rows*fi.dwFontSize.Y+bh, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER);
}
}
}
}
}
}
return FALSE;
}
/* usage */
SetConsoleSize(80, 40);
Get Console Window Size
static BOOL GetConsoleSize(int* cols, int* rows) {
HWND hWnd;
HANDLE hConOut;
CONSOLE_FONT_INFO fi;
int w, h;
RECT rect = {0, 0, 0, 0};
hWnd = GetConsoleWindow();
if (hWnd) {
hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hConOut && hConOut != (HANDLE)-1) {
if (GetCurrentConsoleFont(hConOut, FALSE, &fi)) {
if (GetClientRect(hWnd, &rect)) {
w = rect.right-rect.left;
h = rect.bottom-rect.top;
*cols = w / fi.dwFontSize.X;
*rows = h / fi.dwFontSize.Y;
return TRUE;
}
}
}
}
return FALSE;
}
/* usage */
int cols, rows;
GetConsoleSize(&cols, &rows);
Set Console Buffer Size
static BOOL SetConsoleBufferSize(int cols, int rows) {
HANDLE hConOut;
CONSOLE_SCREEN_BUFFER_INFO bi;
COORD coord = {0, 0};
hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hConOut && hConOut != (HANDLE)-1) {
if (GetConsoleScreenBufferInfo(hConOut, &bi)) {
coord.X = cols;
coord.Y = rows;
return SetConsoleScreenBufferSize(hConOut, coord);
}
}
return FALSE;
}
/* usage */
SetConsoleBufferSize(80, 300);
Get Console Buffer Size
static BOOL GetConsoleBufferSize(int* cols, int* rows) {
HANDLE hConOut;
CONSOLE_SCREEN_BUFFER_INFO bi;
hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hConOut && hConOut != (HANDLE)-1) {
if (GetConsoleScreenBufferInfo(hConOut, &bi)) {
*cols = bi.dwSize.X;
*rows = bi.dwSize.Y;
return TRUE;
}
}
return FALSE;
}
/* usage */
int cols, rows;
GetConsoleBufferSize(&cols, &rows);
I've got a menu attached to a split ribbon button like this (VS2008, Feature Pack):
std::auto_ptr<CMFCRibbonButton> apBtn3(new CMFCRibbonButton(ID_RIBBON_BTN_3, _T("Split Button"), 2, 2));
apBtn3->SetMenu(IDR_RIBBON_MENU_1, TRUE);
apBtn3->SetAlwaysLargeImage();
apBtn3->RemoveSubItem(0);
std::auto_ptr<CMFCRibbonButton> apSubButton(new CMFCRibbonButton(ID_RIBBON_MBTN_1, _T("Item 1"), 2, 2));
apSubButton->SetAlwaysLargeImage();
apBtn3->AddSubItem(apSubButton.release(), 0);
pPanel1->Add(apBtn3.release());
I want to put checkboxes in front of each menu item and have provided SetCheck() calls in the CN_UPDATE_COMMAND_UI handler but checkboxes will only show up, if I disable the large icons.
Is there any way to use checkboxes along with large icons in CMFCRibbonButton menus? If not, what would be the best possible workaround?
I found a usable workaround to check large icons by alpha-blending a green circle with a checkmark in it over the icon -- the same solution Windows uses to mark the default audio device or printer in the control panel:
This is my CMFCRibbonButtonEx class declaration:
class CMFCRibbonButtonEx : public CMFCRibbonButton
{
// Construction
public:
CMFCRibbonButtonEx();
CMFCRibbonButtonEx(UINT nID, LPCTSTR lpszText, int nSmallImageIndex = -1, int nLargeImageIndex = -1, BOOL bAlwaysShowDescription = FALSE);
CMFCRibbonButtonEx(UINT nID, LPCTSTR lpszText, HICON hIcon, BOOL bAlwaysShowDescription = FALSE, HICON hIconSmall = NULL, BOOL bAutoDestroyIcon = FALSE, BOOL bAlphaBlendIcon = FALSE);
// Overridden
void SetCheck(BOOL bCheck = TRUE);
void DrawImage(CDC* pDC, RibbonImageType type, CRect rectImage);
// Attributes
private:
BOOL m_bChecked;
// Helper
private:
void DrawCheckmark(CDC* pDC, int CheckmarkResourceBitmapID, RECT *r);
void PremultiplyBitmapAlpha(HDC hDC, HBITMAP hBmp);
};
Here are the class function definitions:
CMFCRibbonButtonEx::CMFCRibbonButtonEx() : CMFCRibbonButton() { }
CMFCRibbonButtonEx::CMFCRibbonButtonEx(UINT nID, LPCTSTR lpszText, int nSmallImageIndex, int nLargeImageIndex, BOOL bAlwaysShowDescription)
: CMFCRibbonButton(nID, lpszText, nSmallImageIndex, nLargeImageIndex, bAlwaysShowDescription) { }
CMFCRibbonButtonEx::CMFCRibbonButtonEx(UINT nID, LPCTSTR lpszText, HICON hIcon, BOOL bAlwaysShowDescription, HICON hIconSmall, BOOL bAutoDestroyIcon, BOOL bAlphaBlendIcon)
: CMFCRibbonButton(nID, lpszText, hIcon, bAlwaysShowDescription , hIconSmall, bAutoDestroyIcon, bAlphaBlendIcon) { }
void CMFCRibbonButtonEx::SetCheck(BOOL bCheck)
{
m_bChecked = bCheck;
}
void CMFCRibbonButtonEx::DrawImage(CDC* pDC, RibbonImageType type, CRect rectImage)
{
CMFCRibbonButton::DrawImage(pDC, type, rectImage);
if (type == RibbonImageLarge && m_bChecked)
DrawCheckmark(pDC, IDB_BIG_ICON_CHECKMARK, &rectImage);
}
void CMFCRibbonButtonEx::DrawCheckmark(CDC* pDC, int CheckmarkResourceBitmapID, RECT *r)
{
HDC hdc;
CDC *dc;
CDC dcMem;
CBitmap cbm;
VERIFY(hdc = pDC->m_hDC);
VERIFY(dc = pDC);
dcMem.CreateCompatibleDC(dc);
cbm.LoadBitmap(CheckmarkResourceBitmapID);
PremultiplyBitmapAlpha(dcMem.m_hDC, cbm);
SelectObject(dcMem.m_hDC, cbm.m_hObject);
BLENDFUNCTION bf;
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.SourceConstantAlpha = 255;
bf.AlphaFormat = AC_SRC_ALPHA;
::AlphaBlend(hdc, r->left, r->top, r->right-r->left, r->bottom-r->top, dcMem, 0, 0, 32, 32, bf);
VERIFY(dcMem.DeleteDC());
}
void CMFCRibbonButtonEx::PremultiplyBitmapAlpha(HDC hDC, HBITMAP hBmp)
{
BITMAP bm = { 0 };
GetObject(hBmp, sizeof(bm), &bm);
BITMAPINFO* bmi = (BITMAPINFO*) _alloca(sizeof(BITMAPINFOHEADER) + (256 * sizeof(RGBQUAD)));
::ZeroMemory(bmi, sizeof(BITMAPINFOHEADER) + (256 * sizeof(RGBQUAD)));
bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
BOOL bRes = ::GetDIBits(hDC, hBmp, 0, bm.bmHeight, NULL, bmi, DIB_RGB_COLORS);
if( !bRes || bmi->bmiHeader.biBitCount != 32 ) return;
LPBYTE pBitData = (LPBYTE) ::LocalAlloc(LPTR, bm.bmWidth * bm.bmHeight * sizeof(DWORD));
if( pBitData == NULL ) return;
LPBYTE pData = pBitData;
::GetDIBits(hDC, hBmp, 0, bm.bmHeight, pData, bmi, DIB_RGB_COLORS);
for( int y = 0; y < bm.bmHeight; y++ ) {
for( int x = 0; x < bm.bmWidth; x++ ) {
pData[0] = (BYTE)((DWORD)pData[0] * pData[3] / 255);
pData[1] = (BYTE)((DWORD)pData[1] * pData[3] / 255);
pData[2] = (BYTE)((DWORD)pData[2] * pData[3] / 255);
pData += 4;
}
}
::SetDIBits(hDC, hBmp, 0, bm.bmHeight, pBitData, bmi, DIB_RGB_COLORS);
::LocalFree(pBitData);
}
IDB_BIG_ICON_CHECKMARK is a 32bit bmp with alpha channel: Download here
Sadly, in OnCmdMsg only a fake CMFCRibbonButton created by the ribbon framework is being maintained, so SetCheck() doesn't have any effect on an IsChecked() call in DrawImage(). You just have to find the real CMFCRibbonButtonEx in the subitems of the menu like this:
BOOL CEasyCashView::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
...
else if (nID >= ID_MY_ITEMS_BASE && nID < ID_ITEMS_BASE+MAX_MY_ITEMS)
{
if (nCode == CN_COMMAND)
{
OnMyItemsCommand(nID);
}
else if (nCode == CN_UPDATE_COMMAND_UI)
{
((CCmdUI*)pExtra)->SetCheck(myItemsCheckedArray[nID-ID_MY_ITEMS_BASE] == TRUE);
// this won't have any effect, use code below
CMFCRibbonButton* pMyMainMenuButton;
if (pMyMainMenuButton = ((CMainFrame*)AfxGetMainWnd())->m_pMyMainMenuButton)
{
int i;
for (i = 0; i < pMyMainMenuButton->GetSubItems().GetCount(); i++)
if (pMyMainMenuButton->GetSubItems()[i]->GetID() == nID)
{
((CMFCRibbonButtonEx*)pMyMainMenuButton->GetSubItems()[i])->SetCheck(myItemsCheckedArray[nID-ID_MY_ITEMS_BASE] == TRUE);
break;
}
}
return TRUE;
}
}
Should anyone know how to connect the fake CMFCRibbonButton (or CMFCRibbonBaseElement) maintained by CCmdUI to the originally created CMFCRibbonButtonEx, please leave me a comment.
"checkboxes in front of each menu item"
Is the below picture what you're looking for? If yes, this can be achieved by editing the ribbon xml file.
I need help with rendering CRichEditCtrl content with transparent background on graphical context which is displayed on screen and printed as well.
Now I have following code, which is working good except transparency issues:
CRichEditCtrl ctrl; // my CRichEditCtrl
CDC *dc; // - my graphical context
dc->SetBkMode(TRANSPARENT);
dc->DPtoHIMETRIC(&targetSize);
CRect cHiMetricRect( 0, 0, origSize.cx*factor,origSize.cy*factor);
CRect cTwipsRect( 0, 0, (TWIPS_INCH * targetSize.cx + HIMETRIC_INCH / 2) / HIMETRIC_INCH, (TWIPS_INCH * targetSize.cy + HIMETRIC_INCH / 2) / HIMETRIC_INCH);
CMetaFileDC metaFile;
metaFile.CreateEnhanced( dc, NULL, cHiMetricRect, NULL );
metaFile.SetBkMode(TRANSPARENT);
metaFile.SetAttribDC( dc->m_hDC );
FORMATRANGE stFR;
stFR.hdcTarget = stFR.hdc = metaFile.m_hDC;
stFR.rcPage = stFR.rc = cTwipsRect;
stFR.chrg.cpMin = 0;
stFR.chrg.cpMax = -1;
ctrl.FormatRange( &stFR, TRUE);
ctrl.FormatRange( NULL, TRUE);
HENHMETAFILE hMetaFile = metaFile.CloseEnhanced();
dc->PlayMetaFile(hMetaFile,&cr);
DeleteEnhMetaFile(hMetaFile);
I need to render this text with transparency because there are already things drawn on my DC.
I tried to search web for any help about metafiles and transparency but found nothing adequate. I will be thankful for any kind of help.
I'm not very sure about MetaFiles, but I've done something similar with EMFs with straight WinAPI which does work -- call EnumEnhMetaFile instead of PlayMetaFile, like:
BOOL bFirstTime = TRUE;
EnumEnhMetaFile(hDC, m_hEmf, (ENHMFENUMPROC)EmfEnumProc_Play_TranspBackground, &bFirstTime, &rc);
where the EnumProc is defined as
int CALLBACK EmfEnumProc_Play_TranspBackground(HDC hDC, LPHANDLETABLE lpHTable, LPENHMETARECORD lpEMFR, int nObj, LPARAM lpData)
{ BOOL bOK;
if (lpEMFR->iType == EMR_SETBKMODE)
{ EMRSETBKMODE* lpEMRBkMode = (EMRSETBKMODE*) lpEMFR;
if (lpEMRBkMode->iMode == OPAQUE)
{ EMRSETBKMODE EmrBkMode;
EmrBkMode.emr.iType = EMR_SETBKMODE;
EmrBkMode.emr.nSize = (sizeof(EmrBkMode) % 4 == 0 ? sizeof(EmrBkMode) : (((sizeof(EmrBkMode) / 4) + 1) * 4));
EmrBkMode.iMode = TRANSPARENT;
bOK = PlayEnhMetaFileRecord(hDC, lpHTable, (LPENHMETARECORD)&EmrBkMode, (UINT)nObj);
return bOK;
}
}
bOK = PlayEnhMetaFileRecord(hDC, lpHTable, lpEMFR, (UINT)nObj);
if (lpEMFR->iType == EMR_HEADER)
{ BOOL* pbFirstTime = (BOOL*)lpData;
if (*pbFirstTime)
{ EMRSETBKMODE EmrBkMode;
EmrBkMode.emr.iType = EMR_SETBKMODE;
EmrBkMode.emr.nSize = (sizeof(EmrBkMode) % 4 == 0 ? sizeof(EmrBkMode) : (((sizeof(EmrBkMode) / 4) + 1) * 4));
EmrBkMode.iMode = TRANSPARENT;
PlayEnhMetaFileRecord(hDC, lpHTable, (LPENHMETARECORD)&EmrBkMode, (UINT)nObj);
*pbFirstTime = FALSE;
}
}
return bOK;
}
I used EnumEnhMetaFile function:
EnumEnhMetaFile(dc->m_hDC, hMetaFile, myMetafileProc, NULL, rect );
Then I used callback procedure that retrives each record of metafile:
int metafileProc( HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR, int nObj, LPARAM lpData) {
if ( lpEMFR->iType == EMR_EXTTEXTOUTW ) {
COLORREF_STRUCT *c = (COLORREF_STRUCT *)&(lastBgColor);
EMR_EXTTEXTOUTW_STRUCT *s = (EMR_EXTTEXTOUTW_STRUCT *)lpEMFR;
s->textStruct.options = 0x00;
if ( bgColorFlag ) {
bgColorFlag = false;
renderRect( hDC, s->textStruct.rect, c );
}
PlayEnhMetaFileRecord( hDC, lpHTable, lpEMFR, nObj );
} else if ( lpEMFR->iType == EMR_SETBKCOLOR ) {
BYTE *ptr = (BYTE*)lpEMFR;
COLORREF temp = *((COLORREF*)(ptr+8));
if ( temp != 0xFFFFFF ) {
lastBgColor = temp;
bgColorFlag = true;
}
}
return 1;
}
where renderRect( hDC, s->textStruct.rect, c ) is function which draws transparent background in c color. I have to zero options flag for text becouse there was different behaviour in Windows XP and Windows 7 and 8 - after that everything works ok. Thanks for help. Best regards.