window state stuff



Hi,

Appended patch adds support for:

void     gtk_window_present       (GtkWindow *window);
void     gtk_window_iconify       (GtkWindow *window);
void     gtk_window_deiconify     (GtkWindow *window);
void     gtk_window_stick         (GtkWindow *window);
void     gtk_window_unstick       (GtkWindow *window);
void     gtk_window_maximize      (GtkWindow *window);
void     gtk_window_unmaximize    (GtkWindow *window);

and has GDK functions to support that. Also:

typedef enum
{
  GDK_WINDOW_STATE_WITHDRAWN = 1 << 0,
  GDK_WINDOW_STATE_ICONIFIED = 1 << 1,
  GDK_WINDOW_STATE_MAXIMIZED = 1 << 2,
  GDK_WINDOW_STATE_STICKY    = 1 << 3
} GdkWindowState;

struct _GdkEventWindowState
{
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
  GdkWindowState changed;
  GdkWindowState current;
};

Which gives you notification of (de)iconify, (un)stick, (un)maximize,
plus also (un)withdraw (map_event/unmap_event occur on
withdraw/unwithdraw, but also on iconification).

Iconification includes window shading and flipping to another desktop,
at least for Sawfish. So there's no separate states for shading, and
people might be a bit surprised when they get iconify notifications on
desktop flips.

gtk_window_present() is pretty much exactly the same as the Nautilus
version, except that it uses the WM spec focus stuff if available.

The ChangeLog is incomplete, I know.

Havoc

Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/gtk+/ChangeLog,v
retrieving revision 1.1751
diff -u -u -r1.1751 ChangeLog
--- ChangeLog	2001/02/23 23:00:49	1.1751
+++ ChangeLog	2001/02/24 00:43:42
@@ -1,3 +1,11 @@
+2001-02-21  Havoc Pennington  <hp redhat com>
+	
+	* gdk/x11/gdkwindow-x11.c (gdk_window_focus): new function,
+	focuses a window
+
+	* gdk/x11/gdkmain-x11.c (_gdk_wmspec_supported): new function, 
+	finds out if we support a given WM spec hint
+
 Fri Feb 23 17:50:13 2001  Jonathan Blandford  <jrb redhat com>
 
 	* gtk/gtktreeview.c (gtk_tree_view_bin_expose): Only draw the
Index: gdk/gdkevents.c
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/gdkevents.c,v
retrieving revision 1.33
diff -u -u -r1.33 gdkevents.c
--- gdk/gdkevents.c	2000/12/18 19:12:34	1.33
+++ gdk/gdkevents.c	2001/02/24 00:43:43
@@ -454,6 +454,7 @@
       case GDK_EXPOSE:
       case GDK_MAP:
       case GDK_UNMAP:
+      case GDK_WINDOW_STATE:
         /* return current time */
         break;
       }
@@ -529,6 +530,7 @@
       case GDK_EXPOSE:
       case GDK_MAP:
       case GDK_UNMAP:
+      case GDK_WINDOW_STATE:
         /* no state field */
         break;
       }
@@ -803,3 +805,56 @@
       button_number[0] = event->button.button;
     }
 }
+
+
+void
+gdk_synthesize_window_state (GdkWindow     *window,
+                             GdkWindowState unset_flags,
+                             GdkWindowState set_flags)
+{
+  GdkEventWindowState temp_event;
+  GdkWindowState old;
+  
+  g_return_if_fail (window != NULL);
+  
+  temp_event.window = window;
+  temp_event.type = GDK_WINDOW_STATE;
+  temp_event.send_event = FALSE;
+  
+  old = ((GdkWindowObject*)temp_event.window)->state;
+  
+  temp_event.changed = (unset_flags | set_flags) ^ old;
+  temp_event.current = old;
+  temp_event.current |= set_flags;
+  temp_event.current &= ~unset_flags;
+
+  if (temp_event.current == old)
+    return; /* No actual work to do, nothing changed. */
+
+  /* Actually update the field in GdkWindow, this is sort of an odd
+   * place to do it, but seems like the safest since it ensures we expose no
+   * inconsistent state.
+   */
+  
+  ((GdkWindowObject*)window)->state = temp_event.current;
+
+  /* We only really send the event to toplevels, since
+   * all the window states don't apply to non-toplevels.
+   * Non-toplevels do use the GDK_WINDOW_STATE_WITHDRAWN flag
+   * internally so we needed to update window->state.
+   */
+  switch (((GdkWindowObject*)window)->window_type)
+    {
+    case GDK_WINDOW_TOPLEVEL:
+    case GDK_WINDOW_DIALOG:
+    case GDK_WINDOW_TEMP: /* ? */
+      gdk_event_put ((GdkEvent*)&temp_event);
+      break;
+      
+    case GDK_WINDOW_FOREIGN:
+    case GDK_WINDOW_ROOT:
+    case GDK_WINDOW_CHILD:
+      break;
+    }
+}
+
Index: gdk/gdkevents.h
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/gdkevents.h,v
retrieving revision 1.11
diff -u -u -r1.11 gdkevents.h
--- gdk/gdkevents.h	2000/12/17 23:49:58	1.11
+++ gdk/gdkevents.h	2001/02/24 00:43:43
@@ -28,8 +28,8 @@
 typedef struct _GdkEventSelection   GdkEventSelection;
 typedef struct _GdkEventProximity   GdkEventProximity;
 typedef struct _GdkEventClient	    GdkEventClient;
-
 typedef struct _GdkEventDND         GdkEventDND;
+typedef struct _GdkEventWindowState GdkEventWindowState;
 
 typedef union  _GdkEvent	    GdkEvent;
 
@@ -110,7 +110,8 @@
   GDK_CLIENT_EVENT	= 28,
   GDK_VISIBILITY_NOTIFY = 29,
   GDK_NO_EXPOSE		= 30,
-  GDK_SCROLL            = 31
+  GDK_SCROLL            = 31,
+  GDK_WINDOW_STATE      = 32
 } GdkEventType;
 
 /* Event masks. (Used to select what types of events a window
@@ -193,6 +194,14 @@
   GDK_PROPERTY_DELETE
 } GdkPropertyState;
 
+typedef enum
+{
+  GDK_WINDOW_STATE_WITHDRAWN = 1 << 0,
+  GDK_WINDOW_STATE_ICONIFIED = 1 << 1,
+  GDK_WINDOW_STATE_MAXIMIZED = 1 << 2,
+  GDK_WINDOW_STATE_STICKY    = 1 << 3
+} GdkWindowState;
+
 struct _GdkEventAny
 {
   GdkEventType type;
@@ -366,6 +375,16 @@
   } data;
 };
 
+
+struct _GdkEventWindowState
+{
+  GdkEventType type;
+  GdkWindow *window;
+  gint8 send_event;
+  GdkWindowState changed;
+  GdkWindowState current;
+};
+
 /* Event types for DND */
 
 struct _GdkEventDND {
@@ -397,6 +416,7 @@
   GdkEventProximity	    proximity;
   GdkEventClient	    client;
   GdkEventDND               dnd;
+  GdkEventWindowState       window_state;
 };
 
 gboolean  gdk_events_pending	 	(void);
Index: gdk/gdkinternals.h
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/gdkinternals.h,v
retrieving revision 1.6
diff -u -u -r1.6 gdkinternals.h
--- gdk/gdkinternals.h	2000/08/30 00:33:35	1.6
+++ gdk/gdkinternals.h	2001/02/24 00:43:43
@@ -117,6 +117,9 @@
 void   gdk_event_queue_append      (GdkEvent *event);
 
 void gdk_event_button_generate (GdkEvent *event);
