MFC: Freezing on dialog SetParent() - windows

Note: Using SetParent freeze the parent window exists, but does NOT seem to be related to this problem as its in an entirely different framework, in a different language, and seems to be an issue with the message pump (though the message pump here could be the problem, I dont think any solution directly helps the problem I am facing here)
I am trying to create a dialog in MFC and attaching it to a parent window as a modeless dialog. My first attempt at this looks like the following:
// Add window to the segment dialog vector
m_segmentDialogs.emplace_back(std::make_unique<DlgSegmentDatum>(this));
const int tab_number = m_segmentDialogs.size() - 1;
std::string tab_text = "Segment " + std::to_string(tab_number);
m_tabSegments.InsertItem(tab_number, tab_text.c_str());
// Initialize the new dialog
auto& dlg = m_segmentDialogs.back();
dlg->Create(IDD_DIALOG_SEGMENT_DATUM, this);
CRect rc_client, rc_window;
m_tabSegments.GetClientRect(&rc_client);
m_tabSegments.AdjustRect(FALSE, &rc_client);
m_tabSegments.GetWindowRect(&rc_window);
ScreenToClient(rc_window);
rc_client.OffsetRect(rc_window.left, rc_window.top);
dlg->MoveWindow(&rc_client);
displaySegmentTab(tab_number);
This results in the child dialog spawning in the top left corner of the my screen. I assumed that this was because the child dialog didn't associate itself with the parent for some reason. To fix this, I updated the following code segment.
// Initialize the new dialog
auto& dlg = m_segmentDialogs.back();
dlg->Create(IDD_DIALOG_SEGMENT_DATUM, this);
dlg->SetParent(this);
This positions the dialog correctly, but immediately freezes the program.

When you create Dialog resource, it has WS_POPUP style by default. To make it a child of another window, it has to be WS_CHILD.
You can either fix it in your resource file (easy), or, if you use that template elsewhere as a modal dialog, modify its style at run time using ModifyStyle

Related

CEdit not set focus automatically

I'v create an Edit control dynamic by calling the constructor function and the Create function of CEdit:
m_pMyEdit = new CEdit;
m_pMyEdit->Create(style,zindex,100,100,100,100,pParentWindow,ID);
As you know, we can pass the this to the parent window parameter or set this parameter to another window. In my code, I passed the parent window parameter the value of:
CWnd::FromHandle(GetDesktopWindow())
This piece of code can get a pointer of CWnd (CWnd*) from the specific handle. Now run program and the Edit window shows at the position as we expect, and the caret shows in the Edit control and the focus is activited. But when I moved this edit to somewhere else, for example, move it to:
RECT rect = {200,200,100,100};
the caret disappear and the focus has been killed, further more, when I move the cursor on it, click mouse, the caret not appear and the focus not set any more. I have read the MSDN again and again, but I don't know why this happens.
By the way, the m_pMyEdit is the var of CEdit, I haven't derived the CEdit class yet.

How can a dialog become responsive while waiting for a call to DoModal() to return?

A button on a dialog causes a child dialog to be created, and displayed modally.
e.g:
void ParentDialog::OnButton()
{
ChildDialog dlg;
int ret = dlg.DoModal();
}
The parent dialog is initially unresponsive as expected. But, the child dialog also makes COM calls to a server module, which causes the server to make COM calls back to the parent dialog... to update displayed data, etc. In one case, this causes the parent dialog to suddenly become responsive again even though the child dialog is still on screen. Both dialogs can now be interacted with even though the parent dialog is still patiently waiting for OnButton()to return.
How can this happen? I'm trawling the code but is there anything specific I should be looking for?
The dialog has its own message pump/loop and inside the call it has a loop where it keeps receiving and dispatching window messages. This includes COM related messages worker windows receive and convert to COM callbacks you are seeing. Once the dialog is closed, respective windows are destroyed, the function exits from the loop and returns control to your code.
That is, without returning control to your code immediately, window message dispatching still keeps working as usually and UI is responsive.
MSDN:
... The function displays the dialog box, disables the owner window, and starts its own message loop to retrieve and dispatch messages for the dialog box.
When the dialog box procedure calls the EndDialog function, DialogBox destroys the dialog box, ends the message loop, enables the owner window (if previously enabled), and returns the nResult parameter specified by the dialog box procedure when it called EndDialog.
You can create a modeless dialog and use RunModalLoop to wait for the dialog to finish, somewhat similar to DoModal()
void CMyWnd::foo()
{
static BOOL isOpen = FALSE;
if (isOpen) return;//optional, prevents opening multiple dialogs
CDialog dlg;
dlg.Create(IDD_DIALOG1, this);
dlg.ShowWindow(SW_SHOW);
isOpen = TRUE;
int result = dlg.RunModalLoop(0);
dlg.DestroyWindow();
isOpen = 0;
TRACE("res:%d\n", result);
}
Problem: Note that in the above example you can close the program but the dialog will still be up. You have to force the dialog close, the above function doesn't deal with that.
Or you can create modeless dialog the usual way:
if (m_Dlg) return;
m_Dlg = new CDialog
m_Dlg.Create(IDD_DIALOG1, this);
m_Dlg.ShowWindow(SW_SHOW);
However in this example if user clicks OK/Cancel then you don't know about it, you have to override OnOK()/OnCancel then send message to parent Window/Dialog, process message, and then destroy the dialog manually.

