Getting the width of Win32 TreeView control - winapi

The Win32 TreeView control does not have a built-in message/macro to get its (scrollable) width, e.g. if want to set the TreeView's width so it won't need to have a scrollbar.
How can this be done?

Here's a C function to do this:
int TreeView_GetWidth(HWND hTreeWnd)
{
SCROLLINFO scrollInfo;
SCROLLBARINFO scrollBarInfo;
scrollInfo.cbSize = sizeof(scrollInfo);
scrollInfo.fMask = SIF_RANGE;
scrollBarInfo.cbSize = sizeof(scrollBarInfo);
// To find the whole (scrollable) width of the tree control,
// we determine the range of the scrollbar.
// Unfortunately when a scrollbar isn't needed (and is invisible),
// its range isn't zero (but rather 0 to 100),
// so we need to specifically ignore it then.
if (GetScrollInfo(hTreeWnd, SB_HORZ, &scrollInfo) &&
GetScrollBarInfo(hTreeWnd, OBJID_HSCROLL, &scrollBarInfo))
{
// Only if the scrollbar is displayed
if ((scrollBarInfo.rgstate[0] & STATE_SYSTEM_INVISIBLE) == 0)
{
int scrollBarWidth = GetSystemMetrics(SM_CXVSCROLL);
// This is a hardcoded value to accomodate some extra pixels.
// If you can find a cleaner way to account for them (e.g. through
// some extra calls to GetSystemMetrics), please do so.
// (Maybe less than 10 is also enough.)
const int extra = 10;
return (scrollInfo.nMax - scrollInfo.nMin) + scrollBarWidth + extra;
}
}
return 0;
}

Related

Unreliable behaviour of GTK function gtk_tree_view_scroll_to_cell()