+void gdk_synthesize_window_state (GdkWindow     *window,
+                                  GdkWindowState unset_flags,
+                                  GdkWindowState set_flags);
 
 /*************************************
  * Interfaces used by windowing code *
@@ -162,6 +165,8 @@
 						 gint        y,
 						 gint        width,
 						 gint        height);
+
+#define GDK_WINDOW_IS_MAPPED(window) ((((GdkWindowObject*)window)->state & GDK_WINDOW_STATE_WITHDRAWN) == 0)
 
 /* Called before processing updates for a window. This gives the windowing
  * layer a chance to save the region for later use in avoiding duplicate
Index: gdk/gdkwindow.c
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/gdkwindow.c,v
retrieving revision 1.108
diff -u -u -r1.108 gdkwindow.c
--- gdk/gdkwindow.c	2001/02/20 05:21:44	1.108
+++ gdk/gdkwindow.c	2001/02/24 00:43:43
@@ -188,6 +188,8 @@
 
   window->window_type = GDK_WINDOW_CHILD;
 
+  window->state = GDK_WINDOW_STATE_WITHDRAWN;
+  
   window->impl = g_object_new (_gdk_window_impl_get_type (), NULL);
 }
 
@@ -290,7 +292,7 @@
     case GDK_WINDOW_FOREIGN:
       if (!GDK_WINDOW_DESTROYED (window))
 	{
-	  private->mapped = FALSE;
+	  private->state |= GDK_WINDOW_STATE_WITHDRAWN;
 	  private->destroyed = TRUE;
 	  
 	  _gdk_windowing_window_destroy (window, recursing, foreign_destroy);
@@ -573,12 +575,9 @@
 gboolean 
 gdk_window_is_visible (GdkWindow *window)
 {
-  GdkWindowObject *private = (GdkWindowObject *)window;
-  
-  g_return_val_if_fail (window != NULL, FALSE);
   g_return_val_if_fail (GDK_IS_WINDOW (window), FALSE);
   
-  return private->mapped;
+  return GDK_WINDOW_IS_MAPPED (window);
 }
 
 /*************************************************************
@@ -605,7 +604,7 @@
 	 (private != (GdkWindowObject *)gdk_parent_root) &&
 	 (GDK_WINDOW_TYPE (private) != GDK_WINDOW_FOREIGN))
     {
-      if (!private->mapped)
+      if (!GDK_WINDOW_IS_MAPPED (window))
 	return FALSE;
       
       private = (GdkWindowObject *)private->parent;
@@ -615,6 +614,25 @@
 }
 
 /**
+ * gdk_window_get_state:
+ * @window: a #GdkWindow
+ * 
+ * Gets the bitwise OR of the currently active window state flags,
+ * from the #GdkWindowState enumeration.
+ * 
+ * Return value: window state bitfield
+ **/
+GdkWindowState
+gdk_window_get_state (GdkWindow *window)
+{
+  GdkWindowObject *private = (GdkWindowObject *)window;
+  
+  g_return_val_if_fail (GDK_IS_WINDOW (window), FALSE);
+  
+  return private->state;
+}
+
+/**
  * gdk_window_begin_paint_rect:
  * @window: a #GdkWindow
  * @rectangle: rectangle you intend to draw to
@@ -1871,7 +1889,7 @@
   if (GDK_WINDOW_DESTROYED (window))
     return;
   
-  if (private->input_only || !private->mapped)
+  if (private->input_only || !GDK_WINDOW_IS_MAPPED (window))
     return;
 
   if (!rect)
@@ -1927,7 +1945,7 @@
   if (GDK_WINDOW_DESTROYED (window))
     return;
   
-  if (private->input_only || !private->mapped)
+  if (private->input_only || !GDK_WINDOW_IS_MAPPED (window))
     return;
 
   visible_region = gdk_drawable_get_visible_region (window);
Index: gdk/gdkwindow.h
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/gdkwindow.h,v
retrieving revision 1.15
diff -u -u -r1.15 gdkwindow.h
--- gdk/gdkwindow.h	2001/01/26 21:11:57	1.15
+++ gdk/gdkwindow.h	2001/02/24 00:43:43
@@ -199,7 +199,9 @@
   guint8 window_type;
   guint8 depth;
   guint8 resize_count;
-  guint mapped : 1;
+
+  GdkWindowState state;
+  
   guint guffaw_gravity : 1;
   guint input_only : 1;
   
@@ -254,6 +256,8 @@
                                                 gint           height);
 void          gdk_window_raise                 (GdkWindow     *window);
 void          gdk_window_lower                 (GdkWindow     *window);
+void          gdk_window_focus                 (GdkWindow     *window,
+                                                guint32        timestamp);
 void          gdk_window_set_user_data         (GdkWindow     *window,
                                                 gpointer       user_data);
 void          gdk_window_set_override_redirect (GdkWindow     *window,
@@ -305,6 +309,8 @@
 gboolean gdk_window_is_visible     (GdkWindow *window);
 gboolean gdk_window_is_viewable    (GdkWindow *window);
 
+GdkWindowState gdk_window_get_state (GdkWindow *window);
+
 /* Set static bit gravity on the parent, and static
  * window gravity on all children.
  */
@@ -393,7 +399,13 @@
 void	      gdk_window_set_functions	 (GdkWindow	  *window,
 					  GdkWMFunction	   functions);
 GList *       gdk_window_get_toplevels   (void);
+
 void          gdk_window_iconify         (GdkWindow       *window);
+void          gdk_window_deiconify       (GdkWindow       *window);
+void          gdk_window_stick           (GdkWindow       *window);
+void          gdk_window_unstick         (GdkWindow       *window);
+void          gdk_window_maximize        (GdkWindow       *window);
+void          gdk_window_unmaximize      (GdkWindow       *window);
 
 void          gdk_window_register_dnd    (GdkWindow       *window);
 
Index: gdk/x11/gdkevents-x11.c
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/x11/gdkevents-x11.c,v
retrieving revision 1.37
diff -u -u -r1.37 gdkevents-x11.c
--- gdk/x11/gdkevents-x11.c	2001/02/02 17:52:07	1.37
+++ gdk/x11/gdkevents-x11.c	2001/02/24 00:43:43
@@ -44,6 +44,8 @@
 #include <X11/XKBlib.h>
 #endif
 
+#include <X11/Xatom.h>
+
 typedef struct _GdkIOClosure GdkIOClosure;
 typedef struct _GdkEventPrivate GdkEventPrivate;
 
@@ -118,8 +120,10 @@
   gdk_event_dispatch,
   NULL
 };
+
+static GPollFD event_poll_fd;
 