XLIB Decoration questions

I'm writing a small window manager, that add a basic decoration around a window, but actually i have several question about adding/remove decoration of a window.
First Question
Actually the decoration is added during MapNotify event, but it seems to be not a good idea, because it add decoration also to a menu opened by an application everytime the mapnotify is fired with a new window, but i want only to add decoration to main window. Maybe i have to check if the current window is a child of another window ? Actually my code just create the decoration window with a specific name, so at every MapNotify request i give the decoration window a dummy name (Parent) to distinguish it from all other windows in that way if the MapNotify event is launched on a decoration window, at least it doesn't add another decoration.
But i don't understand if MapNotify is launched not only for parent window but also for childrend probably the risk is that i add more than one decoration window.
The actual code is the following:
void map_notify_handler(XEvent local_event, Display* display, ScreenInfos infos){
printf("Map Notify\n");
XWindowAttributes win_attr;
char *child_name;
XGetWindowAttributes(display, local_event.xmap.window, &win_attr);
XFetchName(display, local_event.xmap.window, &child_name);
printf("Attributes: W: %d - H: %d - Name: %s\n", win_attr.width, win_attr.height, child_name);
if(child_name!=NULL){
if(strcmp(child_name, "Parent")){
Window new_win = draw_window_with_name(display, RootWindow(display, infos.screen_num), "Parent", infos.screen_num,
win_attr.x, win_attr.y, win_attr.width, win_attr.height+DECORATION_HEIGHT, 0,
BlackPixel(display, infos.screen_num));
XMapWindow(display, new_win);
XReparentWindow(display,local_event.xmap.window, new_win,0, DECORATION_HEIGHT);
XSelectInput(display, local_event.xmap.window, SubstructureNotifyMask);
put_text(display, new_win, child_name, "9x15", 10, 10, BlackPixel(display,infos.screen_num), WhitePixel(display, infos.screen_num));
}
}
XFree(child_name);
}
So how to avoid adding of decoration on every window except the main application window (or the popup windows, there is a way to distinguish the type of window? How can i figure it out?)
Second Question
WHen i exit a program the window that is destroyed is just the application window not the parent decoration, how to destroy the current window and also the decoration?
I tried with the following:
void destroy_notify_handler(XEvent local_event, Display *display){
Window window = local_event.xdestroywindow.event;
XDestroyWindow(display, window);
}
But i receive the following error:
Error occurred: BadWindow (invalid Window parameter)
I use event instead of window because it seems that it contains the parent window (i read it from there: http://tronche.com/gui/x/xlib/events/window-state-change/destroy.html)
But even if i use window i have the same problem.
Or maybe i have to destroy the parent window earlier? Maybe during UnMapNotify? But how to understand if the event is launched just because the window is going to be closed or for some other reasons?
Thanks for the help :)
Read EWMH spec and you'll find answers to all questions.
Check "override redirect" window flag
You are trying
to destroy window which is already destroyed. Instead of using
event.xdestroywindow.event window id just delete your decoration
window.
Don't forget to add client window to save set if you are
writing reparenting WM. That way if you kill wm application windows
are not destroyed but reparented back to root window

property sheet in dialog using WINAPI example (without using MFC)

