Index: gdkwindow-win32.h
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/win32/gdkwindow-win32.h,v
retrieving revision 1.13
diff -p -u -r1.13 gdkwindow-win32.h
--- gdkwindow-win32.h	9 Nov 2005 02:13:35 -0000	1.13
+++ gdkwindow-win32.h	5 Nov 2006 20:28:41 -0000
@@ -83,9 +83,12 @@ struct _GdkWindowImplWin32
   gint hint_flags;
   GdkGeometry hints;
 
+  gboolean extension_events_selected;
+
   GdkWindowTypeHint type_hint;
 
-  gboolean extension_events_selected;
+  GdkWindow *owner_window;
+  gboolean skips_taskbar;
 };
  
 struct _GdkWindowImplWin32Class 
--- gdkwindow-win32.c	Sun Oct 29 02:54:12 2006
+++ gdkwindow-win32.c.reilly	Sun Oct 29 02:45:58 2006
@@ -1,7 +1,8 @@
 /* GDK - The GIMP Drawing Kit
  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- * Copyright (C) 1998-2004 Tor Lillqvist
+ * Copyright (C) 1998-2006 Tor Lillqvist
  * Copyright (C) 2001-2004 Hans Breuer
+ * Copyright (C) 2005 Charles Reilly
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -48,7 +49,14 @@ static GdkRegion*   gdk_window_impl_win3
 static void gdk_window_impl_win32_init       (GdkWindowImplWin32      *window);
 static void gdk_window_impl_win32_class_init (GdkWindowImplWin32Class *klass);
 static void gdk_window_impl_win32_finalize   (GObject                 *object);
-
+static void set_window_ownership             (GdkWindow* window,
+					      gboolean   is_owner_minimized);
+static GdkFilterReturn owner_window_filter   (GdkXEvent *xev,
+					      GdkEvent  *event,
+					      gpointer   data);
+static GdkFilterReturn owned_window_filter   (GdkXEvent *xev,
+					      GdkEvent  *event,
+					      gpointer   data);
 static gpointer parent_class = NULL;
 
 static void     update_style_bits         (GdkWindow *window);
@@ -610,6 +618,10 @@ gdk_window_new_internal (GdkWindow     *
       g_assert_not_reached ();
     }
 
+  /* If the window is not a child then its parent is its owner */
+  if ((dwStyle & WS_CHILDWINDOW) == 0)
+    impl->owner_window = parent;
+
   _gdk_window_init_position (GDK_WINDOW (private));
 
   if (private->window_type != GDK_WINDOW_CHILD)