-GPollFD event_poll_fd;
+static Window wmspec_check_window = None;
 
 /*********************************************
  * Functions for maintaining the event queue *
@@ -260,6 +264,96 @@
   client_filters = g_list_prepend (client_filters, filter);
 }
 
+static GdkAtom wm_state_atom = 0;
+
+static void
+gdk_check_wm_state_changed (GdkWindow *window)
+{  
+  Atom type;
+  gint format;
+  gulong nitems;
+  gulong bytes_after;
+  GdkAtom *atoms = NULL;
+  gulong i;
+  GdkAtom sticky_atom;
+  GdkAtom maxvert_atom;
+  GdkAtom maxhorz_atom;
+  gboolean found_sticky, found_maxvert, found_maxhorz;
+  GdkWindowState old_state;
+  
+  if (GDK_WINDOW_DESTROYED (window))
+    return;
+  
+  if (wm_state_atom == 0)
+    wm_state_atom = gdk_atom_intern ("_NET_WM_STATE", FALSE);
+  
+  XGetWindowProperty (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XID (window),
+		      wm_state_atom, 0, G_MAXLONG,
+		      False, XA_ATOM, &type, &format, &nitems,
+		      &bytes_after, (guchar **)&atoms);
+
+  if (type == None)
+    return;
+
+  sticky_atom = gdk_atom_intern ("_NET_WM_STATE_STICKY", FALSE);
+  maxvert_atom = gdk_atom_intern ("_NET_WM_STATE_MAXIMIZED_VERT", FALSE);
+  maxhorz_atom = gdk_atom_intern ("_NET_WM_STATE_MAXIMIZED_HORZ", FALSE);    
+
+  found_sticky = FALSE;
+  found_maxvert = FALSE;
+  found_maxhorz = FALSE;
+  
+  i = 0;
+  while (i < nitems)
+    {
+      if (atoms[i] == sticky_atom)
+        found_sticky = TRUE;
+      else if (atoms[i] == maxvert_atom)
+        found_maxvert = TRUE;
+      else if (atoms[i] == maxhorz_atom)
+        found_maxhorz = TRUE;
+
+      ++i;
+    }
+
+  XFree (atoms);
+  
+  old_state = gdk_window_get_state (window);
+
+  if (old_state & GDK_WINDOW_STATE_STICKY)
+    {
+      if (!found_sticky)
+        gdk_synthesize_window_state (window,
+                                     GDK_WINDOW_STATE_STICKY,
+                                     0);
+    }
+  else
+    {
+      if (found_sticky)
+        gdk_synthesize_window_state (window,
+                                     0,
+                                     GDK_WINDOW_STATE_STICKY);
+    }
+
+  /* Our "maximized" means both vertical and horizontal; if only one,
+   * we don't expose that via GDK
+   */
+  if (old_state & GDK_WINDOW_STATE_MAXIMIZED)
+    {
+      if (!(found_maxvert && found_maxhorz))
+        gdk_synthesize_window_state (window,
+                                     GDK_WINDOW_STATE_MAXIMIZED,
+                                     0);
+    }
+  else
+    {
+      if (found_maxvert && found_maxhorz)
+        gdk_synthesize_window_state (window,
+                                     0,
+                                     GDK_WINDOW_STATE_MAXIMIZED);
+    }
+}
+
 static gint
 gdk_event_translate (GdkEvent *event,
 		     XEvent   *xevent,
@@ -306,6 +400,19 @@
   
   if (window != NULL)
     gdk_window_ref (window);
+
+  if (wmspec_check_window != None &&
+      xevent->xany.window == wmspec_check_window)
+    {
+      if (xevent->type == DestroyNotify)
+        wmspec_check_window = None;
+      
+      /* Eat events on this window unless someone had wrapped
+       * it as a foreign window
+       */
+      if (window == NULL)
+        return FALSE;
+    }
   
   event->any.window = window;
   event->any.send_event = xevent->xany.send_event ? TRUE : FALSE;
@@ -948,7 +1055,17 @@
 			   xevent->xmap.window));
       
       event->any.type = GDK_UNMAP;
-      event->any.window = window;
+      event->any.window = window;      
+
+      /* If we are shown (not withdrawn) and get an unmap, it means we
+       * were iconified in the X sense. If we are withdrawn, and get
+       * an unmap, it means we hid the window ourselves, so we
+       * will have already flipped the iconified bit off.
+       */
+      if (GDK_WINDOW_IS_MAPPED (window))
+        gdk_synthesize_window_state (window,
+                                     0,
+                                     GDK_WINDOW_STATE_ICONIFIED);
       
       if (gdk_xgrab_window == window_private)
 	gdk_xgrab_window = NULL;
@@ -962,6 +1079,12 @@
       
       event->any.type = GDK_MAP;
       event->any.window = window;
+
+      /* Unset iconified if it was set */
+      if (((GdkWindowObject*)window)->state & GDK_WINDOW_STATE_ICONIFIED)
+        gdk_synthesize_window_state (window,
+                                     GDK_WINDOW_STATE_ICONIFIED,
+                                     0);
       
       break;
       
@@ -1064,6 +1187,15 @@
       event->property.atom = xevent->xproperty.atom;
       event->property.time = xevent->xproperty.time;
       event->property.state = xevent->xproperty.state;
+
+      if (wm_state_atom == 0)
+        wm_state_atom = gdk_atom_intern ("_NET_WM_STATE", FALSE);
+      
+      if (event->property.atom == wm_state_atom)
+        {
+          /* If window state changed, then synthesize those events. */
+          gdk_check_wm_state_changed (event->property.window);
+        }
       
       break;
       
@@ -1585,4 +1717,89 @@
 
   return xevent.xproperty.time;
 }
+
+
+gboolean
+_gdk_wmspec_supported (GdkAtom property)
+{
+  static GdkAtom wmspec_check_atom = 0;
+  static GdkAtom wmspec_supported_atom = 0;
+  static GdkAtom *atoms = NULL;
+  static gulong n_atoms = 0;
+  Atom type;
+  gint format;
+  gulong nitems;
+  gulong bytes_after;
+  Window xwindow;
+  gulong i;
+
+  if (wmspec_check_window != None)
+    {
+      if (atoms == NULL)
+        return FALSE;
+
+      i = 0;
+      while (i < n_atoms)
+        {
+          if (atoms[i] == property)
+            return TRUE;
+          
+          ++i;
+        }
+
+      return FALSE;
+    }
+
+  if (atoms)
+    XFree (atoms);
+
+  atoms = NULL;
+  n_atoms = 0;
+  
+  /* This function is very slow on every call if you are not running a
+   * spec-supporting WM. For now not optimized, because it isn't in
+   * any critical code paths, but if you used it somewhere that had to
+   * be fast you want to avoid "GTK is slow with old WMs" complaints.
+   */
+  
+  if (wmspec_check_atom == 0)
+    wmspec_check_atom = gdk_atom_intern ("_NET_SUPPORTING_WM_CHECK", FALSE);
+      
+  if (wmspec_supported_atom == 0)
+    wmspec_supported_atom = gdk_atom_intern ("_NET_SUPPORTED", FALSE);
+  
+  XGetWindowProperty (gdk_display, gdk_root_window,
+		      wmspec_check_atom, 0, G_MAXLONG,
+		      False, XA_WINDOW, &type, &format, &nitems,
+		      &bytes_after, (guchar **)&xwindow);
+
+  if (type == None)
+    return FALSE;
+
+  gdk_error_trap_push ();
+
+  /* Find out if this WM goes away, so we can reset everything. */
+  XSelectInput (gdk_display, xwindow,
+                StructureNotifyMask);
+
+  if (gdk_error_trap_pop ())
+    return FALSE;
+
+  gdk_error_trap_push ();
+  
+  XGetWindowProperty (gdk_display, xwindow,
+		      wmspec_check_atom, 0, G_MAXLONG,
+		      False, XA_ATOM, &type, &format, &n_atoms,
+		      &bytes_after, (guchar **)&atoms);
+
+  if (gdk_error_trap_pop ())
+    return FALSE;
+  
+  wmspec_check_window = xwindow;
+
+  /* since wmspec_check_window != None this isn't infinite. ;-) */
+  return _gdk_wmspec_supported (property);
+}
+
+
 
Index: gdk/x11/gdkgeometry-x11.c
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/x11/gdkgeometry-x11.c,v
retrieving revision 1.7
diff -u -u -r1.7 gdkgeometry-x11.c
--- gdk/x11/gdkgeometry-x11.c	2001/01/12 23:23:39	1.7
+++ gdk/x11/gdkgeometry-x11.c	2001/02/24 00:43:43
@@ -28,6 +28,7 @@
 #include "gdkprivate-x11.h"
 #include "gdkx.h"
 #include "gdkregion.h"
+#include "gdkinternals.h"
 
 typedef struct _GdkWindowQueueItem GdkWindowQueueItem;
 typedef struct _GdkWindowParentPos GdkWindowParentPos;
@@ -332,7 +333,7 @@
       if (impl->position_info.no_bg)
 	gdk_window_tmp_reset_bg (window);
 
-      if (!impl->position_info.mapped && new_info.mapped && obj->mapped)
+      if (!impl->position_info.mapped && new_info.mapped && GDK_WINDOW_IS_MAPPED (obj))
 	XMapWindow (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XID (window));
       
       impl->position_info = new_info;
@@ -378,7 +379,7 @@
       if (impl->position_info.no_bg)
 	gdk_window_tmp_reset_bg (window);
 
