How to block resizing after a double-click on a dialog window's top or bottom edges in Windows 10? - c++

I'm coding a customized popup window with C++ using Win32. The condition for this popup window is that it can be only resized from the bottom down. The following is the implementation of such restriction:
RECT rcInitialWindowRectangle = {0};
//The dialog has WS_THICKFRAME style
LRESULT CALLBACK DlgWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
case WM_INITDIALOG:
{
//Set minimum window size
::GetWindowRect(hDlg, &rcInitialWindowRectangle);
}
break;
case WM_SIZING:
{
//Restrict sizing on all sides but bottom
if(wParam != WMSZ_BOTTOM)
{
RECT* pRcWnd = (RECT*)lParam;
//Preserve all sides but bottom
int b = pRcWnd->bottom;
*pRcWnd = rcInitialWindowRectangle;
pRcWnd->bottom = b;
return TRUE;
}
}
break;
case WM_GETMINMAXINFO:
{
//The following is needed to restrict minimum window size
int w = rcInitialWindowRectangle.right - rcInitialWindowRectangle.left;
if(w != 0)
{
MINMAXINFO* pMMI = (MINMAXINFO*)lParam;
pMMI->ptMinTrackSize.x = w;
pMMI->ptMinTrackSize.y = rcInitialWindowRectangle.bottom - rcInitialWindowRectangle.top;
pMMI->ptMaxTrackSize.x = w;
}
}
break;
case WM_NCHITTEST:
{
//The following is needed to display correct cursor for resizing
POINT pnt = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
RECT rcWnd;
::GetWindowRect(hDlg, &rcWnd);
//L, T, R, B
RECT rcBtm = {rcInitialWindowRectangle.left,
rcWnd.bottom - 16, //Some arbitrary border
rcInitialWindowRectangle.right,
rcWnd.bottom};
return ::PtInRect(&rcBtm, pnt) ? HTNOWHERE : HTBORDER;
}
break;
return 0;
}
So this works except one thing. On Windows 10, there's evidently a new feature -- when someone double-clicks on the bottom (or top) edge of a window -- here's an example with Notepad so that you can try:
that window is resized (stretched) to the top and bottom of the screen (akin to maximization, but only vertically.)
So my question is how do I block this double-click resizing? (In my case the top of the popup window should not move.)
PS. My first instinct was to block all double-clicks on the window's edge, but then I thought that maybe there's a less barbaric way to achieve this?

You are already handling WM_NCHITTEST. Handle WM_NCLBUTTONDBLCLK and don't forward to DefWindowProc unless the hit test (in wParam) indicates the lower frame.

here's the workaround that seems to work for me. It is not really about blocking the double-clicks. (Apart from subclassing the WndProc of a dialog box, which I haven't tried, I failed to block that double-click effect in DlgProc alone.) My workaround presented below is to achor the top of the popup window and let it drop down to the bottom of the screen, if the user who double-clicks the bottom border wants similar stuff to happen.
Since this does not answer my original question, I won't mark it as such.
//Add this case statement to my original code
case WM_WINDOWPOSCHANGING:
{
WINDOWPOS* pWP = (WINDOWPOS*)lParam;
if(!(pWP->flags & (SWP_NOMOVE | SWP_NOSIZE)))
{
int w = rcInitialWindowRectangle.right - rcInitialWindowRectangle.left;
if(w > 0)
{
//Anchor the top of the popup window
pWP->x = rcInitialWindowRectangle.left;
pWP->y = rcInitialWindowRectangle.top;
pWP->cx = w;
int h = pWP->cy;
//Make sure that the height fits the screen
POINT pnt = {pWP->x, pWP->y};
MONITORINFO mi = {0};
mi.cbSize = sizeof(mi);
if(::GetMonitorInfo(::MonitorFromPoint(pnt, MONITOR_DEFAULTTONEAREST), &mi))
{
if(pWP->y + h > mi.rcWork.bottom)
{
int nMinDefaultH = rcInitialWindowRectangle.bottom -
rcInitialWindowRectangle.top;
int nAh = mi.rcWork.bottom - y;
if(nAh >= nMinDefaultH)
h = nAh;
else
h = nMinDefaultH;
}
}
pWP->cy = h;
}
}
}
break;