I need some help understanding why a bug is happening in some code using a GtkTreeView, and how to fix it.
In the program, the user opens an image and runs a function to detect stars in the image. This is all reliable and works fine. Data about each star is populated into a GtkTreeView.
The user then clicks on a star in the display which triggers a callback:
gboolean on_drawingarea_button_release_event(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
that in turn calls the function shown below:
void set_iter_of_clicked_psf(double x, double y) {
GtkTreeSelection *selection = GTK_TREE_SELECTION(gtk_builder_get_object(gui.builder, "treeview-selection"));
GtkTreeView *treeview = GTK_TREE_VIEW(lookup_widget("Stars_stored"));
GtkTreeModel *model = gtk_tree_view_get_model(treeview);
GtkTreeIter iter;
gboolean valid;
gboolean is_as;
const double radian_conversion = ((3600.0 * 180.0) / M_PI) / 1.0E3;
double invpixscalex = 1.0;
double bin_X = gfit.unbinned ? (double) gfit.binning_x : 1.0;
if (com.stars && com.stars[0]) {// If the first star has units of arcsec, all should have
is_as = (strcmp(com.stars[0]->units,"px"));
} else {
return; // If com.stars is empty there is no point carrying on
}
if (is_as) {
invpixscalex = 1.0 / (radian_conversion * (double) gfit.pixel_size_x / gfit.focal_length) * bin_X;
}
valid = gtk_tree_model_get_iter_first(model, &iter);
while (valid) {
gdouble xpos, ypos, fwhmx;
gtk_tree_model_get(model, &iter, COLUMN_X0, &xpos, -1);
gtk_tree_model_get(model, &iter, COLUMN_Y0, &ypos, -1);
gtk_tree_model_get(model, &iter, COLUMN_FWHMX, &fwhmx, -1);
fwhmx *= invpixscalex;
gdouble distsq = (xpos - x) * (xpos - x) + (ypos - y) * (ypos - y);
gdouble psflimsq = 6. * fwhmx * fwhmx;
if (distsq < psflimsq) {
GtkTreePath *path = gtk_tree_model_get_path(model, &iter);
if (!path) return;
gtk_tree_selection_select_path(selection, path);
gtk_tree_view_scroll_to_cell(treeview, path, NULL, TRUE, 0.5, 0.0);
gtk_tree_path_free(path);
gui.selected_star = get_index_of_selected_star(xpos, ypos);
display_status();
redraw(REDRAW_OVERLAY);
return;
}
valid = gtk_tree_model_iter_next(model, &iter);
}
siril_debug_print("Point clicked does not correspond to a known star\n");
return;
}
The code steps through each iter in the GtkTreeView and checks whether the location clicked is close enough to the centre of a star: the key bit of the code then follows inside the conditional towards the end of the function. If the location matches a star, a GtkTreePath is declared and initialised based on the iter, changes the selection to that path and calls gtk_tree_view_scroll_to_cell() which is supposed to scroll the GtkTreeView so that the selected star is shown.
For some users the code works every time and causes the tree view to scroll to the selected star. For other users sometimes it works, sometimes it does not work. When it doesn't work I can tell the correct code path is followed because gui.selected_star is updated correctly (it triggers a drawing event in the subsequent redraw(REDRAW_OVERLAY) call. It is only gtk_tree_view_scroll_to_cell() that appears to function erractically.
Neither I nor one of my fellow developers can see why it works sometimes but not always, nor can we see why some users don't see the bug at all. So if any GTK gurus could enlighten me I'd be most grateful!

Custom trackbar ticks

I'm using the stock Trackbar control. I would like to custom draw the ticks.
Here I made an experiment, just trying to draw in the right place:
case WM_NOTIFY:
{
NMHDR* nMhdr = (NMHDR*) lParam;
NMCUSTOMDRAW* nMcd = (NMCUSTOMDRAW*) lParam;
if (nMhdr->code == NM_CUSTOMDRAW)
{
switch (nMcd->dwDrawStage)
{
case CDDS_PREPAINT:
{
return CDRF_NOTIFYITEMDRAW;
}
case CDDS_ITEMPREPAINT:
{
if (nMcd->dwItemSpec == TBCD_TICS)
{
FillRect(nMcd->hdc, &nMcd->rc, (HBRUSH) GetStockObject(BLACK_BRUSH));
return CDRF_SKIPDEFAULT;
}
else
{
return CDRF_DODEFAULT;
}
break;
}
default:
{
result = CDRF_DODEFAULT;
break;
}
}
}
break;
}
In my CDDS_ITEMPREPAINT, if dwItemSpec == TBCD_TICS, then the update rect (NMCUSTOMDRAW->rc) is always an empty rect. I checked, and for the other items (TBCD_CHANNEL and TBCD_THUMB), I get a valid rect and can draw in place of the channel and thumb.
Ok: so what's the point of TBCD_TICS if it doesn't give me a rect to draw in?
So maybe I can get the tick positions another way. Well, there's TBM_GETTICPOS, which seems like it would work. Except the documentation mentions this:
The positions of the first and last tick marks are not directly available via this message.
So how can I get the first and last tick positions? They do not correspond with the start and end of the channel, the ticks are inset slightly. Perhaps we can calculate the insert from the sides of the channel, but that seems fragile (especially on differently scaled displays).
So how can I get the first and last tick positions?
The old method (XP and older) to get them seems to still work (I just tested on Windows 10) :
RECT rectTrackbar;
GetClientRect(hWndTB, &rectTrackbar);
RECT rectThumb;
SendMessage(hWndTB, TBM_GETTHUMBRECT, 0, (LPARAM)&rectThumb);
int nThumbWidth = rectThumb.right - rectThumb.left;
int nXTicFirst = rectTrackbar.left += (nThumbWidth + 2);
int nXTicLast = rectTrackbar.right -= (nThumbWidth + 2 + 1);

PrintWindow: screenshot is offset sometimes

I'm taking screenshots of windows using PrintWindow(). This works finde with the following code.
This works most of the time - I'm calling it very often in a continuos loop - but sometimes it fails. I started saving screenshots of the times it fails. Sometimes they are just black, but sometimes the ClientArea of the captured window is somehow offset and only visible on half the picture. Why is that the case?
EDIT:
Is the PrintWindow function guaranteed to work, even when its called ~100times a second? What makes me wonder is, that after every print window I'm actually verifying two pixels which default colors I stored as a COLORREF at startup. The program says they match almost all the time. Still, once I use GetPixel, the returned value from other pixels is simply black or white from time to time (which should never be the case). Once I save the HDC/HBITMAP as a screenshot (I called GdiFlush before), the screenshot is just plain black (which shouldn't be the case either, as I said, I checked two pixels before...).
Well for now, back to the initial question: Is this definitely my fault, or is it possible that PrintWindow just gives random results from time to time, without returning an error?
Ok, this is the NEW CODE:
/* Try printing the window 10times, in case it fails. */
for (int i = 0; i <= NUM_ITERATIONS_PREP; i++) {
releaseSurface();
prepared_HWND = hwnd;
window_dc = GetWindowDC(hwnd);
surface = CreateCompatibleDC(window_dc);
if (surface) {
RECT r;
GetWindowRect(hwnd, &r);
bmp = CreateCompatibleBitmap(window_dc, r.right - r.left, r.bottom - r.top);
if (!ReleaseDC(hwnd, window_dc)) {
writeLog("ReleaseDC failed.", black, true);
}
if (bmp) {
old_surface_bmp = SelectBitmap(surface, bmp);
//InvalidateRect(hwnd, 0, TRUE);
//UpdateWindow(hwnd);
int result = PrintWindow(hwnd, surface, 0);
if (result != 0) {
if (!verify || verifySurface())
break;
else {
writeLog("Surface could not be verified on iteration " + std::to_string(i) + ". Saving screenshot.", black, true);
saveDebugScreenshot();
QThread::msleep(2);
}
} else {
writeLog("PrintWindow() failed.", black, true);
}
}
else {
writeLog("BMP creation failed.", black, true);
}
}
else {
writeLog("HDC creation failed.", black, true);
}
}
And the new RELEASE CODE:
/* If you call prepareSurface(), call this once the surface isn't need anymore. */
void ClientProfile::releaseSurface() {
SelectObject(surface, old_surface_bmp);
DeleteDC(surface);
DeleteObject(bmp);
prepared_HWND = NULL;
}
releaseSurface() is called either at the beginning of prepareSurface() and additionally every time I'm done processing a table (this means its called once too often for every prepareSurface(). Is that a problem?).
Oh and old_surface_bmp, bmp and surface are declared as private class attributes.