-      if (!impl->position_info.mapped && new_info.mapped && obj->mapped)
+      if (!impl->position_info.mapped && new_info.mapped && GDK_WINDOW_IS_MAPPED (obj))
 	XMapWindow (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XID (window));
 
       impl->position_info = new_info;
@@ -651,7 +652,7 @@
 			 new_info.x, new_info.y, new_info.width, new_info.height);
     }
 
-  if (!impl->position_info.mapped && new_info.mapped && obj->mapped)
+  if (!impl->position_info.mapped && new_info.mapped && GDK_WINDOW_IS_MAPPED (obj))
     XMapWindow (GDK_DRAWABLE_XDISPLAY (window), GDK_DRAWABLE_XID (window));
 
   if (impl->position_info.no_bg)
Index: gdk/x11/gdkmain-x11.c
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/x11/gdkmain-x11.c,v
retrieving revision 1.124
diff -u -u -r1.124 gdkmain-x11.c
--- gdk/x11/gdkmain-x11.c	2001/02/19 20:38:15	1.124
+++ gdk/x11/gdkmain-x11.c	2001/02/24 00:43:43
@@ -737,4 +737,3 @@
   
   return result && !gdk_error_code;
 }
-
Index: gdk/x11/gdkprivate-x11.h
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/x11/gdkprivate-x11.h,v
retrieving revision 1.8
diff -u -u -r1.8 gdkprivate-x11.h
--- gdk/x11/gdkprivate-x11.h	2000/12/15 01:46:40	1.8
+++ gdk/x11/gdkprivate-x11.h	2001/02/24 00:43:43
@@ -86,6 +86,8 @@
 void     _gdk_selection_window_destroyed   (GdkWindow            *window);
 gboolean _gdk_selection_filter_clear_event (XSelectionClearEvent *event);
 
+gboolean _gdk_wmspec_supported (GdkAtom property);
+
 extern GdkDrawableClass  _gdk_x11_drawable_class;
 extern gboolean	         gdk_use_xshm;
 extern Atom		 gdk_wm_delete_window;
Index: gdk/x11/gdkwindow-x11.c
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/x11/gdkwindow-x11.c,v
retrieving revision 1.105
diff -u -u -r1.105 gdkwindow-x11.c
--- gdk/x11/gdkwindow-x11.c	2001/02/03 16:31:35	1.105
+++ gdk/x11/gdkwindow-x11.c	2001/02/24 00:43:43
@@ -635,7 +635,12 @@
   impl->height = attrs.height;
   private->window_type = GDK_WINDOW_FOREIGN;
   private->destroyed = FALSE;
-  private->mapped = (attrs.map_state != IsUnmapped);
+
+  if (attrs.map_state == IsUnmapped)
+    private->state = GDK_WINDOW_STATE_WITHDRAWN;
+  else
+    private->state = 0;
+
   private->depth = attrs.depth;
   
   gdk_drawable_ref (window);
@@ -709,6 +714,63 @@
   gdk_drawable_unref (window);
 }
 
+static void
+set_initial_hints (GdkWindow *window)
+{
+  GdkWindowObject *private;
+  GdkAtom atoms[5];
+  gint i;
+  
+  private = (GdkWindowObject*) window;
+
+  if (private->state & GDK_WINDOW_STATE_ICONIFIED)
+    {
+      XWMHints *wm_hints;
+      
+      wm_hints = XGetWMHints (GDK_WINDOW_XDISPLAY (window),
+                              GDK_WINDOW_XID (window));
+      if (!wm_hints)
+        wm_hints = XAllocWMHints ();
+
+      wm_hints->flags |= StateHint;
+      wm_hints->initial_state = IconicState;
+      
+      XSetWMHints (GDK_WINDOW_XDISPLAY (window),
+                   GDK_WINDOW_XID (window), wm_hints);
+      XFree (wm_hints);
+    }
+
+  /* We set the spec hints regardless of whether the spec is supported,
+   * since it can't hurt and it's kind of expensive to check whether
+   * it's supported.
+   */
+  
+  i = 0;
+
+  if (private->state & GDK_WINDOW_STATE_MAXIMIZED)
+    {
+      atoms[i] = gdk_atom_intern ("_NET_WM_STATE_MAXIMIZED_VERT", FALSE);
+      ++i;
+      atoms[i] = gdk_atom_intern ("_NET_WM_STATE_MAXIMIZED_HORZ", FALSE);
+      ++i;
+    }
+
+  if (private->state & GDK_WINDOW_STATE_STICKY)
+    {
+      atoms[i] = gdk_atom_intern ("_NET_WM_STATE_STICKY", FALSE);
+      ++i;
+    }
+
+  if (i > 0)
+    {
+      XChangeProperty (GDK_WINDOW_XDISPLAY (window),
+                       GDK_WINDOW_XID (window),
+                       gdk_atom_intern ("_NET_WM_STATE", FALSE),
+                       XA_ATOM, 32, PropModeReplace,
+                       (guchar*) atoms, i);
+    }
+}
+
 void
 gdk_window_show (GdkWindow *window)
 {
@@ -719,13 +781,23 @@
   private = (GdkWindowObject*) window;
   if (!private->destroyed)
     {
-      private->mapped = TRUE;
       XRaiseWindow (GDK_WINDOW_XDISPLAY (window),
 		    GDK_WINDOW_XID (window));
+
+      if (!GDK_WINDOW_IS_MAPPED (window))
+        {
+          set_initial_hints (window);
+          
+          gdk_synthesize_window_state (window,
+                                       GDK_WINDOW_STATE_WITHDRAWN,
+                                       0);
+        }
       
+      g_assert (GDK_WINDOW_IS_MAPPED (window));
+      
       if (GDK_WINDOW_IMPL_X11 (private->impl)->position_info.mapped)
-	XMapWindow (GDK_WINDOW_XDISPLAY (window),
-		    GDK_WINDOW_XID (window));
+        XMapWindow (GDK_WINDOW_XDISPLAY (window),
+                    GDK_WINDOW_XID (window));
     }
 }
 
@@ -737,14 +809,36 @@
   g_return_if_fail (window != NULL);
 
   private = (GdkWindowObject*) window;
+
+  /* You can't simply unmap toplevel windows. */
+  switch (private->window_type)
+    {
+    case GDK_WINDOW_TOPLEVEL:
+    case GDK_WINDOW_DIALOG:
+    case GDK_WINDOW_TEMP: /* ? */
+      gdk_window_withdraw (window);
+      return;
+      break;
+      
+    case GDK_WINDOW_FOREIGN:
+    case GDK_WINDOW_ROOT:
+    case GDK_WINDOW_CHILD:
+      break;
+    }
+  
   if (!private->destroyed)
     {
-      private->mapped = FALSE;
+      if (GDK_WINDOW_IS_MAPPED (window))
+        gdk_synthesize_window_state (window,
+                                     0,
+                                     GDK_WINDOW_STATE_WITHDRAWN);
 
+      g_assert (!GDK_WINDOW_IS_MAPPED (window));
+      
       _gdk_window_clear_update_area (window);
       
       XUnmapWindow (GDK_WINDOW_XDISPLAY (window),
-		    GDK_WINDOW_XID (window));
+                    GDK_WINDOW_XID (window));
     }
 }
 
@@ -757,8 +851,17 @@
   
   private = (GdkWindowObject*) window;
   if (!private->destroyed)
-    XWithdrawWindow (GDK_WINDOW_XDISPLAY (window),
-		     GDK_WINDOW_XID (window), 0);
+    {
+      if (GDK_WINDOW_IS_MAPPED (window))
+        gdk_synthesize_window_state (window,
+                                     0,
+                                     GDK_WINDOW_STATE_WITHDRAWN);
+
+      g_assert (!GDK_WINDOW_IS_MAPPED (window));
+      
+      XWithdrawWindow (GDK_WINDOW_XDISPLAY (window),
+                       GDK_WINDOW_XID (window), 0);
+    }
 }
 
 void