Related

CUSTOMDRAW in Win32 Header not appropriately drawing outside columns area

I am currently subclassing a ListView control to support custom color schemes - especially Dark Mode themes.
So first, I changed the ListView and associated Header with:
SetWindowTheme(hwndListView, isDarkModeEnabled() ? L"Explorer" : nullptr, nullptr);
SetWindowTheme(hwndListViewHeader, isDarkModeEnabled() ? L"ItemsView" : nullptr, nullptr);
I can set the ListView background color to any custom color I choose, while answering to the WM_THEMECHANGED message and doing a ListView_SetBkColor() macro.
Unfortunately, it seems I can't do it with the associated Header Control, I must resort to Custom Draw. So I did. I subclassed the ListView, because this is the window handle that will receive WM_NOTIFY messages from the Header control, and when responding to NM_CUSTOMDRAW message, my code looks like this:
case WM_NOTIFY:
{
LPNMHDR nmhdr = reinterpret_cast<LPNMHDR>(lParam);
if (nmhdr->code == NM_CUSTOMDRAW)
{
LPNMCUSTOMDRAW lpcd = reinterpret_cast<LPNMCUSTOMDRAW>(lParam);
switch (lpcd->dwDrawStage)
{
case CDDS_PREPAINT:
{
if (!PluginDarkMode::isEnabled())
return CDRF_DODEFAULT;
FillRect(lpcd->hdc, &lpcd->rc, getDarkerBackgroundBrush());
return CDRF_NOTIFYITEMDRAW | CDRF_NOTIFYPOSTPAINT;
}
case CDDS_ITEMPREPAINT:
{
return DrawListViewHeaderItem(lParam); // This function draws the item entirely and then returns CDRF_SKIPDEFAULT.
}
case CDDS_POSTPAINT:
// Calculates the undrawn border outside columns
HWND hHeader = lpcd->hdr.hwndFrom;
int count = static_cast<int>(Header_GetItemCount(hHeader));
int colsWidth = 0;
RECT wRc = {};
for (int i = 0; i < count; i++)
{
Header_GetItemRect(hHeader, i, &wRc);
colsWidth += wRc.right - wRc.left;
}
RECT clientRect;
GetClientRect(hHeader, &clientRect);
if (clientRect.right > (colsWidth + 1))
{
clientRect.left = colsWidth + 1;
HDC hdc = GetDC(hHeader);
FillRect(hdc, &clientRect, getDarkerBackgroundBrush());
ReleaseDC(hHeader, hdc);
}
return CDRF_SKIPDEFAULT;
}
}
break;
}
Now, my problem is that, although this seems fine, my control will always end up with a black rectangle outside the column's area (the empty space where no column exists). And this effect persists up until I move the mouse outside the header control area.
For example:
And when the mouse leaves the area:
Any ideas of how to solve this?

Tracking tooltip in CScrollView?