Can anyone point me at an WINAPI example of embedding a property sheet in a dialog box using WINAPI (not MFC)?
For the record, here are some of the things I learned while
investigating this question. I obtained most/all of this from
asking questions here or by searching the web. Thanks to all for
your help!
I have written a class to encapsulate everything I've discovered;
if interested in getting a copy send me an email mdorl#wisc.edu.
Pass the HWND of the parent window for your property sheet in
.hwndParent of the PROPSHEETHEADER when creating the property sheet
with the PropertySheet page function.
I used a picture control on the dialog as the parent of the property
sheet so that's the HWND I used as the hwndParent.
Change the style of the property sheet using the call back
pfnCallback field in the header, don't forget to include
PSH_USECALLBACK in dwFlags. See PropSheetProc Function in
the MSDN. In the PSCB_PRECREATE handler of the callback,
adjust the window style of the property sheet by removing
WS_POPUP and adding WS_CHILD. I did not want a border or title
bar on the property sheet so I also removed WS_CAPTION, WS_SYSEMNU,
& DS_MODALFRAME
LONG L = ((LPDLGTEMPLATE)lParam)->style;
L &= ~WS_POPUP;
L &= ~WS_CAPTION; // gets rid of title bar
L &= ~WS_SYSMENU;
L &= ~DS_MODALFRAME; // gets rid of border
L |= WS_CHILD; // 40000000
((LPDLGTEMPLATE)lParam)->style = L;
Using a child window instead of a popup window neatly solves
some problems such as moving the property sheet when the parent
moves, keeping the blue/gray state of the parent window properly
colored, and clipping issues. I.e. you don't have to worry about these things.
If you stopped here, you'd face another problem. The dialog support
code in WIN32 would get in a cpu loop trying to find the property sheet and
controls in its Windows. See
What is WS_EX_CONTROLPARENT and DS_CONTROL For?
for a pretty good explanation of
this problem. The solution is to add the WS_EX_CONTROLPARENT extended
style to the property sheet. You can find information on this
on the web by searching for WS_EX_CONTROLPARENT. What you won't find
caused me much grief. If you use a control on the dialog as the
parent, you must also set its WS_EX_CONTROLPARENT.
I do not know what the difference between WS_EX_CONTROLPARENT and DS_CONTROL is nor do I know if you could substitute DS_CONTROL for WS_EX_CONTROLPARENT. From the web I gather that DS_CONTROL has something to do with tabs. My test app works the same with respect to tabs with or without DS_CONTROL.
I took care of the WS_EX_CONTROLPARENT business after calling the
PropertySheet function to create the property sheet. I do not know
if you could do this in the call back routine or not. I suppose
you could use GetParent in the call back to get the HWND of the
control.
LONG S;
S = GetWindowLong (hwndPS, GWL_EXSTYLE) | WS_EX_CONTROLPARENT;
SetWindowLong (hwndPS, GWL_EXSTYLE, S);
S = GetWindowLong (hwndPS_Area, GWL_EXSTYLE) | WS_EX_CONTROLPARENT;
SetWindowLong (hwndPS_Area, GWL_EXSTYLE, S);
After creating the property sheet with the PropertySheet function,
you have to position it properly:
SetWindowPos(hwndPS, HWND_TOP, 2, 2, -1, -1,
SWP_NOSIZE | SWP_NOACTIVATE);
I allowed a couple of pixels for the border.
If you want to get rid of all the property sheet buttons and the space they take:
RECT rectWnd;
RECT rectButton;
GetWindowRect(hwndPS, &rectWnd);
HWND hWnd = ::GetDlgItem(hwndPS, IDOK);
if(!hWnd)
{
DebugBreak();
}
GetWindowRect(hWnd, &rectButton);
SetWindowPos (hwndPS, NULL, 0, 0,
rectWnd.right - rectWnd.left, rectButton.top - rectWnd.top,
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
Individual buttons can be eliminated:
hwndOk = GetDlgItem (hwndPS,IDOK); // Hide the OK button
ShowWindow (hwndOk, SW_HIDE);
EnableWindow (hwndOk, FALSE);
You can get rid of the APPLY button using the PSH_NOAPPLYNOW
dwFlags in the PROPERTYSHEETHEADER when you create the property sheet.
The property sheet pages will NOT be created until the user first
activates them. I wanted them all to be created when the property
sheet was created.
unsigned int iP;
for (iP=0; iP<Header.nPages; iP++)
{
if (iP != Header.nStartPage)
SendMessage (hwndPS, PSM_SETCURSEL,iP,NULL);
}
SendMessage (hwndPS, PSM_SETCURSEL,Header.nStartPage,NULL);
There's a PROPSHEETPAGE flag that does the same thing (from the MSDN)
PSP_PREMATURE Version 4.71. Causes the page to be created when the
property sheet is created. If this flag is not specified, the page
will not be created until it is selected the first time.
But I was worried about the Version 4.71 business so I did it myself.
You can change the text on any of the buttons:
SetDlgItemText (hwndPS,ButtonID,Text);
where ButtonID is one of IDOK IDCANCEL IDHELP IDAPPLYNOW
IDAPPLYNOW is undefined on my system so
#define IDAPPLYNOW 0x3021
Here's how I intend to use my property sheet. I'm going to put all the initialization and
termination code in the dialog containing the property sheet and eliminate all the property sheet buttons. I can set all the initial values there and retrieve all the final values there when the user pushes some DO IT button. I notice that the act of setting, for example, a text box causes the containing page dialog to get a WM_COMMAND/EN_CHANGE. If you use this message to enable the APPLY button, you may need to disable the change flag for the page after setting any list boxes. I solved this problem by clearing all the changed flags after setting the initial value. (I suspect the property sheet clears the changed flag after all the INITDIALOG messages have been sent. In any case APPLY is not enabled if the text boxes are set in the page INITDIALOG handlers.) If I find an error I will select that property sheet page and put some red text in the parent dialog describing the error. The only thing the property sheet dialog procedures need to do is manipulate their controls.
One of the things that puzzles me is how to determine if all pages have returned PSNRET_NOERROR from their WS_NOTIFY/PSN_APPLY messages. I've had some success counting the
number of successive PSNRET_NOERROR replies setting the count to zero when a PSNRET_INVALID is reported or a PSN_KILLACTIVE is received. If the count reaches the number of pages in the property sheet header, I assume all pages have returned PSNRET_NOERROR. But I worry about things like disabled and invisible pages.

In X11, how do I set the window title before creating it?

Context:
I use glfw under xmonad. Glfw apparently sets the window title after creating the window, thus not allowing xmonad to properly handle it. I want to modify the glfw source so that I can set the window title before creating the window.
Problem:
So I download glfw-2.6, and I look into lib/x11/x11_window.c ; the lines causing the trouble are:
// Create a window
_glfwWin.Win = XCreateWindow(
_glfwLibrary.Dpy,
RootWindow( _glfwLibrary.Dpy, _glfwWin.VI->screen ),
0, 0, // Upper left corner
_glfwWin.Width, _glfwWin.Height, // Width, height
0, // Borderwidth
_glfwWin.VI->depth, // Depth
InputOutput,
_glfwWin.VI->visual,
CWBorderPixel | CWColormap | CWEventMask,
&wa
);
Followed sometime later by:
_glfwPlatformSetWindowTitle( "GLFW Window" );
where
void _glfwPlatformSetWindowTitle( const char *title )
{
// Set window & icon title
XStoreName( _glfwLibrary.Dpy, _glfwWin.Win, title );
XSetIconName( _glfwLibrary.Dpy, _glfwWin.Win, title );
}
Now, if I tr yto move the glfwPlatformSetWindowTitle call before the CreateWindow call, I get a segfault -- as I should, since _glfwWin.win would not be defined.
I don't know how to solve this problem since to set the window title, I need _glfwWin.Win to be initialized, but to initialize it, I need to create the window.
Thus, I ask: in X11, what is the proper way to set the window title before creating the window?
Thanks!
This is not possible in X11, but also not necessary for stuff to work. There must be a bug somewhere causing the symptoms you're seeing. The window title is just a property on the window, and properties can't exist until there's a window for them to be on.
You say "not allowing xmonad to properly handle it" which implies it isn't coping with changes to the name; window managers absolutely must handle setting the title at any time, including changing the title long after a window is created.
What the spec says (http://www.x.org/docs/ICCCM/icccm.pdf) is:
"The window manager will examine the contents of these properties when the window makes the
transition from the Withdrawn state and will monitor some properties for changes while the window is in the Iconic or Normal state."
The "transition from the Withdrawn state" is the point where glfw calls XMapWindow(). At that point, the window will remain unmapped but the WM will receive a MapRequest. The WM would then read properties and such and then map the window. All window managers I've ever seen also handle later changes to the property because changing the window title is pretty normal. For example web browsers the page title on every url.
If xmonad doesn't handle changes maybe it at least waits for the map, so maybe you just need to set title before XMapWindow(). Really all setup should be done before MapWindow though only a few properties are required to be before it by the specs. The props that must be before it generally can't be changed without unmapping.
Incidentally, _glfwPlatformSetWindowTitle won't work for anything but Latin-1. The modern way to do it is to set _NET_WM_NAME and _NET_WM_ICON_NAME with XChangeProperty() (setting the old Latin-1 WM_NAME is fine too but only as a fallback).

Resources