@@ -945,6 +1048,41 @@
 }
 
 void
+gdk_window_focus (GdkWindow *window,
+                  guint32    timestamp)
+{
+  g_return_if_fail (GDK_IS_WINDOW (window));
+
+  if (GDK_WINDOW_DESTROYED (window))
+    return;
+  
+  if (_gdk_wmspec_supported (gdk_atom_intern ("_NET_ACTIVE_WINDOW", FALSE)))
+    {
+      XEvent xev;
+
+      xev.xclient.type = ClientMessage;
+      xev.xclient.serial = 0;
+      xev.xclient.send_event = True;
+      xev.xclient.window = GDK_WINDOW_XWINDOW (window);
+      xev.xclient.display = gdk_display;
+      xev.xclient.message_type = gdk_atom_intern ("_NET_ACTIVE_WINDOW", FALSE);
+      xev.xclient.format = 32;
+      xev.xclient.data.l[0] = 0;
+      
+      XSendEvent (gdk_display, gdk_root_window, False,
+                  SubstructureRedirectMask | SubstructureNotifyMask,
+                  &xev);
+    }
+  else
+    {
+      XSetInputFocus (GDK_DISPLAY (),
+                      GDK_WINDOW_XWINDOW (window),
+                      RevertToNone,
+                      timestamp);
+    }
+}
+
+void
 gdk_window_set_hints (GdkWindow *window,
 		      gint       x,
 		      gint       y,
@@ -1807,12 +1945,13 @@
 		      GUINT_TO_POINTER (TRUE));
 
   set_text_property (window, gdk_atom_intern ("WM_ICON_NAME", FALSE), name);
-}  
+}
 
 void
 gdk_window_iconify (GdkWindow *window)
 {
   Display *display;
+  GdkWindowObject *private;
   
   g_return_if_fail (window != NULL);
   g_return_if_fail (GDK_IS_WINDOW (window));
@@ -1821,7 +1960,229 @@
     return;
 
   display = GDK_WINDOW_XDISPLAY (window);
-  XIconifyWindow (display, GDK_WINDOW_XWINDOW (window), DefaultScreen (display));
+
+  private = (GdkWindowObject*) window;
+
+  if (GDK_WINDOW_IS_MAPPED (window))
+    {  
+      XIconifyWindow (display, GDK_WINDOW_XWINDOW (window), DefaultScreen (display));
+
+    }
+  else
+    {
+      /* Flip our client side flag, the real work happens on map. */
+      gdk_synthesize_window_state (window,
+                                   0,
+                                   GDK_WINDOW_STATE_ICONIFIED);
+    }
+}
+
+void
+gdk_window_deiconify (GdkWindow *window)
+{
+  Display *display;
+  GdkWindowObject *private;
+  
+  g_return_if_fail (window != NULL);
+  g_return_if_fail (GDK_IS_WINDOW (window));
+
+  if (GDK_WINDOW_DESTROYED (window))
+    return;
+
+  display = GDK_WINDOW_XDISPLAY (window);
+
+  private = (GdkWindowObject*) window;
+
+  if (GDK_WINDOW_IS_MAPPED (window))
+    {  
+      gdk_window_show (window);
+    }
+  else
+    {
+      /* Flip our client side flag, the real work happens on map. */
+      gdk_synthesize_window_state (window,
+                                   GDK_WINDOW_STATE_ICONIFIED,
+                                   0);
+    }
+}
+
+void
+gdk_window_stick (GdkWindow *window)
+{
+  g_return_if_fail (GDK_IS_WINDOW (window));
+
+  if (GDK_WINDOW_DESTROYED (window))
+    return;
+
+  if (GDK_WINDOW_IS_MAPPED (window))
+    {
+      if (_gdk_wmspec_supported (gdk_atom_intern ("_NET_WM_STATE", FALSE)))
+        {
+          XEvent xev;
+
+          xev.xclient.type = ClientMessage;
+          xev.xclient.serial = 0;
+          xev.xclient.send_event = True;
+          xev.xclient.window = GDK_WINDOW_XWINDOW (window);
+          xev.xclient.display = gdk_display;
+          xev.xclient.message_type = gdk_atom_intern ("_NET_WM_STATE", FALSE);
+          xev.xclient.format = 32;
+
+          xev.xclient.data.l[0] = gdk_atom_intern ("_NET_WM_STATE_ADD", FALSE);
+          xev.xclient.data.l[1] = gdk_atom_intern ("_NET_WM_STATE_STICKY", FALSE);
+          xev.xclient.data.l[2] = 0;
+      
+          XSendEvent (gdk_display, gdk_root_window, False,
+                      SubstructureRedirectMask | SubstructureNotifyMask,
+                      &xev);
+        }
+      else
+        {
+          /* Nothing we can do to emulate this without the WM spec,
+           * I don't think.
+           */          
+        }
+    }
+  else
+    {
+      /* Flip our client side flag, the real work happens on map. */
+      gdk_synthesize_window_state (window,
+                                   0,
+                                   GDK_WINDOW_STATE_STICKY);
+    }
+}
+
+void
+gdk_window_unstick (GdkWindow *window)
+{
+  g_return_if_fail (GDK_IS_WINDOW (window));
+
+  if (GDK_WINDOW_DESTROYED (window))
+    return;
+
+  if (GDK_WINDOW_IS_MAPPED (window))
+    {
+      if (_gdk_wmspec_supported (gdk_atom_intern ("_NET_WM_STATE", FALSE)))
+        {
+          XEvent xev;
+
+          xev.xclient.type = ClientMessage;
+          xev.xclient.serial = 0;
+          xev.xclient.send_event = True;
+          xev.xclient.window = GDK_WINDOW_XWINDOW (window);
+          xev.xclient.display = gdk_display;
+          xev.xclient.message_type = gdk_atom_intern ("_NET_WM_STATE", FALSE);
+          xev.xclient.format = 32;
+
+          xev.xclient.data.l[0] = gdk_atom_intern ("_NET_WM_STATE_REMOVE", FALSE);
+          xev.xclient.data.l[1] = gdk_atom_intern ("_NET_WM_STATE_STICKY", FALSE);
+          xev.xclient.data.l[2] = 0;
+      
+          XSendEvent (gdk_display, gdk_root_window, False,
+                      SubstructureRedirectMask | SubstructureNotifyMask,
+                      &xev);
+        }
+      else
+        {
+          /* Nothing we can do to emulate this without the WM spec,
+           * I don't think.
+           */
+        }
+    }
+  else
+    {
+      /* Flip our client side flag, the real work happens on map. */
+      gdk_synthesize_window_state (window,
+                                   GDK_WINDOW_STATE_STICKY,
+                                   0);
+
+    }
+}
+
+void
+gdk_window_maximize (GdkWindow *window)
+{
+  g_return_if_fail (GDK_IS_WINDOW (window));
+
+  if (GDK_WINDOW_DESTROYED (window))
+    return;
+
+  if (GDK_WINDOW_IS_MAPPED (window))
+    {
+      if (_gdk_wmspec_supported (gdk_atom_intern ("_NET_WM_STATE", FALSE)))
+        {
+          XEvent xev;
+
+          xev.xclient.type = ClientMessage;
+          xev.xclient.serial = 0;
+          xev.xclient.send_event = True;
+          xev.xclient.window = GDK_WINDOW_XWINDOW (window);
+          xev.xclient.display = gdk_display;
+          xev.xclient.message_type = gdk_atom_intern ("_NET_WM_STATE", FALSE);
+          xev.xclient.format = 32;
+
+          xev.xclient.data.l[0] = gdk_atom_intern ("_NET_WM_STATE_ADD", FALSE);
+          xev.xclient.data.l[1] = gdk_atom_intern ("_NET_WM_STATE_MAXIMIZED_VERT", FALSE);
+          xev.xclient.data.l[2] = gdk_atom_intern ("_NET_WM_STATE_MAXIMIZED_HORZ", FALSE);
+      
+          XSendEvent (gdk_display, gdk_root_window, False,
+                      SubstructureRedirectMask | SubstructureNotifyMask,
+                      &xev);
+        }
+      else
+        {
+          /* FIXME try to emulate maximization somehow? */
+        }
+    }
+  else
+    {
+      gdk_synthesize_window_state (window,
+                                   0,
+                                   GDK_WINDOW_STATE_MAXIMIZED);
+    }
+}
+
+void
+gdk_window_unmaximize (GdkWindow *window)
+{
+  g_return_if_fail (GDK_IS_WINDOW (window));
+
+  if (GDK_WINDOW_DESTROYED (window))
+    return;
+
+  if (GDK_WINDOW_IS_MAPPED (window))
+    {
+      if (_gdk_wmspec_supported (gdk_atom_intern ("_NET_WM_STATE", FALSE)))
+        {
+          XEvent xev;
+
+          xev.xclient.type = ClientMessage;
+          xev.xclient.serial = 0;
+          xev.xclient.send_event = True;
+          xev.xclient.window = GDK_WINDOW_XWINDOW (window);
+          xev.xclient.display = gdk_display;
+          xev.xclient.message_type = gdk_atom_intern ("_NET_WM_STATE", FALSE);
+          xev.xclient.format = 32;
+
+          xev.xclient.data.l[0] = gdk_atom_intern ("_NET_WM_STATE_REMOVE", FALSE);
+          xev.xclient.data.l[1] = gdk_atom_intern ("_NET_WM_STATE_MAXIMIZED_VERT", FALSE);
+          xev.xclient.data.l[2] = gdk_atom_intern ("_NET_WM_STATE_MAXIMIZED_HORZ", FALSE);
+      
+          XSendEvent (gdk_display, gdk_root_window, False,
+                      SubstructureRedirectMask | SubstructureNotifyMask,
+                      &xev);
+        }
+      else
+        {
+          /* FIXME try to emulate maximization somehow? */
+        }
+    }
+  else
+    {
+      gdk_synthesize_window_state (window,
+                                   GDK_WINDOW_STATE_MAXIMIZED,
+                                   0);
+    }
 }
 
 void          
