transient windows on win32



Hi,

Having been frustrated by vanishing tool windows in inkscape, I've
developed a patch to make transient windows work on win32.

The existing code had two problems, one trivial and one not.

The trivial problem was that gdk_window_set_skip_taskbar_hint set the
window owner.  This overwrote the window owner set by
gdk_window_set_transient_for.  (I guess this was deliberate, since
transients didn't work anyway?)

The non-trivial problem was caused by the windows taskbar.  Normally, when
you minimize a window, Windows automatically hides any owned (transient)
windows.  GDK suppresses this, so owned windows stay visible.  However, if
you click on an owned window while the owner is minimized, the taskbar
starts treating the owned window as the application window.  So when you
click on the taskbar button to restore the application, Windows restores
the owned window instead and the application stays hidden.  The solution
I've implemented is to disconnect the owned window from its owner when the
owner window is minimized, and to restore the ownership relation when the
owner is restored.  If set_skip_taskbar_hint has been called the owned
window is given a temporary owner (rather than no owner) to keep it off
the taskbar while the owner is minimized.

If you want to try this with inkscape you need to edit the inkscape
functions sp_transientize and sp_transientize_callback in
dialog-events.cpp to comment out the "#ifndef WIN32"/"#endif" lines.

I've tested this with inkscape on XPSP2 and briefly with the gimp (which
didn't seem to behave any differently).  The patches were created with
diff (not cvs diff).

Any comments would be most welcome.

Cheers,
Charles

gdk\win32\gdkwindow-win32.h:

69a70,72
>   GdkWindow *owner_window;
>
>   gboolean skips_taskbar;

gdk\win32\gdkwindow-win32.c:

86a87,94
> static void gdk_window_win32_set_window_ownership(GdkWindow* window,
> 							gboolean is_owner_minimized);
> static GdkFilterReturn gdk_window_win32_owner_window_filter (GdkXEvent
*xev,
> 							GdkEvent  *event,
> 							gpointer   data);
> static GdkFilterReturn gdk_window_win32_owned_window_filter (GdkXEvent
*xev,
> 							GdkEvent  *event,
> 							gpointer   data);
662a671,674
>   // If the window is not a child then its parent is its owner
>   if ((dwStyle & WS_CHILDWINDOW) == 0)
>     impl->owner_window = parent;
>
886a899,900
>   GdkWindowImplWin32 *impl;
>
894a909,917
>   // Ideally, this would be handled in
gdk_window_win32_owned_window_filter but
>   // the filter list isn't reentrant, so we remove the owner window
filter here
>   impl = GDK_WINDOW_IMPL_WIN32 (GDK_WINDOW_OBJECT (window)->impl);
>   if (impl->owner_window)
>   {
>     gdk_window_remove_filter(impl->owner_window,
gdk_window_win32_owner_window_filter, window);
>     impl->owner_window = NULL;
>   }
>
1779a1803,1862
> static GdkFilterReturn
> gdk_window_win32_owner_window_filter (GdkXEvent *xev,
> 		      GdkEvent  *event,
> 		      gpointer   data)
> {
>   MSG *msg = (MSG *) xev;
>   // This is a window owned by the window that received the message
>   GdkWindow *window = (GdkWindow*)data;
>   GdkWindowImplWin32 *impl;
>
>   g_return_if_fail (GDK_IS_WINDOW (window));
>
>   if (msg->message == WM_ACTIVATE)
>   {
>     if (LOWORD(msg->wParam) == WA_INACTIVE)
>       gdk_window_win32_set_window_ownership(window, HIWORD(msg->wParam));
>   }
>   else if (msg->message == WM_SIZE)
>   {
>     if ((msg->wParam == SIZE_MAXIMIZED) || (msg->wParam == SIZE_RESTORED))
> 		gdk_window_win32_set_window_ownership(window, FALSE);
>   }
>   else if (msg->message == WM_DESTROY)
>   {
>     impl = GDK_WINDOW_IMPL_WIN32 (GDK_WINDOW_OBJECT (window)->impl);
>     gdk_window_remove_filter(impl->owner_window,
gdk_window_win32_owner_window_filter, window);
> 	impl->owner_window = NULL;
>   }
>
>   return GDK_FILTER_CONTINUE;
> }
>
> static GdkFilterReturn
> gdk_window_win32_owned_window_filter (GdkXEvent *xev,
> 		      GdkEvent  *event,
> 		      gpointer   data)
> {
>   MSG *msg = (MSG *) xev;
>   GdkWindow* window = event->any.window;
>   GdkWindowImplWin32 *impl;
>
>   g_return_if_fail (GDK_IS_WINDOW (window));
>
>   if (msg->message == WM_SHOWWINDOW)
>   {
>     if (!msg->wParam && (msg->lParam == SW_PARENTCLOSING))
> 	{
>       gdk_window_win32_set_window_ownership(window, TRUE);
>     }
>   }
>   else if (msg->message == WM_DESTROY)
>   {
>     gdk_window_remove_filter(window,
gdk_window_win32_owned_window_filter, 0);
> 	// We can't remove the other filter here because the filter list isn't
reentrant.
> 	// This is handled in gdk_window_destroy_notify
>   }
>
>   return GDK_FILTER_CONTINUE;
> }
>
1784c1867
<   HWND window_id, parent_id;
---
>   gboolean is_minimized = FALSE;
1787a1871
>   g_return_if_fail ((parent == NULL) || GDK_IS_WINDOW (parent));
1802,1803c1886,1888
<   window_id = GDK_WINDOW_HWND (window);
<   parent_id = GDK_WINDOW_HWND (parent);
---
>   // This may be called several times, but the gdk_window_add_filter
function
>   // discards duplicate entries.
>   gdk_window_add_filter(window, gdk_window_win32_owned_window_filter, 0);
1805,1813c1890,1988
<   /* This changes the *owner* of the window, despite the misleading
<    * name. (Owner and parent are unrelated concepts.) At least that's
<    * what people who seem to know what they talk about say on
<    * USENET. Search on Google.
<    */
<   SetLastError (0);
<   if (SetWindowLong (window_id, GWL_HWNDPARENT, (long) parent_id) == 0 &&
<       GetLastError () != 0)
<     WIN32_API_FAILED ("SetWindowLong");
---
>   GdkWindowImplWin32 *impl;
>   impl = GDK_WINDOW_IMPL_WIN32 (GDK_WINDOW_OBJECT (window)->impl);
>
>   if (impl->owner_window != parent)
>   {
>     if (NULL != impl->owner_window)
>     {
>       // Remove the previous filter on the parent window
>       gdk_window_remove_filter(impl->owner_window,
gdk_window_win32_owner_window_filter, window);
>     }
>
>     impl->owner_window = parent;
>
> 	if (NULL != parent)
> 	{
>       // Set a filter on the parent window so that when it's restored
>       // this window gets hooked up again.
>       gdk_window_add_filter(parent,
gdk_window_win32_owner_window_filter, window);
>     }
>   }
>
>   if (parent)
>   {
> 	is_minimized = IsIconic(GDK_WINDOW_HWND (parent));
>   }
>
>   // This window is unlikely to get a new transient if it's minimized,
>   // but it can happen if you right-click on a minimized application
>   // and choose close; the "do you want to save" dialog is shown.
>   // Note that that code is broken because it positions the dialog
>   // off screen.
>   gdk_window_win32_set_window_ownership(window, is_minimized);
> }
>
> // Set window ownership based on the taskbar hint, whether it's transient
> // and whether the owner is minimizing.
> // This is a lot of bother.  The problem is with the Windows taskbar.  If
> // you minimize an owner window but don't let Windows hide the owned
> // windows, then when you click on one of the owned windows the taskbar
> // starts treating it as the application window.  So if you click on
> // the taskbar button the application window doesn't reappear.  We fix
> // this by disconnecting owned windows from their owners when the owners
> // are minimized.  The gdk_window_win32_restore_transients function
> // reattaches owned windows to their owner when the owner is restored.
> static void
> gdk_window_win32_set_window_ownership(GdkWindow* window, gboolean
is_owner_minimized)
> {
> 	GdkWindowImplWin32 *impl;
>     static GdkWindow *owner = 0;
>     GdkWindowAttr wa;
>
>     g_return_if_fail (GDK_IS_WINDOW (window));
>
> 	impl = GDK_WINDOW_IMPL_WIN32 (((GdkWindowObject *) window)->impl);
>
> 	if (!is_owner_minimized && impl->owner_window)
> 	{
>       g_return_if_fail (GDK_IS_WINDOW (impl->owner_window));
>
>       SetWindowLong(GDK_WINDOW_HWND (window), GWL_HWNDPARENT, (LONG_PTR)
GDK_WINDOW_HWND (impl->owner_window));
>       // Ensure the owner is behind the owned window
>       SetWindowPos(GDK_WINDOW_HWND (impl->owner_window), GDK_WINDOW_HWND
(window), 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
> 	}
> 	else if (impl->skips_taskbar || (is_owner_minimized &&
impl->owner_window))
> 	{
>       // Don't overwrite an existing owner unless it's minimizing
>       if (!(impl->owner_window && is_owner_minimized))
>       {
>         if (GetWindowLong(GDK_WINDOW_HWND (window), GWL_HWNDPARENT) !=
NULL)
>           return;
> 	  }
>
>       if (NULL == owner)
>       {
>         // Create an invisible window to act as the owner.  The variable
is static
>         // so it gets reused.
>         wa.window_type = GDK_WINDOW_TEMP;
>         wa.wclass = GDK_INPUT_OUTPUT;
>         wa.width = wa.height = 1;
>         wa.event_mask = 0;
>         owner = gdk_window_new_internal (NULL, &wa, 0, TRUE);
> 	  }
>
>       SetWindowLongPtr (GDK_WINDOW_HWND (window), GWL_HWNDPARENT,
>         (LONG_PTR) GDK_WINDOW_HWND (owner));
>
> #if 0 /* Should we also turn off the minimize and maximize buttons? */
>       SetWindowLong (GDK_WINDOW_HWND (window), GWL_STYLE,
> 		     GetWindowLong (GDK_WINDOW_HWND (window), GWL_STYLE) &
~(WS_MINIMIZEBOX|WS_MAXIMIZEBOX|WS_SYSMENU));
>       SetWindowPos (GDK_WINDOW_HWND (window), NULL,
> 		    0, 0, 0, 0,
> 		    SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE |
> 		    SWP_NOREPOSITION | SWP_NOSIZE | SWP_NOZORDER);
> #endif
> 	}
> 	else
> 	{
>       SetWindowLong(GDK_WINDOW_HWND (window), GWL_HWNDPARENT, 0);
> 	}
3155,3156c3330,3331
<   static GdkWindow *owner = NULL;
<   GdkWindowAttr wa;
---
>   GdkWindowImplWin32 *impl;
>   gboolean is_minimized = FALSE;
3164,3173c3339,3340
<   if (skips_taskbar)
<     {
<       if (owner == NULL)
< 	{
< 	  wa.window_type = GDK_WINDOW_TEMP;
< 	  wa.wclass = GDK_INPUT_OUTPUT;
< 	  wa.width = wa.height = 1;
< 	  wa.event_mask = 0;
< 	  owner = gdk_window_new_internal (NULL, &wa, 0, TRUE);
< 	}
---
>   impl = GDK_WINDOW_IMPL_WIN32 (((GdkWindowObject *) window)->impl);
>   impl->skips_taskbar = skips_taskbar;
3175,3176c3342,3345
<       SetWindowLong (GDK_WINDOW_HWND (window), GWL_HWNDPARENT,
< 		     (long) GDK_WINDOW_HWND (owner));
---
>   if (impl->owner_window)
>   {
> 	is_minimized = IsIconic(GDK_WINDOW_HWND (impl->owner_window));
>   }
3178,3190c3347
< #if 0 /* Should we also turn off the minimize and maximize buttons? */
<       SetWindowLong (GDK_WINDOW_HWND (window), GWL_STYLE,
< 		     GetWindowLong (GDK_WINDOW_HWND (window), GWL_STYLE) &
~(WS_MINIMIZEBOX|WS_MAXIMIZEBOX|WS_SYSMENU));
<       SetWindowPos (GDK_WINDOW_HWND (window), NULL,
< 		    0, 0, 0, 0,
< 		    SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE |
< 		    SWP_NOREPOSITION | SWP_NOSIZE | SWP_NOZORDER);
< #endif
<     }
<   else
<     {
<       SetWindowLong (GDK_WINDOW_HWND (window), GWL_HWNDPARENT, 0);
<     }
---
>   gdk_window_win32_set_window_ownership(window, is_minimized);




[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]