@@ -834,6 +846,8 @@ _gdk_windowing_window_destroy_foreign (G
 void
 gdk_window_destroy_notify (GdkWindow *window)
 {
+  GdkWindowImplWin32 *impl;
+
   g_return_if_fail (GDK_IS_WINDOW (window));
 
   GDK_NOTE (EVENTS,
@@ -841,6 +855,17 @@ gdk_window_destroy_notify (GdkWindow *wi
 		     GDK_WINDOW_HWND (window),
 		     (GDK_WINDOW_DESTROYED (window) ? " (destroyed)" : "")));
 
+  /* Ideally, this would be handled in 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, owner_window_filter, window);
+      impl->owner_window = NULL;
+    }
+
   if (!GDK_WINDOW_DESTROYED (window))
     {
       if (GDK_WINDOW_TYPE (window) != GDK_WINDOW_FOREIGN)
@@ -1735,13 +1760,71 @@ gdk_window_set_role (GdkWindow   *window
   /* XXX */
 }
 
-void          
+static GdkFilterReturn
+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;
+
+  switch (msg->message)
+    {
+    case WM_ACTIVATE:
+      if (LOWORD (msg->wParam) == WA_INACTIVE)
+	set_window_ownership (window, HIWORD (msg->wParam));
+      break;
+    case WM_SIZE:
+      if ((msg->wParam == SIZE_MAXIMIZED) || (msg->wParam == SIZE_RESTORED))
+	set_window_ownership (window, FALSE);
+      break;
+    case WM_DESTROY:
+      impl = GDK_WINDOW_IMPL_WIN32 (GDK_WINDOW_OBJECT (window)->impl);
+      gdk_window_remove_filter (impl->owner_window, owner_window_filter, window);
+      impl->owner_window = NULL;
+      break;
+    }
+
+  return GDK_FILTER_CONTINUE;
+}
+
+static GdkFilterReturn
+owned_window_filter (GdkXEvent *xev,
+		     GdkEvent  *event,
+		     gpointer   data)
+{
+  MSG *msg = (MSG *) xev;
+  GdkWindow *window = event->any.window;
+
+  switch (msg->message)
+    {
+    case WM_SHOWWINDOW:
+      if (!msg->wParam && (msg->lParam == SW_PARENTCLOSING))
+	set_window_ownership (window, TRUE);
+      break;
+    case WM_DESTROY:
+      gdk_window_remove_filter (window, 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().
+       */
+      break;
+    }
+
+  return GDK_FILTER_CONTINUE;
+}
+
+void
 gdk_window_set_transient_for (GdkWindow *window, 
 			      GdkWindow *parent)
 {
-  HWND window_id, parent_id;
+  GdkWindowImplWin32 *impl;
+  gboolean is_minimized = FALSE;
 
   g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail ((parent == NULL) || GDK_IS_WINDOW (parent));
   
   GDK_NOTE (MISC, g_print ("gdk_window_set_transient_for: %p: %p\n",
 			   GDK_WINDOW_HWND (window),
@@ -1756,18 +1839,126 @@ gdk_window_set_transient_for (GdkWindow 
       return;
     }
   
-  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, owned_window_filter, 0);
+
+  impl = GDK_WINDOW_IMPL_WIN32 (GDK_WINDOW_OBJECT (window)->impl);
+
+  if (impl->owner_window != parent)
+    {
+      if (impl->owner_window != NULL)
+	{
+	  /* Remove the previous filter on the parent window */
+	  gdk_window_remove_filter (impl->owner_window, owner_window_filter, window);
+	}
+
+    impl->owner_window = parent;
+
+    if (parent != NULL)
+      {
+	/* Set a filter on the parent window so that when it's
+         * restored this window gets hooked up again.
+	 */
+	gdk_window_add_filter (parent, 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.
+   */
+  set_window_ownership (window, is_minimized);
+}
+
+/* Set window ownership based on the taskbar hint, whether it's
+ * transient and whether the owner is minimized.
+ *
+ * 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 ownership is restored by
+ * owner_window_filter() and set_window_ownership().
+ */
+static void
+set_window_ownership (GdkWindow* window,
+		      gboolean   is_owner_minimized)
+{
+  GdkWindowImplWin32 *impl;
+#if 1
+  static GdkWindow *owner = NULL;
+#endif
+  GdkWindowAttr wa;
+
+  impl = GDK_WINDOW_IMPL_WIN32 (((GdkWindowObject *) window)->impl);
+
+  if (!is_owner_minimized && 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))
+    {
+#if 1
+      /* Charles Reilly's original code */
+
+      /* 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) != 0)
+	    return;
+	}
+
+      if (owner == NULL)
+	{
+	  /* 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));
+#else
+      /* tml test? */
+      if (is_owner_minimized)
+	ShowWindow (GDK_WINDOW_HWND (window), SW_HIDE);
+#endif
 
-  /* 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");
+#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
+    {
+#if 1
+      /* Was commented out? tml test? */
+      SetWindowLong (GDK_WINDOW_HWND (window), GWL_HWNDPARENT, 0);
+#endif
+    }
 }
 
 void
@@ -2506,13 +2697,10 @@ gdk_window_set_icon_name (GdkWindow   *w
     return;
   
 #if 0
-  /* This is not the correct thing to do. We should keep both the
-   * "normal" window title, and the icon name. When the window is
-   * minimized, call SetWindowText() with the icon name, and when the
-   * window is restored, with the normal window title. Also, the name
-   * is in UTF-8, so we should do the normal conversion to either wide
-   * chars or system codepage, and use either the W or A version of
-   * SetWindowText(), depending on Windows version.
+  /* This is not the correct thing to do. The term "icon name" refers
+   * to the freedesktop.org icon naming convention, not any title or
+   * whatever to be displayed in association with the application
+   * icon.
    */
   API_CALL (SetWindowText, (GDK_WINDOW_HWND (window), name));
 #endif
@@ -3217,8 +3405,8 @@ void
 gdk_window_set_skip_taskbar_hint (GdkWindow *window,
 				  gboolean   skips_taskbar)
 {
-  static GdkWindow *owner = NULL;
-  GdkWindowAttr wa;
+  GdkWindowImplWin32 *impl;
+  gboolean is_minimized = FALSE;
 
   g_return_if_fail (GDK_IS_WINDOW (window));
 
@@ -3226,33 +3414,13 @@ gdk_window_set_skip_taskbar_hint (GdkWin
 			   GDK_WINDOW_HWND (window),
 			   skips_taskbar ? "TRUE" : "FALSE"));
 
-  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;
 
-      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));
 
-#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);
-    }
+  set_window_ownership (window, is_minimized);
 }
 
 void