Index: gtk/gtkmain.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkmain.c,v
retrieving revision 1.150
diff -u -u -r1.150 gtkmain.c
--- gtk/gtkmain.c	2001/02/19 21:54:03	1.150
+++ gtk/gtkmain.c	2001/02/24 00:43:43
@@ -888,6 +888,7 @@
     case GDK_SELECTION_NOTIFY:
     case GDK_CLIENT_EVENT:
     case GDK_VISIBILITY_NOTIFY:
+    case GDK_WINDOW_STATE:
       gtk_widget_event (event_widget, event);
       break;
 
Index: gtk/gtkwidget.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwidget.c,v
retrieving revision 1.185
diff -u -u -r1.185 gtkwidget.c
--- gtk/gtkwidget.c	2001/02/03 01:09:40	1.185
+++ gtk/gtkwidget.c	2001/02/24 00:43:43
@@ -99,6 +99,7 @@
   CLIENT_EVENT,
   NO_EXPOSE_EVENT,
   VISIBILITY_NOTIFY_EVENT,
+  WINDOW_STATE_EVENT,
   DEBUG_MSG,
   LAST_SIGNAL
 };
@@ -314,6 +315,7 @@
   klass->focus_out_event = NULL;
   klass->map_event = NULL;
   klass->unmap_event = NULL;
+  klass->window_state_event = NULL;
   klass->property_notify_event = gtk_selection_property_notify;
   klass->selection_clear_event = gtk_selection_clear;
   klass->selection_request_event = gtk_selection_request;
@@ -775,6 +777,14 @@
 		    gtk_marshal_BOOLEAN__POINTER,
 		    GTK_TYPE_BOOL, 1,
 		    GTK_TYPE_GDK_EVENT);
+  widget_signals[WINDOW_STATE_EVENT] =
+    gtk_signal_new ("window_state_event",
+		    GTK_RUN_LAST,
+		    GTK_CLASS_TYPE (object_class),
+		    GTK_SIGNAL_OFFSET (GtkWidgetClass, no_expose_event),
+		    gtk_marshal_BOOLEAN__POINTER,
+		    GTK_TYPE_BOOL, 1,
+		    GTK_TYPE_GDK_EVENT);
   widget_signals[DEBUG_MSG] =
     gtk_signal_new ("debug_msg",
 		    GTK_RUN_LAST | GTK_RUN_ACTION,
@@ -2336,6 +2346,9 @@
       break;
     case GDK_UNMAP:
       signal_num = UNMAP_EVENT;
+      break;
+    case GDK_WINDOW_STATE:
+      signal_num = WINDOW_STATE_EVENT;
       break;
     case GDK_PROPERTY_NOTIFY:
       signal_num = PROPERTY_NOTIFY_EVENT;
Index: gtk/gtkwidget.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwidget.h,v
retrieving revision 1.89
diff -u -u -r1.89 gtkwidget.h
--- gtk/gtkwidget.h	2001/02/03 01:09:40	1.89
+++ gtk/gtkwidget.h	2001/02/24 00:43:43
@@ -325,7 +325,9 @@
 				    GdkEventClient     *event);
   gint (* no_expose_event)	   (GtkWidget	       *widget,
 				    GdkEventAny	       *event);
-
+  gint (* window_state_event)      (GtkWidget          *widget,
+                                    GdkEventWindowState *event);
+  
   /* selection */
   void (* selection_get)           (GtkWidget          *widget,
 				    GtkSelectionData   *selection_data,
Index: gtk/gtkwindow.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwindow.c,v
retrieving revision 1.97
diff -u -u -r1.97 gtkwindow.c
--- gtk/gtkwindow.c	2001/02/03 01:09:40	1.97
+++ gtk/gtkwindow.c	2001/02/24 00:43:43
@@ -1247,7 +1247,8 @@
 gtk_window_map (GtkWidget *widget)
 {
   GtkWindow *window;
-
+  GdkWindow *toplevel;
+  
   g_return_if_fail (widget != NULL);
   g_return_if_fail (GTK_IS_WINDOW (widget));
 
@@ -1260,7 +1261,28 @@
       !GTK_WIDGET_MAPPED (window->bin.child))
     gtk_widget_map (window->bin.child);
 
+  if (window->frame)
+    toplevel = window->frame;
+  else
+    toplevel = widget->window;
+  
+  if (window->maximize_initially)
+    gdk_window_maximize (toplevel);
+  else
+    gdk_window_unmaximize (toplevel);
+  
+  if (window->stick_initially)
+    gdk_window_stick (toplevel);
+  else
+    gdk_window_unstick (toplevel);
+  
+  if (window->iconify_initially)
+    gdk_window_iconify (toplevel);
+  else
+    gdk_window_deiconify (toplevel);
+  
   gdk_window_show (widget->window);
+
   if (window->frame)
     gdk_window_show (window->frame);
 }
@@ -1398,7 +1420,8 @@
   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
   if (window->frame)
     gtk_style_set_background (widget->style, window->frame, GTK_STATE_NORMAL);
-  
+
+  /* This is a bad hack to set the window background. */
   gtk_window_paint (widget, NULL);
   
   if (window->transient_parent &&
@@ -1407,7 +1430,6 @@
 				  GTK_WIDGET (window->transient_parent)->window);
 }
 
-
 static void
 gtk_window_unrealize (GtkWidget *widget)
 {
@@ -2786,3 +2808,267 @@
 }
 
 