In a standard C++/MFC MDI doc/view project, I want to implement a tracking tooltip in the view (the tabbed view windows which generally occupy most of the main frame window). So, in class MyAppView, I have a member CToolTipCtrl tooltip. Function MyAppView::OnInitialUpdate() contains the initialization
BOOL ok0 = tooltip.Create(this, TTS_ALWAYSTIP);
CRect clientRect; GetClientRect(&clientRect);
BOOL ok2 = tooltip.AddTool(this, LPSTR_TEXTCALLBACK, &clientRect, 1234/*tool ID*/);
tooltip.Activate(TRUE);
to make the entire client area of the view be the "tool". The message map contains an entry
ON_NOTIFY_EX(TTN_NEEDTEXT, 0, OnNeedToolTipText)
and the function OnNeedToolTipText is defined as
BOOL MyAppView::OnNeedToolTipText(UINT id, NMHDR *pNMHDR, LRESULT *pResult)
{
UNREFERENCED_PARAMETER(id);
NMTTDISPINFO *pTTT = (NMTTDISPINFO *)pNMHDR;
UINT_PTR nID = pNMHDR->idFrom;
BOOL bRet = FALSE;
if(nID == 1234)
{
// Come here when text is needed for tracking tooltip
}
if(pTTT->uFlags & TTF_IDISHWND)
{
// idFrom is actually the HWND of the tool
nID = ::GetDlgCtrlID((HWND)nID);
if(nID)
{
_stprintf_s(pTTT->szText, sizeof(pTTT->szText) / sizeof(TCHAR),
_T("Control ID = %d"), nID);
pTTT->hinst = AfxGetResourceHandle();
bRet = TRUE;
}
}
*pResult = 0;
return bRet;
}
What happens is that only placing the mouse on the menu items (File, Edit, View, Window, Help) causes the code to enter OnNeedToolTipText, with an ID of 0-5. Moving the mouse into the client area (the view) does nothing.
How can I get the tooltip to appear in the client area of the view only?
Visual Studio 2017; C++; 64-bit Windows 7
In order to solve the problem you need to do the following:
BOOL CMyAppView::PreTranslateMessage(MSG* pMsg)
{
switch (pMsg->message)
{
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_LBUTTONUP:
case WM_RBUTTONUP:
case WM_MBUTTONUP:
case WM_MOUSEMOVE:
if (m_pToolTip->GetSafeHwnd () != NULL)
{
m_pToolTip->RelayEvent(pMsg);
}
break;
}
return CScrollView::PreTranslateMessage(pMsg);
}
If you want a tracking tooltip in a view, these are the steps to follow:
Create tooltip and add the tool.
void CToolTipDemoView::OnInitialUpdate()
{
// ...
m_toolTip.Create(this, TTS_ALWAYSTIP | TTS_NOANIMATE);
m_toolTip.AddTool(this, _T("Doesn't matter"));
}
Handle WM_MOUSEMOVE message. First, call _TrackMouseEvent in order to further receive WM_MOUSELEAVE and activate the tooltip. Second, update the tooltip text, and show it at mouse pointer coordinates.
void CToolTipDemoView::OnMouseMove(UINT nFlags, CPoint point)
{
if (!m_bTrackingMouseLeave)
{
TRACKMOUSEEVENT tme = { 0 };
tme.cbSize = sizeof(TRACKMOUSEEVENT);
tme.dwFlags = TME_LEAVE;
tme.hwndTrack = m_hWnd;
::_TrackMouseEvent(&tme);
m_toolTip.Activate(TRUE);
m_bTrackingMouseLeave = TRUE;
}
if (m_pointLastMousePos != point)
{
CString strText;
strText.Format(_T("x = %d y = %d"), point.x, point.y);
m_toolTip.UpdateTipText(strText, this);
m_toolTip.Popup();
m_pointLastMousePos = point;
}
CScrollView::OnMouseMove(nFlags, point);
}
Handle WM_MOUSELEAVE and deactivate the tooltip.
void CCToolTipDemoView::OnMouseLeave()
{
m_bTrackingMouseLeave = FALSE;
// mouse pointer leaves the window so deactivate the tooltip
m_toolTip.Activate(FALSE);
CScrollView::OnMouseLeave();
}
Notes:
there is no more necessary to handle TTN_NEEDTEXT.
also, there is no more necessary to override PreTranslateMessage
So I went back to see what I could be missing. I wrote this stuff over 10 years ago. I had also overridden a CWnd member
virtual INT_PTR OnToolHitTest( CPoint point, TOOLINFO* pTI ) const;
With:
INT_PTR HERichView::OnToolHitTest( CPoint point, TOOLINFO* pTI ) const
{
pTI->hwnd = m_hWnd;
pTI->uId = point.x + ( point.y << 16 );
CRect rect;
GetClientRect( rect );
pTI->rect= rect;
pTI->lpszText= LPSTR_TEXTCALLBACK;
return pTI->uId;
}
And I checked, it won't work without this. So your:
ON_NOTIFY_EX( TTN_NEEDTEXT, 0, OnToolTip )
Should get called if you add the above. And only EnableToolTips( ); Should be needed.
I have not succeeded in getting the tracking tooltip to work within MFC. The closest I have come is
In message map: ON_NOTIFY_EX(TTN_NEEDTEXT, 0, OnNeedToolTipText)
In OnInitialUpdate: BOOL ok1 = EnableTrackingToolTips(TRUE);
In override of virtual function OnToolHitTest:
pTI->hwnd = m_hWnd;
pTI->uId = (UINT_PTR)m_hWnd;
pTI->uFlags = TTF_IDISHWND | TTF_ALWAYSTIP | TTF_TRACK | TTF_NOTBUTTON | TTF_ABSOLUTE | TTF_SUBCLASS;
pTI->lpszText = LPSTR_TEXTCALLBACK;
return pTI->uId;
In OnNeedToolTipText:
NMTTDISPINFO *pTTT = (NMTTDISPINFO *)pNMHDR;
UINT_PTR nID = pNMHDR->idFrom;
BOOL bRet = FALSE;
if(pTTT->uFlags & TTF_IDISHWND)
{
// idFrom is actually the HWND of the tool
nID = ::GetDlgCtrlID((HWND)nID);
if(nID)
{
CURSORINFO ci; ci.cbSize = sizeof(CURSORINFO); // get something interesting to display
GetCursorInfo(&ci);
_stprintf_s(pTTT->szText, sizeof(pTTT->szText) / sizeof(TCHAR),
_T("Control ID = %lld at (%d, %d)"), nID, ci.ptScreenPos.x, ci.ptScreenPos.y);
pTTT->hinst = AfxGetResourceHandle();
bRet = TRUE;
}
}
*pResult = 0;
return bRet;
This produces the following peculiar behavior. When I start the app and move the mouse cursor into the client area of the CScrollView, a tooltip appears right next to the cursor.
If I move the mouse carefully (smoothly) the tooltip tracks properly. After a while, though, it disappears, and no further mouse motions, including leaving the CScrollView window and returning, make it re-appear.
I think what is happening is that when the mouse cursor moves over the tooltip window, the tooltip is turned off, permanently. This disappearance does not seem to be time-related (e g, due to auto-pop); if the mouse is left untouched, the tooltip remains indefinitely.