How to get current display mode (resolution, refresh rate) of a monitor/output in DXGI?

I am creating a multi-monitor full screen DXGI/D3D application. I am enumerating through the available outputs and adapters in preparation of creating their swap chains.
When creating my swap chain using DXGI's IDXGIFactory::CreateSwapChain method, I need to provide a swap chain description which includes a buffer description of type DXGI_MODE_DESC that details the width, height, refresh rate, etc. How can I find out what the output is currently set to (or how can I find out what the display mode of the output currently is)? I don't want to change the user's resolution or refresh rate when I go to full screen with this swap chain.
After looking around some more I stumbled upon the EnumDisplaySettings legacy GDI function, which allows me to access the current resolution and refresh rate. Combining this with the IDXGIOutput::FindClosestMatchingMode function I can get pretty close to the current display mode:
void getClosestDisplayModeToCurrent(IDXGIOutput* output, DXGI_MODE_DESC* outCurrentDisplayMode)
{
DXGI_OUTPUT_DESC outputDesc;
output->GetDesc(&outputDesc);
HMONITOR hMonitor = outputDesc.Monitor;
MONITORINFOEX monitorInfo;
monitorInfo.cbSize = sizeof(MONITORINFOEX);
GetMonitorInfo(hMonitor, &monitorInfo);
DEVMODE devMode;
devMode.dmSize = sizeof(DEVMODE);
devMode.dmDriverExtra = 0;
EnumDisplaySettings(monitorInfo.szDevice, ENUM_CURRENT_SETTINGS, &devMode);
DXGI_MODE_DESC current;
current.Width = devMode.dmPelsWidth;
current.Height = devMode.dmPelsHeight;
bool useDefaultRefreshRate = 1 == devMode.dmDisplayFrequency || 0 == devMode.dmDisplayFrequency;
current.RefreshRate.Numerator = useDefaultRefreshRate ? 0 : devMode.dmDisplayFrequency;
current.RefreshRate.Denominator = useDefaultRefreshRate ? 0 : 1;
current.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
current.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
current.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
output->FindClosestMatchingMode(&current, outCurrentDisplayMode, NULL);
}
...But I don't think that this is really the correct answer because I'm needing to use legacy functions. Is there any way to do this with DXGI to get the exact current display mode rather than using this method?
I saw solution here:
http://www.rastertek.com/dx11tut03.html
In folow part:
// Now go through all the display modes and find the one that matches the screen width and height.
// When a match is found store the numerator and denominator of the refresh rate for that monitor.
for(i=0; i<numModes; i++)
{
if(displayModeList[i].Width == (unsigned int)screenWidth)
{
if(displayModeList[i].Height == (unsigned int)screenHeight)
{
numerator = displayModeList[i].RefreshRate.Numerator;
denominator = displayModeList[i].RefreshRate.Denominator;
}
}
}
Is my understanding correct, the available resolution is in the displayModeList.
This might be what you are looking for:
// Get display mode list
std::vector<DXGI_MODE_DESC*> modeList = GetDisplayModeList(*outputItor);
for(std::vector<DXGI_MODE_DESC*>::iterator modeItor = modeList.begin(); modeItor != modeList.end(); ++modeItor)
{
// PrintDisplayModeInfo(*modeItor);
}
}
std::vector<DXGI_MODE_DESC*> GetDisplayModeList(IDXGIOutput* output)
{
UINT num = 0;
DXGI_FORMAT format = DXGI_FORMAT_R32G32B32A32_TYPELESS;
UINT flags = DXGI_ENUM_MODES_INTERLACED | DXGI_ENUM_MODES_SCALING;
// Get number of display modes
output->GetDisplayModeList(format, flags, &num, 0);
// Get display mode list
DXGI_MODE_DESC * pDescs = new DXGI_MODE_DESC[num];
output->GetDisplayModeList(format, flags, &num, pDescs);
std::vector<DXGI_MODE_DESC*> displayList;
for(int i = 0; i < num; ++i)
{
displayList.push_back(&pDescs[i]);
}
return displayList;
}