+
+/**
+ * gtk_window_present:
+ * @window: a #GtkWindow
+ *
+ * Presents a window to the user. This may mean raising the window
+ * in the stacking order, deiconifying it, moving it to the current
+ * desktop, and/or giving it the keyboard focus, possibly dependent
+ * on the user's platform, window manager, and preferences.
+ *
+ * If @window is hidden, this function calls gtk_widget_show()
+ * as well.
+ * 
+ * This function should be used when the user tries to open a window
+ * that's already open. Say for example the preferences dialog is
+ * currently open, and the user chooses Preferences from the menu
+ * a second time; use gtk_window_present() to move the already-open dialog
+ * where the user can see it.
+ * 
+ **/
+void
+gtk_window_present (GtkWindow *window)
+{
+  GtkWidget *widget;
+
+  g_return_if_fail (GTK_IS_WINDOW (window));
+
+  widget = GTK_WIDGET (window);
+
+  if (widget->window != NULL)
+    {
+      gdk_window_show (widget->window);
+      gdk_window_focus (widget->window,
+                        gtk_get_current_event_time ());
+
+
+      /* FIXME add desktop-moving stuff from WM spec */
+    }
+
+  gtk_widget_show (widget);
+}
+
+/**
+ * gtk_window_iconify:
+ * @window: a #GtkWindow
+ *
+ * Asks to iconify @window. Note that you shouldn't assume the window
+ * is definitely iconified afterward, because other entities (e.g. the
+ * user or window manager) could deiconify it again, or there may not
+ * be a window manager in which case iconification isn't possible,
+ * etc. But normally the window will end up iconified. Just don't write
+ * code that crashes if not.
+ *
+ * It's permitted to call this function before showing a window,
+ * in which case the window will be iconified before it ever appears
+ * onscreen.
+ *
+ * You can track iconification via the "window_state_event" signal
+ * on #GtkWidget.
+ * 
+ **/
+void
+gtk_window_iconify (GtkWindow *window)
+{
+  GtkWidget *widget;
+  GdkWindow *toplevel;
+  
+  g_return_if_fail (GTK_IS_WINDOW (window));
+
+  widget = GTK_WIDGET (window);
+
+  window->iconify_initially = TRUE;
+
+  if (window->frame)
+    toplevel = window->frame;
+  else
+    toplevel = widget->window;
+  
+  if (toplevel != NULL)
+    gdk_window_iconify (toplevel);
+}
+
+/**
+ * gtk_window_deiconify:
+ * @window: a #GtkWindow
+ *
+ * Asks to deiconify @window. Note that you shouldn't assume the
+ * window is definitely deiconified afterward, because other entities
+ * (e.g. the user or window manager) could iconify it again before
+ * your code which assumes deiconification gets to run.
+ *
+ * You can track iconification via the "window_state_event" signal
+ * on #GtkWidget.
+ **/
+void
+gtk_window_deiconify (GtkWindow *window)
+{
+  GtkWidget *widget;
+  GdkWindow *toplevel;
+  
+  g_return_if_fail (GTK_IS_WINDOW (window));
+
+  widget = GTK_WIDGET (window);
+
+  window->iconify_initially = FALSE;
+
+  if (window->frame)
+    toplevel = window->frame;
+  else
+    toplevel = widget->window;
+  
+  if (toplevel != NULL)
+    gdk_window_deiconify (toplevel);
+}
+
+/**
+ * gtk_window_stick:
+ * @window: a #GtkWindow
+ *
+ * Asks to stick @window, which means that it will appear on all user
+ * desktops. Note that you shouldn't assume the window is definitely
+ * stuck afterward, because other entities (e.g. the user or window
+ * manager) could unstick it again, and some window managers do not
+ * support sticking windows. But normally the window will end up
+ * stuck. Just don't write code that crashes if not.
+ *
+ * It's permitted to call this function before showing a window.
+ *
+ * You can track stickiness via the "window_state_event" signal
+ * on #GtkWidget.
+ * 
+ **/
+void
+gtk_window_stick (GtkWindow *window)
+{
+  GtkWidget *widget;
+  GdkWindow *toplevel;
+  
+  g_return_if_fail (GTK_IS_WINDOW (window));
+
+  widget = GTK_WIDGET (window);
+
+  window->stick_initially = TRUE;
+
+  if (window->frame)
+    toplevel = window->frame;
+  else
+    toplevel = widget->window;
+  
+  if (toplevel != NULL)
+    gdk_window_stick (toplevel);
+}
+
+/**
+ * gtk_window_unstick:
+ * @window: a #GtkWindow
+ *
+ * Asks to unstick @window, which means that it will appear on only
+ * one of the user's desktops. Note that you shouldn't assume the
+ * window is definitely unstuck afterward, because other entities
+ * (e.g. the user or window manager) could stick it again. But
+ * normally the window will end up stuck. Just don't write code that
+ * crashes if not.
+ *
+ * You can track stickiness via the "window_state_event" signal
+ * on #GtkWidget.
+ * 
+ **/
+void
+gtk_window_unstick (GtkWindow *window)
+{
+  GtkWidget *widget;
+  GdkWindow *toplevel;
+  
+  g_return_if_fail (GTK_IS_WINDOW (window));
+
+  widget = GTK_WIDGET (window);
+
+  window->stick_initially = FALSE;
+
+  if (window->frame)
+    toplevel = window->frame;
+  else
+    toplevel = widget->window;
+  
+  if (toplevel != NULL)
+    gdk_window_unstick (toplevel);
+}
+
+/**
+ * gtk_window_maximize:
+ * @window: a #GtkWindow
+ *
+ * Asks to maximize @window, so that it becomes full-screen. Note that
+ * you shouldn't assume the window is definitely maximized afterward,
+ * because other entities (e.g. the user or window manager) could
+ * unmaximize it again, and not all window managers support
+ * maximization. But normally the window will end up maximized. Just
+ * don't write code that crashes if not.
+ *
+ * It's permitted to call this function before showing a window,
+ * in which case the window will be maximized when it appears onscreen
+ * initially.
+ *
+ * You can track maximization via the "window_state_event" signal
+ * on #GtkWidget.
+ * 
+ **/
+void
+gtk_window_maximize (GtkWindow *window)
+{
+  GtkWidget *widget;
+  GdkWindow *toplevel;
+  
+  g_return_if_fail (GTK_IS_WINDOW (window));
+
+  widget = GTK_WIDGET (window);
+
+  window->maximize_initially = TRUE;
+
+  if (window->frame)
+    toplevel = window->frame;
+  else
+    toplevel = widget->window;
+  
+  if (toplevel != NULL)
+    gdk_window_maximize (toplevel);
+}
+
+/**
+ * gtk_window_unmaximize:
+ * @window: a #GtkWindow
+ *
+ * Asks to unmaximize @window, so that it becomes full-screen. Note
+ * that you shouldn't assume the window is definitely unmaximized
+ * afterward, because other entities (e.g. the user or window manager)
+ * could maximize it again, and not all window managers honor requests
+ * to unmaximize. But normally the window will end up unmaximized. Just
+ * don't write code that crashes if not.
+ *
+ * You can track maximization via the "window_state_event" signal
+ * on #GtkWidget.
+ * 
+ **/
+void
+gtk_window_unmaximize (GtkWindow *window)
+{
+  GtkWidget *widget;
+  GdkWindow *toplevel;
+  
+  g_return_if_fail (GTK_IS_WINDOW (window));
+
+  widget = GTK_WIDGET (window);
+
+  window->maximize_initially = FALSE;
+
+  if (window->frame)
+    toplevel = window->frame;
+  else
+    toplevel = widget->window;
+  
+  if (toplevel != NULL)
+    gdk_window_unmaximize (toplevel);
+}
Index: gtk/gtkwindow.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwindow.h,v
retrieving revision 1.25
diff -u -u -r1.25 gtkwindow.h
--- gtk/gtkwindow.h	2001/01/08 17:04:17	1.25
+++ gtk/gtkwindow.h	2001/02/24 00:43:43
@@ -85,6 +85,11 @@
   
   guint has_frame : 1;
 
+  /* gtk_window_iconify() called before realization */
+  guint iconify_initially : 1;
+  guint stick_initially : 1;
+  guint maximize_initially : 1;
+  
   guint frame_left;
   guint frame_top;
   guint frame_right;