What is the correct way to use CFontDialog common dialog?

I recently change the relationship between the window extent and the view port of an MFC application, and, since then, every time I change my application font size, the selected font become supper gigantic even if I chose the smallest of all font size.
(EDIT: I noticed that CFontDialog::GetSize() return a size that is ten times the size chosen in the dialog. Is that a usual behaviour ? If not what could make the dialogue return such a value? Although I am not sure, but it seem this multiplication of size seem to be my problem. How do I get CFontDialog::GetSize() to return the actual selected size, if that indeed is the problem?)
What am I doing wrong? What is the correct way to use CFontDialog ?
Shown next is a snippet of the font changing code:
CClientDC dc(pView);
pView->OnPrepareDC(&dc)
pLastFont = pLastText->GetFont();
oldColor = pLastText->GetColor();
LOGFONT logFont = (LOGFONT) (*pLastFont);
CFontDialog fontDialog(&logFont);
CSize szPrevSize;
//Some missing codes
MyFigure *pMyFigure;
if(dynamic_cast<MyTextFigure*>(pMyFigure) != NULL)
{
if(dynamic_cast<MyTextBoxFigure*>(pLastText) != NULL)
{
MyTextBoxFigure *pTextBox = dynamic_cast<MyTextBoxFigure*>(pLastText);
szPrevSize = pTextBox->GetTextSize();
}
}
else if(dynamic_cast<MyTableFigure*>(pMyFigure) != NULL)
{
MyTableFigure *pTableFigure = dynamic_cast<MyTableFigure*>(pMyFigure);
TCell *pCell = (TCell *)*pTableFigure;
szPrevSize = pCell->GetTextSize();
}
fontDialog.m_cf.rgbColors = (COLORREF) oldColor;
if (fontDialog.DoModal() == IDOK)
{
fontDialog.GetCurrentFont(&logFont);
MyFont newFont = (MyFont) logFont;
MyColor newColor = (MyColor) fontDialog.GetColor();
pMyFigure->SetFont(newFont, &dc);
pMyFigure->SetColor(newColor);
}
Please note that MyFont is a wrapper around the LOGFONT structure and has an operator that enable cast to LOGFONT. Also note that MyFigure class has a MyFont data member that is set by the member function SetFont.
The following code shows how the relationship between the view port and the window extent was set.
void CDisplayView::OnInitialUpdate()
{
CRect rcClient;
GetClientRect(&rcClient);
CClientDC dc(this);
dc.SetMapMode(MM_ISOTROPIC);
CSize szWindow(m_pAppDoc->GetZoomRatio() * SCALE_RATIO * dc.GetDeviceCaps(HORZRES),m_pAppDoc->GetZoomRatio() * SCALE_RATIO * dc.GetDeviceCaps(VERTRES));
CSize szViewport(dc.GetDeviceCaps(HORZRES),dc.GetDeviceCaps(VERTRES));
dc.SetWindowExt(szWindow);
dc.SetViewportExt(szViewport);
dc.DPtoLP(&rcClient);
//And so on
}
ZoomRatio is 1, an SCALE_RATIO is 1.2
The assignment operator is:
MyFont& MyFont::operator=(const MyFont& font)
{
if (this != &font)
{
m_logFont = font.m_logFont;
}
return *this;
}
P.S.
The summary of the code is:
LOGFONT OldlogFont ;
LOGFONT newLogFont;
COLLORREF OldColorref;
COLORREF newColorref;
CFontDialog fontDialog(&OldlogFont);
fontDialog.m_cf.rgbColors = oldColorref;
if (fontDialog.DoModal() == IDOK)
{
fontDialog.GetCurrentFont(&OldlogFont);
newLogFont = OldlogFont;
newColorref = fontDialog.GetColor();
}
The focus here is the LOGFONT structure.
The user selected value is gotten with old logfont then assigned to new logfont.
For debugging purposes, I retrieved the size of the selected font and was shocked to find that it is ten times the size that I selected after lunching the CFontDialog for use in my application i.e
LOGFONT OldlogFont ;
LOGFONT newLogFont;
COLLORREF OldColorref;
COLORREF newColorref;
CFontDialog fontDialog(&OldlogFont);
fontDialog.m_cf.rgbColors = oldColorref;
if (fontDialog.DoModal() == IDOK)
{
fontDialog.GetCurrentFont(&OldlogFont);
newLogFont = OldlogFont;
newColorref = fontDialog.GetColor();
int iFontSize = fontDialog.GetSize();
//when I selected a font size of 10 from the dialog, what I get here in my code is 100.
}
This is how I do it:
void CExportSettingsDlg::OnBtnSelectFont()
{
CClientDC dc(this);
m_plfCurrentFont->lfHeight =
-MulDiv(m_plfCurrentFont->lfHeight, dc.GetDeviceCaps(LOGPIXELSY), 72);
CMyFontDialog dlgFont( m_plfCurrentFont );
dlgFont.m_cf.Flags |= CF_NOSCRIPTSEL;
dlgFont.m_cf.rgbColors = m_crCurrentFontColour;
if( dlgFont.DoModal() == IDOK)
{
m_plfCurrentFont->lfHeight = -(dlgFont.GetSize()/10);
// We must retrieve the font colour into the correct variable.
// The logfont is already correct because we passed in the
// logfont pointer into the font dialogue.
switch( m_iFontType )
{
case FONT_TITLE:
m_sSettings.crTitleFontColour = dlgFont.GetColor();
break;
case FONT_HEADING:
m_sSettings.crHeadingFontColour = dlgFont.GetColor();
break;
case FONT_GENERAL:
m_sSettings.crGeneralFontColour = dlgFont.GetColor();
break;
case FONT_EVENT:
m_sSettings.crEventFontColour = dlgFont.GetColor();
break;
case FONT_NOTES:
m_sSettings.crNotesFontColour = dlgFont.GetColor();
break;
}
// Update display
SetSampleFont();
// Html Preview
UpdatePreviewHtml();
}
}
If it does not answer your question I will remove the answer. Notice how I set the font size at the start of the code.
If you read up on the MSDN for GetSize it states:
The font's size, in tenths of a point