Best algorithm for syntax highlight (WINAPI)

I write a program on WINAPI. I must to implement syntax highlight. At this moment the I using following algorithm:
void PaintWords(const char *SearchWord,COLORREF rgb)
{
counter = TabCtrl_GetCurSel(TabControl_hWnd);
ft.chrg.cpMin = 0;
ft.chrg.cpMax = GetWindowTextLength(hWnd);
ft.lpstrText = (LPCSTR)SearchWord; //keyword
do
{
int poe_p = SendMessage(hWnd, EM_FINDTEXTEX, FR_DOWN | FR_WHOLEWORD | FR_MATCHCASE, (LPARAM)&ft);
if(poe_p != -1)
{
int selword = SendMessage(hWnd, EM_EXSETSEL,0,(LPARAM)&ft.chrgText);
ZeroMemory(&chd, sizeof(chd));
chd.cbSize = sizeof(CHARFORMAT);
chd.dwMask = CFM_SIZE | CFM_FACE | CFM_COLOR | CFM_CHARSET;
chd.crTextColor = rgb;
chd.bPitchAndFamily = FW_THIN;
lstrcpy(chd.szFaceName , "Courier New");
SendMessage(hWnd,EM_SETCHARFORMAT,SCF_WORD|SCF_SELECTION,(LPARAM)&chd);
ft.chrg.cpMin = ft.chrgText.cpMax;
}
else
{
break;
}
}while(ft.chrg.cpMin != ft.chrg.cpMax);
}
This code is too slow, because this is not best option, flicker is visible.
I interested in other variants.
I think you need to use double buffering to reduce the flicker. Other than that, you should not use SendMessage (or even PostMessage) to any window (within same thread). Why selection must happen in your syntax-highlighting paint code?
One of the article on double buffer is this.
I have come across this project's RichTextbox, which is used as an xml editor: http://xpathvisualizer.codeplex.com/SourceControl/changeset/view/42057#XPathVisualizer/CustomControls/RichTextBoxEx.cs
it's in C#, but the messages sent are visible.
When using this TextBox, before highlighting the text the BeginUpdateAndSuspendEvents function should be called.
public IntPtr BeginUpdateAndSuspendEvents()
{
// Stop redrawing:
User32.SendMessage(this.Handle, (int) User32.Msgs.WM_SETREDRAW, 0, IntPtr.Zero);
// Stop sending of events:
IntPtr eventMask = User32.SendMessage(this.Handle, User32.Msgs.EM_GETEVENTMASK, 0, IntPtr.Zero);
return eventMask;
}
This function prevents redrawing while you are working on the text, after you finish editing, you should call
public void EndUpdateAndResumeEvents(IntPtr eventMask)
{
// turn on events
User32.SendMessage(this.Handle, User32.Msgs.EM_SETEVENTMASK, 0, eventMask);
// turn on redrawing
User32.SendMessage(this.Handle, User32.Msgs.WM_SETREDRAW, 1, IntPtr.Zero);
NeedRecomputeOfLineNumbers();
this.Invalidate();
}
Double buffering does not solve this problem because it will not stop the paints from occurring, highlighting an editor without disabling the paint event can cause the program to halt for more than 5 minutes depending on the size of the file and the number of words to be highlighted.

Resources