@@ -152,6 +157,14 @@
 
 /* Get the "built-in" accel group (convenience thing) */
 GtkAccelGroup* gtk_window_get_default_accel_group (GtkWindow *window);
+
+void     gtk_window_present       (GtkWindow *window);
+void     gtk_window_iconify       (GtkWindow *window);
+void     gtk_window_deiconify     (GtkWindow *window);
+void     gtk_window_stick         (GtkWindow *window);
+void     gtk_window_unstick       (GtkWindow *window);
+void     gtk_window_maximize      (GtkWindow *window);
+void     gtk_window_unmaximize    (GtkWindow *window);
 
 
 /* --- internal functions --- */
Index: gtk/testgtk.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/testgtk.c,v
retrieving revision 1.220
diff -u -u -r1.220 testgtk.c
--- gtk/testgtk.c	2001/02/21 19:59:22	1.220
+++ gtk/testgtk.c	2001/02/24 00:43:43
@@ -7663,6 +7663,199 @@
     gtk_widget_destroy (window);
 }
 
+
+/*
+ * Window state tracking
+ */
+
+static gint
+window_state_callback (GtkWidget *widget,
+                       GdkEventWindowState *event,
+                       gpointer data)
+{
+  GtkWidget *label = data;
+  gchar *msg;
+
+  msg = g_strconcat (GTK_WINDOW (widget)->title, ": ",
+                     (event->current & GDK_WINDOW_STATE_WITHDRAWN) ?
+                     "withdrawn" : "not withdrawn", ", ",
+                     (event->current & GDK_WINDOW_STATE_ICONIFIED) ?
+                     "iconified" : "not iconified", ", ",
+                     (event->current & GDK_WINDOW_STATE_STICKY) ?
+                     "sticky" : "not sticky", ", ",
+                     (event->current & GDK_WINDOW_STATE_MAXIMIZED) ?
+                     "maximized" : "not maximized",
+                     NULL);
+  
+  gtk_label_set_text (GTK_LABEL (label), msg);
+
+  g_free (msg);
+
+  return FALSE;
+}
+
+static GtkWidget*
+tracking_label (GtkWidget *window)
+{
+  GtkWidget *label;
+  GtkWidget *hbox;
+  GtkWidget *button;
+
+  hbox = gtk_hbox_new (FALSE, 5);
+  
+  gtk_signal_connect_object (GTK_OBJECT (hbox),
+                             "destroy",
+                             GTK_SIGNAL_FUNC (gtk_widget_destroy),
+                             GTK_OBJECT (window));
+
+  label = gtk_label_new ("<no window state events received>");
+  gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+  
+  gtk_signal_connect (GTK_OBJECT (window),
+                      "window_state_event",
+                      GTK_SIGNAL_FUNC (window_state_callback),
+                      label);
+
+  button = gtk_button_new_with_label ("Deiconify");
+  gtk_signal_connect_object (GTK_WIDGET (button),
+                             "clicked",
+                             GTK_SIGNAL_FUNC (gtk_window_deiconify),
+                             GTK_OBJECT (window));
+  gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+
+  button = gtk_button_new_with_label ("Iconify");
+  gtk_signal_connect_object (GTK_WIDGET (button),
+                             "clicked",
+                             GTK_SIGNAL_FUNC (gtk_window_iconify),
+                             GTK_OBJECT (window));
+  gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+  
+  button = gtk_button_new_with_label ("Present");
+  gtk_signal_connect_object (GTK_WIDGET (button),
+                             "clicked",
+                             GTK_SIGNAL_FUNC (gtk_window_present),
+                             GTK_OBJECT (window));
+  gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+
+  button = gtk_button_new_with_label ("Show");
+  gtk_signal_connect_object (GTK_WIDGET (button),
+                             "clicked",
+                             GTK_SIGNAL_FUNC (gtk_widget_show),
+                             GTK_OBJECT (window));
+  gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+  
+  gtk_widget_show_all (hbox);
+  
+  return hbox;
+}
+
+static GtkWidget*
+get_state_controls (GtkWidget *window)
+{
+  GtkWidget *vbox;
+  GtkWidget *button;
+
+  vbox = gtk_vbox_new (FALSE, 0);
+  
+  button = gtk_button_new_with_label ("Stick");
+  gtk_signal_connect_object (GTK_WIDGET (button),
+                             "clicked",
+                             GTK_SIGNAL_FUNC (gtk_window_stick),
+                             GTK_OBJECT (window));
+  gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+
+  button = gtk_button_new_with_label ("Unstick");
+  gtk_signal_connect_object (GTK_WIDGET (button),
+                             "clicked",
+                             GTK_SIGNAL_FUNC (gtk_window_unstick),
+                             GTK_OBJECT (window));
+  gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+  
+  button = gtk_button_new_with_label ("Maximize");
+  gtk_signal_connect_object (GTK_WIDGET (button),
+                             "clicked",
+                             GTK_SIGNAL_FUNC (gtk_window_maximize),
+                             GTK_OBJECT (window));
+  gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+
+  button = gtk_button_new_with_label ("Unmaximize");
+  gtk_signal_connect_object (GTK_WIDGET (button),
+                             "clicked",
+                             GTK_SIGNAL_FUNC (gtk_window_unmaximize),
+                             GTK_OBJECT (window));
+  gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+
+  button = gtk_button_new_with_label ("Iconify");
+  gtk_signal_connect_object (GTK_WIDGET (button),
+                             "clicked",
+                             GTK_SIGNAL_FUNC (gtk_window_iconify),
+                             GTK_OBJECT (window));
+  gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+
+  button = gtk_button_new_with_label ("Hide (withdraw)");
+  gtk_signal_connect_object (GTK_WIDGET (button),
+                             "clicked",
+                             GTK_SIGNAL_FUNC (gtk_widget_hide),
+                             GTK_OBJECT (window));
+  gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+  
+  gtk_widget_show_all (vbox);
+
+  return vbox;
+}
+
+void
+create_window_states (void)
+{
+  static GtkWidget *window = NULL;
+  GtkWidget *label;
+  GtkWidget *box1;
+  GtkWidget *iconified;
+  GtkWidget *normal;
+  GtkWidget *controls;
+  
+  if (!window)
+    {
+      window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+      gtk_signal_connect (GTK_OBJECT (window), "destroy",
+			  GTK_SIGNAL_FUNC(gtk_widget_destroyed),
+			  &window);
+
+      gtk_window_set_title (GTK_WINDOW (window), "Window states");
+      
+      box1 = gtk_vbox_new (FALSE, 0);
+      gtk_container_add (GTK_CONTAINER (window), box1);
+
+      iconified = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+      gtk_window_iconify (iconified);
+      gtk_window_set_title (GTK_WINDOW (iconified), "Iconified initially");
+      controls = get_state_controls (iconified);
+      gtk_container_add (GTK_CONTAINER (iconified), controls);
+      
+      normal = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+      gtk_window_set_title (GTK_WINDOW (normal), "Deiconified initially");
+      controls = get_state_controls (normal);
+      gtk_container_add (GTK_CONTAINER (normal), controls);
+      
+      label = tracking_label (iconified);
+      gtk_container_add (GTK_CONTAINER (box1), label);
+
+      label = tracking_label (normal);
+      gtk_container_add (GTK_CONTAINER (box1), label);
+
+      gtk_widget_show_all (iconified);
+      gtk_widget_show_all (normal);
+      gtk_widget_show_all (box1);
+    }
+
+  if (!GTK_WIDGET_VISIBLE (window))
+    gtk_widget_show (window);
+  else
+    gtk_widget_destroy (window);
+}
+
 /*
  * GtkProgressBar
  */
@@ -9361,6 +9554,7 @@
       { "tooltips", create_tooltips },
       { "tree", create_tree_mode_window},
       { "WM hints", create_wmhints },
+      { "window states", create_window_states }
     };
   int nbuttons = sizeof (buttons) / sizeof (buttons[0]);
   GtkWidget *window;






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