CMFCListCtrl force selected item to have red color

I have my own CMFCListCtrl derived class where I implemented the
virtual COLORREF OnGetCellTextColor(int nRow, int nColum)
{
CMyClass* pMyClass = (CMyClass*)GetItemData(nRow);
if (pMyClass && pMyClass->m_bDeleted)
return RGB(255, 0, 0);
return __super::OnGetCellTextColor(nRow, nColum);
}
function for marking red the deleted entries. This works except when a item is selected.
I went to the void CMFCListCtrl::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult) function and placed a breakpoint with the condition iRow==selected item on the line
lplvcd->clrText = OnGetCellTextColor(iRow, iColumn);
executed it, then I created a new Data Breakpoint for &lplvcd->clrText.
The Data Breakpoint got hit on function
comctl32.dll!SHThemeComputeTextColors()
, as the callstack image shows:
, which is clearly overriding the variable value.
As I search for SHThemeComputeTextColors on Internet and nothing appears, can somebody help me on forcing the selected items text to be red?
Changing the colors and fonts of highlighted items is trickier than it seems, because highlighting selected items is a system-wide issue. In fact, it was something I had delayed for a long time, and just today I've finally tackled...
There are at least two ways to change the looks of a list control: owner draw (you have to do all the drawing yourself) and custom draw (the system tells you when it is about to do some steps of the drawing and lets you change the color, the font, etc.). This answer is about custom draw. This article covers the basics of using Custom Draw with CListCtrl.
How to change the highlight color in a CListCtrl (not a CMFCListCtrl, we'll get to that soon) is explained in this other article. These are the steps you have to do:
Intercept the listview draw routine just before it is about to draw a highlighted row (item).
Turn off the row highlight.
Set the row colors to whatever you want.
Let the listview draw the row.
Intercept the listview draw routine after it has drawn the row (post-draw item).
Turn this row's highlighting back on.
So, when the listview is about to draw a highlighted row, you have to telll the system the row is not highlighted so it doesn't use the system colors to paint it, tell what color to use, and set the item as highlighted again, so the selection works as usual.
Here is the code from that article:
COLORREF g_MyClrFgHi; // My foreground hilite color
COLORREF g_MyClrBgHi; // My background hilite color
HWND g_hListView; // Window handle of listview control
void EnableHighlighting(HWND hWnd, int row, bool bHighlight)
{
ListView_SetItemState(hWnd, row, bHighlight? 0xff: 0, LVIS_SELECTED);
}
bool IsRowSelected(HWND hWnd, int row)
{
return ListView_GetItemState(hWnd, row, LVIS_SELECTED) != 0;
}
bool IsRowHighlighted(HWND hWnd, int row)
{
// We check if row is selected.
// We also check if window has focus. This was because the original listview
// control I created did not have style LVS_SHOWSELALWAYS. So if the listview
// does not have focus, then there is no highlighting.
return IsRowSelected(hWnd, row) && (::GetFocus(hWnd) == hWnd);
}
BOOL OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
static bool bIsHighlighted = false;
*pResult = 0;
NMHDR *p = (NMHDR *)lParam;
switch (p->code)
{
...
case NM_CUSTOMDRAW:
NMLVCUSTOMDRAW *lvcd = (NMLVCUSTOMDRAW *)p;
NMCUSTOMDRAW &nmcd = lvcd->nmcd;
switch (nmcd.dwDrawStage)
{
case CDDS_PREPAINT:
// We want item prepaint notifications, so...
*pResult = CDRF_NOTIFYITEMDRAW;
break;
case CDDS_ITEMPREPAINT:
{
int iRow = (int)nmcd.dwItemSpec;
bHighlighted = IsRowHighlighted(g_hListView, iRow);
if (bHighlighted)
{
lvcd->clrText = g_MyClrFgHi; // Use my foreground hilite color
lvcd->clrTextBk = g_MyClrBgHi; // Use my background hilite color
// Turn off listview highlight otherwise it uses the system colors!
EnableHighlighting(g_hListView, iRow, false);
}
// We want item post-paint notifications, so...
*pResult = CDRF_DODEFAULT | CDRF_NOTIFYPOSTPAINT;
break;
}
case CDDS_ITEMPOSTPAINT:
{
if (bHighlighted)
{
int iRow = (int)nmcd.dwItemSpec;
// Turn listview control's highlighting back on now that we have
// drawn the row in the colors we want.
EnableHighlighting(g_hListView, iRow, true);
}
*pResult = CDRF_DODEFAULT;
break;
}
default:
*pResult = CDRF_DODEFAULT;
break;
}
break;
...
}
}
This works fine with a CListCtrl, but you are asking about a CMFCListCtrl. The problem is that CMFCListCtrl is already asking to be notified about NM_CUSTOMDRAW notifications (that's when it calls the OnGetCellTextColor function, as you've seen). If you create a handler for that in your own CMFCListCtrl-derived class, those notifications won't get to CMFCListCtrl and you lose that functionality (it may fine, depending on your needs).
So here is what I've done: I've created a NM_CUSTOMDRAW handler in my list control and, if I'm dealing with a highlighted row, I change the colors, otherwise, I call CMFCListCtrl::OnNMCustomDraw(). As you'll see, I just use the normal highlight colors; that's because I just wanted to see the selected items even when the control doesn't have the focus:
void CMyListCtrl::OnNMCustomdraw(NMHDR* pNMHDR, LRESULT* pResult)
{
bool callParent = true;
static bool bHighlighted = false;
LPNMLVCUSTOMDRAW lpLVCustomDraw = reinterpret_cast<LPNMLVCUSTOMDRAW>(pNMHDR);
NMCUSTOMDRAW nmcd = lpLVCustomDraw->nmcd;
*pResult = CDRF_DODEFAULT;
switch (lpLVCustomDraw->nmcd.dwDrawStage)
{
case CDDS_PREPAINT:
*pResult = CDRF_NOTIFYITEMDRAW;
break;
case CDDS_ITEMPREPAINT:
{
int row = nmcd.dwItemSpec;
bHighlighted = IsRowHighlighted(row);
if (bHighlighted)
{
lpLVCustomDraw->clrText = GetSysColor(COLOR_HIGHLIGHTTEXT);
lpLVCustomDraw->clrTextBk = GetSysColor(COLOR_HIGHLIGHT);
EnableHighlighting(row, false);
*pResult = CDRF_DODEFAULT | CDRF_NOTIFYPOSTPAINT;
callParent = false;
}
}
break;
case CDDS_ITEMPOSTPAINT:
if (bHighlighted)
{
int row = nmcd.dwItemSpec;
EnableHighlighting(row, true);
callParent = false;
}
*pResult = CDRF_DODEFAULT;
break;
default:
break;
}
if (callParent)
{
__super ::OnCustomDraw(pNMHDR, pResult);
}
}
bool CMyListCtrl::IsRowHighlighted(int row)
{
bool selected = GetItemState(row, LVIS_SELECTED) != 0;
return selected;
}
void CMyListCtrl::EnableHighlighting(int row, bool enable)
{
SetItemState(row, enable ? 0xff : 0, LVIS_SELECTED);
}
I haven't tested it thoroughly, but it seems to work.
UPDATE:
There is a little problem. When you call EnableHigilighting(), the dialog will get LVN_ITEMCHANGED notifications, which can trigger a redraw, which will trigger a LVN_ITEMCHANGED notification... So if you are listening to LVN_ITEMCHANGED notifications, you might need to do something like this:
void CMyListCtrl::EnableHighlighting(int row, bool enable)
{
m_internalStateChange = true;
SetItemState(row, enable ? 0xff : 0, LVIS_SELECTED);
m_internalStateChange = false;
}
void CWhateverDialog::OnLvnItemchangedListaEjesPane(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
if (!c_List.InternalStateChange() && /* other conditions */)
{
// Respond to state changes
}
}

C++ - Change vertical pitch of your mouse

The Idea
I was wondering if it's possible to change the pitch of your mouse over the entire screen. To give an example: If you draw a perfect circle with your physical mouse on your table, your screen would draw an oval (with: width > height).
This could be useful if you e.g. used to have a customizable mouse that supported individual m_pitch settings but recently broke down and you can't afford a new one. You could use this program to virtualize those individual settings.
So I went to work and concocted this small program:
#include <windows.h>
#include <iostream>
#include <math.h>
using namespace std;
HHOOK hMouseHook;
bool prev_initialized = false;
bool real_input = true;
MOUSEHOOKSTRUCT prevMouseStruct;
double ratio = 0.5; //cursor should pitch 0.5x as fast as it normally would
double rest = 0;
LRESULT CALLBACK mouseProc (int nCode, WPARAM wParam, LPARAM lParam)
{
MOUSEHOOKSTRUCT * pMouseStruct = (MOUSEHOOKSTRUCT *)lParam;
if (pMouseStruct != NULL){
if(wParam == WM_MOUSEMOVE)
{
printf("Mouse position X = %d Mouse Position Y = %d\n", pMouseStruct->pt.x,pMouseStruct->pt.y);
if(prev_initialized && real_input){
int y_diff = prevMouseStruct.pt.y - pMouseStruct->pt.y;
if(y_diff != 0){
double ratiod_diff = ((double)y_diff) * ratio;
int rounded_diff = (int)round(ratiod_diff);
int newY = prevMouseStruct.pt.y - rounded_diff;
rest += ratiod_diff - rounded_diff;
if(rest > 1){
--rest;
++newY;
}else if(rest < -1){
++rest;
--newY;
}
real_input = false;
pMouseStruct->pt.y = newY;
SetCursorPos(pMouseStruct->pt.x, pMouseStruct->pt.y);
printf("New- Mouse position X = %d Mouse Position Y = %d\n", pMouseStruct->pt.x,newY);
}
}else if(!real_input){
real_input = true;
} else{
prevMouseStruct = *pMouseStruct;
prev_initialized = true;
}
}
}
return CallNextHookEx(hMouseHook, nCode, wParam, lParam);
}
int main()
{
HINSTANCE hInstance = GetModuleHandle(NULL);
hMouseHook = SetWindowsHookEx(WH_MOUSE_LL, mouseProc, hInstance, NULL);
MSG message;
while (GetMessage(&message,NULL,0,0)) {
TranslateMessage( &message );
DispatchMessage( &message );
}
UnhookWindowsHookEx(hMouseHook);
return 0;
}
How?
This program registers all mousemovement-events on the screen and remembers 2 coordinates:
The previous mouse location; e.g.: POINT(100, 100)
The current mouse location; e.g.: POINT(120,150) -> the mouse moved 20px to the right and 50px down
Then, I calculate where I want the cursor to be with the ratio (and some other fancy precision stuff). E.g.: the cursor moves 50px down and the ratio is 0.5 -> the mouse only goes 25px down.
Lastly, I call SetMousePos() with the new y-value to move the cursor to my desired location. (Note: I also try to make sure that this function does not call mouseProc() again by using the boolean real_input as a flag).
The problem
I am running into the following problem: the cursor is not staying at its new y-position. After calling the setMousePos()-function, the mouse shifts to its new position for a split second and then instantly reverts back to its former position. This leaves a makeshift 'afterimage' of the mouse. In this GIF, I move the cursor from the top to the bottom. You can see that it leaves it's after-image that grows larger in distance when you move further away from your starting-position (the starting-position is the position of your mouse when the console opens).
Any feedback would be MUCH appreciated!