resize/move grips



Hi,

This is a rough patch to implement part of the WM spec that we forgot
about, _NET_WM_MOVERESIZE. It also has fallback code for noncompliant 
window managers.

Summary of changes in the patch:

 - code from gtk_window_constrain_size moved to
   gdk_window_constrain_size since it's needed in the fallback code

 - gdk_window_get_geometry_hints() added as a static function, 
   should it be exported?

 - GdkWindowEdge enum gives the edge or corner of the resize operation
   (I realize the corners aren't edges, but don't have a better name)

 - gdk_window_begin_resize_drag(), gdk_window_begin_move_drag() start 
   a resize or move operation
 
 - gtk_window_begin_resize_drag(), gtk_window_begin_move_drag()
   mirroring the GdkWindow functions

 - gross hack in gdk_event_translate to support the fallback mode

 - GtkStatusbar has a resize grip by default, can be disabled 
   with gtk_statusbar_set_has_grip() (maybe should rename
   set_has_resize_grip() for clarity, function won't be used often)

 - GtkStyle can draw a resize grip.

Some thought given to a GtkResizeGrip widget, rejected this idea on
the grounds that it doesn't work for the statusbar anyhow (doesn't
draw properly with the frame), and it's trivial to add a grip to any
existing widget given gtk_paint_resize_grip() and
gtk_window_begin_resize_drag(). Also, couldn't think of anywhere you'd
want to have a resize grip other than in the statusbar. So maybe we'll
add this widget someday but for now it's not there.

I think the resize grip is a nice usability enhancement, the UI
experts of the world seem universally in favor of it.

Havoc


Index: gdk/gdkwindow.c
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/gdkwindow.c,v
retrieving revision 1.109
diff -u -u -r1.109 gdkwindow.c
--- gdk/gdkwindow.c	2001/02/27 20:39:47	1.109
+++ gdk/gdkwindow.c	2001/03/09 23:45:05
@@ -2137,3 +2137,122 @@
   debug_updates = setting;
 }
 
+/* Constrain a window size to obey the hints passed in geometry
+ * and flags. The result will be stored in *new_width and *new_height
+ *
+ * This routine is partially borrowed from fvwm.
+ *
+ * Copyright 1993, Robert Nation
+ *     You may use this code for any purpose, as long as the original
+ *     copyright remains in the source code and all documentation
+ *
+ * which in turn borrows parts of the algorithm from uwm
+ */
+void
+gdk_window_constrain_size (GdkGeometry *geometry,
+			   guint        flags,
+			   gint         width,
+			   gint         height,
+			   gint        *new_width,
+			   gint        *new_height)
+{
+  gint min_width = 0;
+  gint min_height = 0;
+  gint base_width = 0;
+  gint base_height = 0;
+  gint xinc = 1;
+  gint yinc = 1;
+  gint max_width = G_MAXINT;
+  gint max_height = G_MAXINT;
+  
+#define FLOOR(value, base)	( ((gint) ((value) / (base))) * (base) )
+
+  if ((flags & GDK_HINT_BASE_SIZE) && (flags & GDK_HINT_MIN_SIZE))
+    {
+      base_width = geometry->base_width;
+      base_height = geometry->base_height;
+      min_width = geometry->min_width;
+      min_height = geometry->min_height;
+    }
+  else if (flags & GDK_HINT_BASE_SIZE)
+    {
+      base_width = geometry->base_width;
+      base_height = geometry->base_height;
+      min_width = geometry->base_width;
+      min_height = geometry->base_height;
+    }
+  else if (flags & GDK_HINT_MIN_SIZE)
+    {
+      base_width = geometry->min_width;
+      base_height = geometry->min_height;
+      min_width = geometry->min_width;
+      min_height = geometry->min_height;
+    }
+
+  if (flags & GDK_HINT_MAX_SIZE)
+    {
+      max_width = geometry->max_width ;
+      max_height = geometry->max_height;
+    }
+
+  if (flags & GDK_HINT_RESIZE_INC)
+    {
+      xinc = MAX (xinc, geometry->width_inc);
+      yinc = MAX (yinc, geometry->height_inc);
+    }
+  
+  /* clamp width and height to min and max values
+   */
+  width = CLAMP (width, min_width, max_width);
+  height = CLAMP (height, min_height, max_height);
+  
+  /* shrink to base + N * inc
+   */
+  width = base_width + FLOOR (width - base_width, xinc);
+  height = base_height + FLOOR (height - base_height, yinc);
+
+  /* constrain aspect ratio, according to:
+   *
+   *                width     
+   * min_aspect <= -------- <= max_aspect
+   *                height    
+   */
+  
+  if (flags & GDK_HINT_ASPECT &&
+      geometry->min_aspect > 0 &&
+      geometry->max_aspect > 0)
+    {
+      gint delta;
+
+      if (geometry->min_aspect * height > width)
+	{
+	  delta = FLOOR (height - width * geometry->min_aspect, yinc);
+	  if (height - delta >= min_height)
+	    height -= delta;
+	  else
+	    { 
+	      delta = FLOOR (height * geometry->min_aspect - width, xinc);
+	      if (width + delta <= max_width) 
+		width += delta;
+	    }
+	}
+      
+      if (geometry->max_aspect * height < width)
+	{
+	  delta = FLOOR (width - height * geometry->max_aspect, xinc);
+	  if (width - delta >= min_width) 
+	    width -= delta;
+	  else
+	    {
+	      delta = FLOOR (width / geometry->max_aspect - height, yinc);
+	      if (height + delta <= max_height)
+		height += delta;
+	    }
+	}
+    }
+
+#undef FLOOR
+  
+  *new_width = width;
+  *new_height = height;
+}
Index: gdk/gdkwindow.h
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/gdkwindow.h,v
retrieving revision 1.18
diff -u -u -r1.18 gdkwindow.h
--- gdk/gdkwindow.h	2001/03/05 15:08:36	1.18
+++ gdk/gdkwindow.h	2001/03/09 23:45:05
@@ -150,6 +150,19 @@
   GDK_GRAVITY_STATIC
 } GdkGravity;
 
+
+typedef enum
+{
+  GDK_WINDOW_EDGE_NORTH_WEST,
+  GDK_WINDOW_EDGE_NORTH,
+  GDK_WINDOW_EDGE_NORTH_EAST,
+  GDK_WINDOW_EDGE_WEST,
+  GDK_WINDOW_EDGE_EAST,
+  GDK_WINDOW_EDGE_SOUTH_WEST,
+  GDK_WINDOW_EDGE_SOUTH,
+  GDK_WINDOW_EDGE_SOUTH_EAST  
+} GdkWindowEdge;
+
 struct _GdkWindowAttr
 {
   gchar *title;
@@ -443,6 +456,18 @@
 
 void          gdk_window_register_dnd    (GdkWindow       *window);
 
+void gdk_window_begin_resize_drag (GdkWindow     *window,
+                                   GdkWindowEdge  edge,
+                                   gint           button,
+                                   gint           root_x,
+                                   gint           root_y,
+                                   guint32        timestamp);
+void gdk_window_begin_move_drag   (GdkWindow     *window,
+                                   gint           button,
+                                   gint           root_x,
+                                   gint           root_y,
+                                   guint32        timestamp);
+
 /* Interface for dirty-region queueing */
 void       gdk_window_invalidate_rect     (GdkWindow    *window,
 					   GdkRectangle *rect,
@@ -461,6 +486,13 @@
 
 /* Enable/disable flicker, so you can tell if your code is inefficient. */
 void       gdk_window_set_debug_updates   (gboolean      setting);
+
+void       gdk_window_constrain_size      (GdkGeometry  *geometry,
+                                           guint         flags,
+                                           gint          width,
+                                           gint          height,
+                                           gint         *new_width,
+                                           gint         *new_height);
 
 #ifdef __cplusplus
 }
Index: gdk/x11/gdkevents-x11.c
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/x11/gdkevents-x11.c,v
retrieving revision 1.41
diff -u -u -r1.41 gdkevents-x11.c
--- gdk/x11/gdkevents-x11.c	2001/03/03 21:34:18	1.41
+++ gdk/x11/gdkevents-x11.c	2001/03/09 23:45:05
@@ -433,6 +433,14 @@
   if (window != NULL)
     gdk_window_ref (window);
 
+  if (window != NULL &&
+      window == _gdk_moveresize_emulation_window)
+    {
+      _gdk_moveresize_handle_event (xevent);
+      gdk_window_unref (window);
+      return FALSE;
+    }
+    
   if (wmspec_check_window != None &&
       xevent->xany.window == wmspec_check_window)
     {
Index: gdk/x11/gdkprivate-x11.h
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/x11/gdkprivate-x11.h,v
retrieving revision 1.10
diff -u -u -r1.10 gdkprivate-x11.h
--- gdk/x11/gdkprivate-x11.h	2001/03/03 21:34:19	1.10
+++ gdk/x11/gdkprivate-x11.h	2001/03/09 23:45:05
@@ -92,6 +92,8 @@
                                             XRectangle          **rects,
                                             gint                 *n_rects);
 
+void     _gdk_moveresize_handle_event      (XEvent *event);
+
 extern GdkDrawableClass  _gdk_x11_drawable_class;
 extern gboolean	         gdk_use_xshm;
 extern Atom		 gdk_wm_delete_window;
@@ -123,5 +125,7 @@
  * to checking the next event with XPending().
  */
 extern gboolean _gdk_have_xkb_autorepeat;
+
+extern GdkWindow *_gdk_moveresize_emulation_window;
 
 #endif /* __GDK_PRIVATE_X11_H__ */
Index: gdk/x11/gdkwindow-x11.c
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/x11/gdkwindow-x11.c,v
retrieving revision 1.109
diff -u -u -r1.109 gdkwindow-x11.c
--- gdk/x11/gdkwindow-x11.c	2001/03/05 15:08:36	1.109
+++ gdk/x11/gdkwindow-x11.c	2001/03/09 23:45:05
@@ -1344,7 +1344,7 @@
   if (geom_mask & GDK_HINT_WIN_GRAVITY)
     {
       size_hints.flags |= PWinGravity;
-      size_hints.width_inc = geometry->win_gravity;
+      size_hints.win_gravity = geometry->win_gravity;
     }
   
   /* FIXME: Would it be better to delete this property of
@@ -1355,6 +1355,65 @@
 		     &size_hints);
 }
 
+static void
+gdk_window_get_geometry_hints (GdkWindow      *window,
+                               GdkGeometry    *geometry,
+                               GdkWindowHints *geom_mask)
+{
+  XSizeHints size_hints;  
+  glong junk_size_mask = 0;
+
+  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (geometry != NULL);
+  g_return_if_fail (geom_mask != NULL);
+
+  *geom_mask = 0;
+  
+  if (GDK_WINDOW_DESTROYED (window))
+    return;
+  
+  if (!XGetWMNormalHints (GDK_WINDOW_XDISPLAY (window),
+                          GDK_WINDOW_XID (window),
+                          &size_hints,
+                          &junk_size_mask))
+    return;                   
+
+  if (size_hints.flags & PMinSize)
+    {
+      *geom_mask |= GDK_HINT_MIN_SIZE;
+      geometry->min_width = size_hints.min_width;
+      geometry->min_height = size_hints.min_height;
+    }
+
+  if (size_hints.flags & PMaxSize)
+    {
+      *geom_mask |= GDK_HINT_MAX_SIZE;
+      geometry->max_width = MAX (size_hints.max_width, 1);
+      geometry->max_height = MAX (size_hints.max_height, 1);
+    }
+
+  if (size_hints.flags & PResizeInc)
+    {
+      *geom_mask |= GDK_HINT_RESIZE_INC;
+      geometry->width_inc = size_hints.width_inc;
+      geometry->height_inc = size_hints.height_inc;
+    }
+
+  if (size_hints.flags & PAspect)
+    {
+      *geom_mask |= GDK_HINT_ASPECT;
+
+      geometry->min_aspect = (gdouble) size_hints.min_aspect.x / (gdouble) size_hints.min_aspect.y;
+      geometry->max_aspect = (gdouble) size_hints.max_aspect.x / (gdouble) size_hints.max_aspect.y;
+    }
+
+  if (size_hints.flags & PWinGravity)
+    {
+      *geom_mask |= GDK_HINT_WIN_GRAVITY;
+      geometry->win_gravity = size_hints.win_gravity;
+    }
+}
+
 static gboolean
 utf8_is_latin1 (const gchar *str)
 {
@@ -3234,5 +3293,345 @@
     }
   XUngrabServer (xdisplay);
   return root;
+}
+
+static void
+wmspec_moveresize (GdkWindow *window,
+                   gint       direction,
+                   gint       root_x,
+                   gint       root_y)     
+{
+  XEvent xev;
+
+  /* Release passive grab */
+  gdk_pointer_ungrab (GDK_CURRENT_TIME);
+  
+  xev.xclient.type = ClientMessage;
+  xev.xclient.serial = 0;
+  xev.xclient.send_event = True;
+  xev.xclient.display = gdk_display;
+  xev.xclient.window = GDK_WINDOW_XID (window);
+  xev.xclient.message_type = gdk_atom_intern ("_NET_WM_MOVERESIZE", FALSE);
+  xev.xclient.format = 32;
+  xev.xclient.data.l[0] = root_x;
+  xev.xclient.data.l[1] = root_y;
+  xev.xclient.data.l[2] = direction;
+  xev.xclient.data.l[3] = 0;
+  xev.xclient.data.l[4] = 0;
+  
+  XSendEvent (gdk_display, gdk_root_window, False,
+	      SubstructureRedirectMask | SubstructureNotifyMask,
+	      &xev);
+}
+
+/* From the WM spec */
+#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT      0
+#define _NET_WM_MOVERESIZE_SIZE_TOP          1
+#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT     2
+#define _NET_WM_MOVERESIZE_SIZE_RIGHT        3
+#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT  4
+#define _NET_WM_MOVERESIZE_SIZE_BOTTOM       5
+#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT   6
+#define _NET_WM_MOVERESIZE_SIZE_LEFT         7
+#define _NET_WM_MOVERESIZE_MOVE              8
+
+static void
+wmspec_resize_drag (GdkWindow     *window,
+                    GdkWindowEdge  edge,
+                    gint           button,
+                    gint           root_x,
+                    gint           root_y,
+                    guint32        timestamp)
+{
+  gint direction;
+  
+  /* Let the compiler turn a switch into a table, instead
+   * of doing the table manually, this way is easier to verify.
+   */
+  switch (edge)
+    {
+    case GDK_WINDOW_EDGE_NORTH_WEST:
+      direction = _NET_WM_MOVERESIZE_SIZE_TOPLEFT;
+      break;
+
+    case GDK_WINDOW_EDGE_NORTH:
+      direction = _NET_WM_MOVERESIZE_SIZE_TOP;
+      break;
+
+    case GDK_WINDOW_EDGE_NORTH_EAST:
+      direction = _NET_WM_MOVERESIZE_SIZE_TOPRIGHT;
+      break;
+
+    case GDK_WINDOW_EDGE_WEST:
+      direction = _NET_WM_MOVERESIZE_SIZE_LEFT;
+      break;
+
+    case GDK_WINDOW_EDGE_EAST:
+      direction = _NET_WM_MOVERESIZE_SIZE_RIGHT;
+      break;
+
+    case GDK_WINDOW_EDGE_SOUTH_WEST:
+      direction = _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT;
+      break;
+
+    case GDK_WINDOW_EDGE_SOUTH:
+      direction = _NET_WM_MOVERESIZE_SIZE_BOTTOM;
+      break;
+
+    case GDK_WINDOW_EDGE_SOUTH_EAST:
+      direction = _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT;
+      break;
+
+    default:
+      g_warning ("gdk_window_begin_resize_drag: bad resize edge %d!",
+                 edge);
+      return;
+      break;
+    }
+  
+  wmspec_moveresize (window, direction, root_x, root_y);
+}
+
+/* This is global for use in gdkevents-x11.c */
+GdkWindow *_gdk_moveresize_emulation_window = NULL;
+
+static GdkWindow *moveresize_window = NULL;
+static gboolean is_resize = FALSE;
+static GdkWindowEdge resize_edge;
+static gint moveresize_button;
+static gint moveresize_x;
+static gint moveresize_y;
+static gint moveresize_orig_x;
+static gint moveresize_orig_y;
+static gint moveresize_orig_width;
+static gint moveresize_orig_height;
+static GdkWindowHints moveresize_geom_mask = 0;
+static GdkGeometry moveresize_geometry;
+
+static void
+update_pos (gint new_root_x,
+            gint new_root_y)
+{
+  gint dx, dy;
+  
+  dx = new_root_x - moveresize_x;
+  dy = new_root_y - moveresize_y;
+
+  if (is_resize)
+    {
+      gint w, h;
+
+      w = moveresize_orig_width;
+      h = moveresize_orig_height;
+      
+      switch (resize_edge)
+        {
+        case GDK_WINDOW_EDGE_SOUTH_EAST:
+          w += dx;
+          h += dy;
+          break;
+
+        }
+
+      w = MAX (w, 1);
+      h = MAX (h, 1);
+      
+      if (moveresize_geom_mask)
+        {
+          gdk_window_constrain_size (&moveresize_geometry,
+                                     moveresize_geom_mask,
+                                     w, h,
+                                     &w, &h);
+        }
+      
+      gdk_window_resize (moveresize_window, w, h);
+    }
+  else
+    {
+      gint x, y;
+
+      x = moveresize_orig_x + dx;
+      y = moveresize_orig_y + dy;
+      
+      gdk_window_move (moveresize_window, x, y);
+    }
+}
+
+static void
+finish_drag (void)
+{
+  gdk_window_destroy (_gdk_moveresize_emulation_window);
+  _gdk_moveresize_emulation_window = NULL;
+}
+
+void
+_gdk_moveresize_handle_event (XEvent *event)
+{
+  gint i;
+  guint button_mask = 0;
+  
+  i = moveresize_button - 1;
+  button_mask = GDK_BUTTON1_MASK;
+  while (i > 0)
+    {
+      button_mask <<= 1;
+      --i;
+    }
+  
+  switch (event->xany.type)
+    {
+    case MotionNotify:
+      update_pos (event->xmotion.x_root,
+                  event->xmotion.y_root);
+      
+      if ((event->xmotion.state & button_mask) == 0)
+        finish_drag ();
+      else
+        gdk_window_get_pointer (_gdk_moveresize_emulation_window, NULL, NULL, NULL);
+      break;
+
+    case ButtonRelease:
+      update_pos (event->xbutton.x_root,
+                  event->xbutton.y_root);
+      
+      if (event->xbutton.button == moveresize_button)
+        finish_drag ();
+      break;
+    }
+}
+
+static void
+create_moveresize_window (guint32 timestamp)
+{
+  GdkWindowAttr attributes;
+  gint attributes_mask;
+  GdkGrabStatus status;
+
+  g_assert (_gdk_moveresize_emulation_window == NULL);
+  
+  attributes.x = -100;
+  attributes.y = -100;
+  attributes.width = 10;
+  attributes.height = 10;
+  attributes.window_type = GDK_WINDOW_TEMP;
+  attributes.wclass = GDK_INPUT_ONLY;
+  attributes.override_redirect = TRUE;
+  attributes.event_mask = GDK_BUTTON_RELEASE_MASK |
+    GDK_POINTER_MOTION_MASK |
+    GDK_POINTER_MOTION_HINT_MASK;
+
+  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_NOREDIR;
+
+  _gdk_moveresize_emulation_window =
+    gdk_window_new (NULL, &attributes, attributes_mask);
+
+  gdk_window_show (_gdk_moveresize_emulation_window);
+
+  /* Note the race; the button release can occur on the window
+   * the user originally clicked on before we do this grab.
+   * To minimize the problem in that case, we check the state
+   * on all motion events in _gdk_moveresize_handle_event()
+   * above.
+   */
+  status = gdk_pointer_grab (_gdk_moveresize_emulation_window,
+                             TRUE,
+                             GDK_BUTTON_RELEASE_MASK |
+                             GDK_POINTER_MOTION_MASK |
+                             GDK_POINTER_MOTION_HINT_MASK,
+                             FALSE,
+                             NULL,
+                             timestamp);
+
+  if (status != GDK_GRAB_SUCCESS)
+    {
+      g_warning ("You can't call gdk_window_begin_resize_drag() or gdk_window_begin_move_drag() while the pointer is grabbed, grab failed with status %d", status);
+      gdk_window_destroy (_gdk_moveresize_emulation_window);
+      _gdk_moveresize_emulation_window = NULL;
+    }
+}
+
+static void
+emulate_resize_drag (GdkWindow     *window,
+                     GdkWindowEdge  edge,
+                     gint           button,
+                     gint           root_x,
+                     gint           root_y,
+                     guint32        timestamp)
+{
+  is_resize = TRUE;
+  moveresize_button = button;
+  resize_edge = edge;
+  moveresize_x = root_x;
+  moveresize_y = root_y;
+  moveresize_window = GDK_WINDOW (g_object_ref (G_OBJECT (window)));
+
+  gdk_window_get_size (window, &moveresize_orig_width, &moveresize_orig_height);
+  
+  moveresize_geom_mask = 0;
+  gdk_window_get_geometry_hints (window,
+                                 &moveresize_geometry,
+                                 &moveresize_geom_mask);
+  
+  create_moveresize_window (timestamp);
+}
+
+static void
+emulate_move_drag (GdkWindow     *window,
+                   gint           button,
+                   gint           root_x,
+                   gint           root_y,
+                   guint32        timestamp)
+{
+  is_resize = FALSE;
+  moveresize_button = button;
+  moveresize_x = root_x;
+  moveresize_y = root_y;
+  moveresize_window = GDK_WINDOW (g_object_ref (G_OBJECT (window)));
+
+  gdk_window_get_deskrelative_origin (moveresize_window,
+                                      &moveresize_orig_x,
+                                      &moveresize_orig_y);
+  
+  create_moveresize_window (timestamp);
+}
+
+void
+gdk_window_begin_resize_drag (GdkWindow     *window,
+                              GdkWindowEdge  edge,
+                              gint           button,
+                              gint           root_x,
+                              gint           root_y,
+                              guint32        timestamp)
+{
+  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (_gdk_moveresize_emulation_window == NULL);
+  
+  if (GDK_WINDOW_DESTROYED (window))
+    return;
+
+  if (gdk_net_wm_supports (gdk_atom_intern ("_NET_WM_MOVERESIZE", FALSE)))
+    wmspec_resize_drag (window, edge, button, root_x, root_y, timestamp);
+  else
+    emulate_resize_drag (window, edge, button, root_x, root_y, timestamp);
+}
+
+void
+gdk_window_begin_move_drag (GdkWindow *window,
+                            gint       button,
+                            gint       root_x,
+                            gint       root_y,
+                            guint32    timestamp)
+{
+  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (_gdk_moveresize_emulation_window == NULL);
+  
+  if (GDK_WINDOW_DESTROYED (window))
+    return;
+
+  if (gdk_net_wm_supports (gdk_atom_intern ("_NET_WM_MOVERESIZE", FALSE)))
+    wmspec_moveresize (window, _NET_WM_MOVERESIZE_MOVE,
+                       root_x, root_y);
+  else
+    emulate_move_drag (window, button, root_x, root_y, timestamp);
 }
 
Index: gtk/gtkstatusbar.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkstatusbar.c,v
retrieving revision 1.26
diff -u -u -r1.26 gtkstatusbar.c
--- gtk/gtkstatusbar.c	2001/02/03 01:09:40	1.26
+++ gtk/gtkstatusbar.c	2001/03/09 23:45:05
@@ -29,6 +29,7 @@
 #include "gtklabel.h"
 #include "gtksignal.h"
 #include "gtkstatusbar.h"
+#include "gtkwindow.h"
 
 typedef struct _GtkStatusbarMsg GtkStatusbarMsg;
 
@@ -52,7 +53,20 @@
 static void gtk_statusbar_update		   (GtkStatusbar      *statusbar,
 						    guint	       context_id,
 						    const gchar       *text);
-     
+static void gtk_statusbar_size_allocate            (GtkWidget         *widget,
+                                                    GtkAllocation     *allocation);
+static void gtk_statusbar_realize                  (GtkWidget         *widget);
+static void gtk_statusbar_unrealize                (GtkWidget         *widget);
+static void gtk_statusbar_map                      (GtkWidget         *widget);
+static void gtk_statusbar_unmap                    (GtkWidget         *widget);
+static gint gtk_statusbar_button_press             (GtkWidget         *widget,
+                                                    GdkEventButton    *event);
+static gint gtk_statusbar_expose_event             (GtkWidget         *widget,
+                                                    GdkEventExpose    *event);
+
+static void gtk_statusbar_create_window            (GtkStatusbar      *statusbar);
+static void gtk_statusbar_destroy_window           (GtkStatusbar      *statusbar);
+
 static GtkContainerClass *parent_class;
 static guint              statusbar_signals[SIGNAL_LAST] = { 0 };
 
@@ -96,6 +110,16 @@
   
   object_class->destroy = gtk_statusbar_destroy;
 
+  widget_class->size_allocate = gtk_statusbar_size_allocate;
+  
+  widget_class->realize = gtk_statusbar_realize;
+  widget_class->unrealize = gtk_statusbar_unrealize;
+  widget_class->map = gtk_statusbar_map;
+  widget_class->unmap = gtk_statusbar_unmap;
+  
+  widget_class->button_press_event = gtk_statusbar_button_press;
+  widget_class->expose_event = gtk_statusbar_expose_event;
+  
   class->messages_mem_chunk = g_mem_chunk_new ("GtkStatusBar messages mem chunk",
 					       sizeof (GtkStatusbarMsg),
 					       sizeof (GtkStatusbarMsg) * 64,
@@ -103,7 +127,7 @@
 
   class->text_pushed = gtk_statusbar_update;
   class->text_popped = gtk_statusbar_update;
-
+  
   statusbar_signals[SIGNAL_TEXT_PUSHED] =
     gtk_signal_new ("text_pushed",
 		    GTK_RUN_LAST,
@@ -134,6 +158,8 @@
   box->spacing = 2;
   box->homogeneous = FALSE;
 
+  statusbar->has_grip = TRUE;
+  
   statusbar->frame = gtk_frame_new (NULL);
   gtk_frame_set_shadow_type (GTK_FRAME (statusbar->frame), GTK_SHADOW_IN);
   gtk_box_pack_start (box, statusbar->frame, TRUE, TRUE, 0);
@@ -312,6 +338,37 @@
     }
 }
 
+void
+gtk_statusbar_set_has_grip (GtkStatusbar *statusbar,
+                            gboolean      setting)
+{
+  g_return_if_fail (GTK_IS_STATUSBAR (statusbar));
+
+  setting = setting != FALSE;
+
+  if (setting != statusbar->has_grip)
+    {
+      statusbar->has_grip = setting;
+      gtk_widget_queue_draw (GTK_WIDGET (statusbar));
+
+      if (GTK_WIDGET_REALIZED (statusbar))
+        {
+          if (statusbar->has_grip && statusbar->grip_window == NULL)
+            gtk_statusbar_create_window (statusbar);
+          else if (!statusbar->has_grip && statusbar->grip_window != NULL)
+            gtk_statusbar_destroy_window (statusbar);
+        }
+    }
+}
+
+gboolean
+gtk_statusbar_get_has_grip (GtkStatusbar *statusbar)
+{
+  g_return_val_if_fail (GTK_IS_STATUSBAR (statusbar), FALSE);
+
+  return statusbar->has_grip;
+}
+
 static void
 gtk_statusbar_destroy (GtkObject *object)
 {
@@ -342,4 +399,207 @@
   statusbar->keys = NULL;
 
   GTK_OBJECT_CLASS (parent_class)->destroy (object);
+}
+
+static void
+get_grip_rect (GtkStatusbar *statusbar,
+               GdkRectangle *rect)
+{
+  GtkWidget *widget;
+  gint w, h;
+  
+  widget = GTK_WIDGET (statusbar);
+
+  /* These are in effect the max/default size of the grip. */
+  w = 18;
+  h = 18;
+
+  if (w > (widget->allocation.width))
+    w = widget->allocation.width;
+
+  if (h > (widget->allocation.height - widget->style->ythickness))
+    h = widget->allocation.height - widget->style->ythickness;
+  
+  rect->x = widget->allocation.x + widget->allocation.width - w;
+  rect->y = widget->allocation.y + widget->allocation.height - h;
+  rect->width = w;
+  rect->height = h;
+}
+
+static void
+gtk_statusbar_size_allocate (GtkWidget     *widget,
+                             GtkAllocation *allocation)
+{
+  GtkStatusbar *statusbar;
+  GdkRectangle rect;
+  
+  statusbar = GTK_STATUSBAR (widget);
+  
+  GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
+
+  get_grip_rect (statusbar, &rect);
+  
+  if (statusbar->grip_window)
+    gdk_window_move_resize (statusbar->grip_window,
+                            rect.x, rect.y,
+                            rect.width, rect.height);
+}
+
+static void
+gtk_statusbar_create_window (GtkStatusbar *statusbar)
+{
+  GtkWidget *widget;
+  GdkWindowAttr attributes;
+  gint attributes_mask;
+  GdkRectangle rect;
+  
+  g_return_if_fail (GTK_WIDGET_REALIZED (statusbar));
+  g_return_if_fail (statusbar->has_grip);
+  
+  widget = GTK_WIDGET (statusbar);
+
+  get_grip_rect (statusbar, &rect);
+
+  attributes.x = rect.x;
+  attributes.y = rect.y;
+  attributes.width = rect.width;
+  attributes.height = rect.height;
+  attributes.window_type = GDK_WINDOW_TEMP;
+  attributes.wclass = GDK_INPUT_ONLY;
+  attributes.override_redirect = TRUE;
+  attributes.event_mask = gtk_widget_get_events (widget) |
+    GDK_BUTTON_PRESS_MASK;
+
+  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_NOREDIR;
+
+  statusbar->grip_window = gdk_window_new (widget->window,
+                                           &attributes, attributes_mask);
+  gdk_window_set_user_data (statusbar->grip_window, widget);
+}
+
+static void
+gtk_statusbar_destroy_window (GtkStatusbar *statusbar)
+{
+  gdk_window_set_user_data (statusbar->grip_window, NULL);
+  gdk_window_destroy (statusbar->grip_window);
+  statusbar->grip_window = NULL;
+}
+
+static void
+gtk_statusbar_realize (GtkWidget *widget)
+{
+  GtkStatusbar *statusbar;
+
+  statusbar = GTK_STATUSBAR (widget);
+  
+  (* GTK_WIDGET_CLASS (parent_class)->realize) (widget);
+
+  if (statusbar->has_grip)
+    gtk_statusbar_create_window (statusbar);
+}
+
+static void
+gtk_statusbar_unrealize (GtkWidget *widget)
+{
+  GtkStatusbar *statusbar;
+
+  statusbar = GTK_STATUSBAR (widget);
+
+  if (statusbar->grip_window)
+    gtk_statusbar_destroy_window (statusbar);
+  
+  (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
+}
+
+static void
+gtk_statusbar_map (GtkWidget *widget)
+{
+  GtkStatusbar *statusbar;
+
+  statusbar = GTK_STATUSBAR (widget);
+  
+  (* GTK_WIDGET_CLASS (parent_class)->map) (widget);
+  
+  if (statusbar->grip_window)
+    gdk_window_show (statusbar->grip_window);
+}
+
+static void
+gtk_statusbar_unmap (GtkWidget *widget)
+{
+  GtkStatusbar *statusbar;
+
+  statusbar = GTK_STATUSBAR (widget);
+
+  if (statusbar->grip_window)
+    gdk_window_hide (statusbar->grip_window);
+  
+  (* GTK_WIDGET_CLASS (parent_class)->unmap) (widget);
+}
+
+static gint
+gtk_statusbar_button_press (GtkWidget      *widget,
+                            GdkEventButton *event)
+{
+  GtkStatusbar *statusbar;
+  GtkWidget *ancestor;
+  
+  statusbar = GTK_STATUSBAR (widget);
+  
+  if (!statusbar->has_grip)
+    return FALSE;
+  
+  ancestor = gtk_widget_get_toplevel (widget);
+
+  if (!GTK_IS_WINDOW (ancestor))
+    return FALSE;
+
+  if (event->button == 1)
+    gtk_window_begin_resize_drag (GTK_WINDOW (ancestor),
+                                  GDK_WINDOW_EDGE_SOUTH_EAST,
+                                  event->button,
+                                  event->x_root, event->y_root,
+                                  event->time);
+  else if (event->button == 2)
+    gtk_window_begin_move_drag (GTK_WINDOW (ancestor),
+                                event->button,
+                                event->x_root, event->y_root,
+                                event->time);
+  else
+    return FALSE;
+  
+  return TRUE;
+}
+
+static gint
+gtk_statusbar_expose_event (GtkWidget      *widget,
+                            GdkEventExpose *event)
+{
+  GtkStatusbar *statusbar;
+  GdkRectangle rect;
+  
+  statusbar = GTK_STATUSBAR (widget);
+
+  GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
+
+  if (statusbar->has_grip)
+    {
+      get_grip_rect (statusbar, &rect);
+      
+      gtk_paint_resize_grip (widget->style,
+                             widget->window,
+                             GTK_WIDGET_STATE (widget),
+                             NULL,
+                             widget,
+                             "statusbar",
+                             GDK_WINDOW_EDGE_SOUTH_EAST,
+                             rect.x, rect.y,
+                             /* don't draw grip over the frame, though you
+                              * can click on the frame.
+                              */
+                             rect.width - widget->style->xthickness,
+                             rect.height - widget->style->ythickness);
+    }
+
+  return FALSE;
 }
Index: gtk/gtkstatusbar.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkstatusbar.h,v
retrieving revision 1.13
diff -u -u -r1.13 gtkstatusbar.h
--- gtk/gtkstatusbar.h	2001/02/03 01:09:40	1.13
+++ gtk/gtkstatusbar.h	2001/03/09 23:45:05
@@ -58,6 +58,10 @@
 
   guint seq_context_id;
   guint seq_message_id;
+
+  GdkWindow *grip_window;
+  
+  guint has_grip : 1;
 };
 
 struct _GtkStatusbarClass
@@ -91,6 +95,9 @@
 void       gtk_statusbar_remove        	(GtkStatusbar *statusbar,
 					 guint	       context_id,
 					 guint         message_id);
+void       gtk_statusbar_set_has_grip   (GtkStatusbar *statusbar,
+                                         gboolean      setting);
+gboolean   gtk_statusbar_get_has_grip   (GtkStatusbar *statusbar);
 
 
 
Index: gtk/gtkstyle.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkstyle.c,v
retrieving revision 1.53
diff -u -u -r1.53 gtkstyle.c
--- gtk/gtkstyle.c	2001/03/02 17:06:11	1.53
+++ gtk/gtkstyle.c	2001/03/09 23:45:05
@@ -285,7 +285,19 @@
                                          gint             x,
                                          gint             y,
                                          PangoLayout     *layout);
+static void gtk_default_draw_resize_grip (GtkStyle       *style,
+                                          GdkWindow      *window,
+                                          GtkStateType    state_type,
+                                          GdkRectangle   *area,
+                                          GtkWidget      *widget,
+                                          const gchar    *detail,
+                                          GdkWindowEdge   edge,
+                                          gint            x,
+                                          gint            y,
+                                          gint            width,
+                                          gint            height);
 
+
 static void gtk_style_shade (GdkColor *a, GdkColor *b, gdouble k);
 static void rgb_to_hls (gdouble *r, gdouble *g, gdouble *b);
 static void hls_to_rgb (gdouble *h, gdouble *l, gdouble *s);
@@ -452,6 +464,7 @@
   klass->draw_handle = gtk_default_draw_handle;
   klass->draw_expander = gtk_default_draw_expander;
   klass->draw_layout = gtk_default_draw_layout;
+  klass->draw_resize_grip = gtk_default_draw_resize_grip;
 }
 
 static void
@@ -1008,6 +1021,26 @@
 }
 
 void
+gtk_draw_resize_grip (GtkStyle     *style,
+                      GdkWindow    *window,
+                      GtkStateType  state_type,
+                      GdkWindowEdge edge,
+                      gint          x,
+                      gint          y,
+                      gint          width,
+                      gint          height)
+{
+  g_return_if_fail (GTK_IS_STYLE (style));
+  g_return_if_fail (GTK_STYLE_GET_CLASS (style)->draw_resize_grip != NULL);
+
+  GTK_STYLE_GET_CLASS (style)->draw_resize_grip (style, window, state_type,
+                                                 NULL, NULL, NULL,
+                                                 edge,
+                                                 x, y, width, height);
+}
+
+
+void
 gtk_style_set_background (GtkStyle    *style,
                           GdkWindow   *window,
                           GtkStateType state_type)
@@ -3749,6 +3782,79 @@
 }
 
 static void
+gtk_default_draw_resize_grip (GtkStyle       *style,
+                              GdkWindow      *window,
+                              GtkStateType    state_type,
+                              GdkRectangle   *area,
+                              GtkWidget      *widget,
+                              const gchar    *detail,
+                              GdkWindowEdge   edge,
+                              gint            x,
+                              gint            y,
+                              gint            width,
+                              gint            height)
+{
+  g_return_if_fail (GTK_IS_STYLE (style));
+  g_return_if_fail (window != NULL);
+  
+  if (area)
+    {
+      gdk_gc_set_clip_rectangle (style->light_gc[state_type], area);
+      gdk_gc_set_clip_rectangle (style->dark_gc[state_type], area);
+    }
+
+  /* make it square */
+  if (width != height)
+    width = height = MIN (width, height);
+  
+  switch (edge)
+    {
+    case GDK_WINDOW_EDGE_SOUTH_EAST:
+      {
+        gint xi, yi;
+
+        xi = x;
+        yi = y;
+
+        while (xi < (x + width - 3))
+          {
+            gdk_draw_line (window,
+                           style->light_gc[state_type],
+                           xi, y + height,
+                           x + width, yi);                           
+
+            ++xi;
+            ++yi;
+            
+            gdk_draw_line (window,
+                           style->dark_gc[state_type],
+                           xi, y + height,
+                           x + width, yi);                           
+
+            ++xi;
+            ++yi;
+            
+            gdk_draw_line (window,
+                           style->dark_gc[state_type],
+                           xi, y + height,
+                           x + width, yi);
+
+            xi += 3;
+            yi += 3;
+          }
+      }
+      break;
+
+    }
+  
+  if (area)
+    {
+      gdk_gc_set_clip_rectangle (style->light_gc[state_type], NULL);
+      gdk_gc_set_clip_rectangle (style->dark_gc[state_type], NULL);
+    }
+}
+
+static void
 gtk_style_shade (GdkColor *a,
                  GdkColor *b,
                  gdouble   k)
@@ -4313,4 +4419,25 @@
                                             widget, detail, x, y, layout);
 }
 
+void
+gtk_paint_resize_grip (GtkStyle      *style,
+                       GdkWindow     *window,
+                       GtkStateType   state_type,
+                       GdkRectangle  *area,
+                       GtkWidget     *widget,
+                       const gchar   *detail,
+                       GdkWindowEdge  edge,
+                       gint           x,
+                       gint           y,
+                       gint           width,
+                       gint           height)
+
+{
+  g_return_if_fail (GTK_IS_STYLE (style));
+  g_return_if_fail (GTK_STYLE_GET_CLASS (style)->draw_resize_grip != NULL);
+
+  GTK_STYLE_GET_CLASS (style)->draw_resize_grip (style, window, state_type,
+                                                 area, widget, detail,
+                                                 edge, x, y, width, height);
+}
 
Index: gtk/gtkstyle.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkstyle.h,v
retrieving revision 1.21
diff -u -u -r1.21 gtkstyle.h
--- gtk/gtkstyle.h	2001/02/03 01:09:40	1.21
+++ gtk/gtkstyle.h	2001/03/09 23:45:05
@@ -383,6 +383,17 @@
 				 gint			 x,
 				 gint			 y,
                                  PangoLayout            *layout);
+  void (*draw_resize_grip)      (GtkStyle		*style,
+				 GdkWindow		*window,
+				 GtkStateType		 state_type,
+				 GdkRectangle		*area,
+				 GtkWidget		*widget,
+				 const gchar		*detail,
+                                 GdkWindowEdge           edge,
+				 gint			 x,
+				 gint			 y,
+				 gint			 width,
+				 gint			 height);
   
 };
 
@@ -577,6 +588,15 @@
                           gint             y,
                           PangoLayout     *layout);
 
+void gtk_draw_resize_grip (GtkStyle       *style,
+                           GdkWindow      *window,
+                           GtkStateType    state_type,
+                           GdkWindowEdge   edge,
+                           gint            x,
+                           gint            y,
+                           gint            width,
+                           gint            height);
+
 void gtk_paint_hline      (GtkStyle        *style,
 			   GdkWindow       *window,
 			   GtkStateType     state_type,
@@ -797,6 +817,18 @@
                            gint             x,
                            gint             y,
                            PangoLayout     *layout);
+
+void gtk_paint_resize_grip (GtkStyle		*style,
+                            GdkWindow		*window,
+                            GtkStateType         state_type,
+                            GdkRectangle        *area,
+                            GtkWidget		*widget,
+                            const gchar		*detail,
+                            GdkWindowEdge        edge,
+                            gint                 x,
+                            gint                 y,
+                            gint                 width,
+                            gint           	 height);
 
 
 #ifdef __cplusplus
Index: gtk/gtkwidget.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwidget.c,v
retrieving revision 1.190
diff -u -u -r1.190 gtkwidget.c
--- gtk/gtkwidget.c	2001/03/08 06:14:42	1.190
+++ gtk/gtkwidget.c	2001/03/09 23:45:05
@@ -4502,7 +4502,7 @@
       
       gtk_default_direction = dir;
 
-      tmp_list = toplevels = gtk_window_list_toplevels ();
+      tmp_list = toplevels = _gtk_window_reference_toplevels ();
       while (tmp_list)
 	{
 	  gtk_widget_set_default_direction_recurse (tmp_list->data,
Index: gtk/gtkwindow.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwindow.c,v
retrieving revision 1.103
diff -u -u -r1.103 gtkwindow.c
--- gtk/gtkwindow.c	2001/03/08 06:14:43	1.103
+++ gtk/gtkwindow.c	2001/03/09 23:45:05
@@ -777,11 +777,10 @@
 /**
  * gtk_window_list_toplevels:
  * 
- * Returns a list of all existing toplevel windows. Each widget
- * in the list has a reference added to it; to free the
- * list, first unref each widget in the list, then free the list.
+ * Returns a list of all existing toplevel windows. The list must
+ * be freed with g_list_free(), but the list contents need not be unreferenced.
  * 
- * Return value: list of referenced toplevel widgets
+ * Return value: list of all toplevel windows
  **/
 GList*
 gtk_window_list_toplevels (void)
@@ -790,6 +789,18 @@
   GSList *slist;
 
   for (slist = toplevel_list; slist; slist = slist->next)
+    list = g_list_prepend (list, slist->data);
+
+  return list;
+}
+
+GList*
+_gtk_window_reference_toplevels (void)
+{
+  GList *list = NULL;
+  GSList *slist;
+
+  for (slist = toplevel_list; slist; slist = slist->next)
     list = g_list_prepend (list, gtk_widget_ref (slist->data));
 
   return list;
@@ -2002,7 +2013,7 @@
 
       _gtk_icon_set_invalidate_caches ();
       
-      toplevels = gtk_window_list_toplevels ();
+      toplevels = _gtk_window_reference_toplevels ();
       
       for (list = toplevels; list; list = list->next)
 	{
@@ -2498,18 +2509,6 @@
     }
 }
 
-
-/* Constrain a window size to obey the hints passed in geometry
- * and flags. The result will be stored in *new_width and *new_height
- *
- * This routine is partially borrowed from fvwm.
- *
- * Copyright 1993, Robert Nation
- *     You may use this code for any purpose, as long as the original
- *     copyright remains in the source code and all documentation
- *
- * which in turn borrows parts of the algorithm from uwm
- */
 static void 
 gtk_window_constrain_size (GtkWindow   *window,
 			   GdkGeometry *geometry,
@@ -2519,105 +2518,8 @@
 			   gint        *new_width,
 			   gint        *new_height)
 {
-  gint min_width = 0;
-  gint min_height = 0;
-  gint base_width = 0;
-  gint base_height = 0;
-  gint xinc = 1;
-  gint yinc = 1;
-  gint max_width = G_MAXINT;
-  gint max_height = G_MAXINT;
-  
-#define FLOOR(value, base)	( ((gint) ((value) / (base))) * (base) )
-
-  if ((flags & GDK_HINT_BASE_SIZE) && (flags & GDK_HINT_MIN_SIZE))
-    {
-      base_width = geometry->base_width;
-      base_height = geometry->base_height;
-      min_width = geometry->min_width;
-      min_height = geometry->min_height;
-    }
-  else if (flags & GDK_HINT_BASE_SIZE)
-    {
-      base_width = geometry->base_width;
-      base_height = geometry->base_height;
-      min_width = geometry->base_width;
-      min_height = geometry->base_height;
-    }
-  else if (flags & GDK_HINT_MIN_SIZE)
-    {
-      base_width = geometry->min_width;
-      base_height = geometry->min_height;
-      min_width = geometry->min_width;
-      min_height = geometry->min_height;
-    }
-
-  if (flags & GDK_HINT_MAX_SIZE)
-    {
-      max_width = geometry->max_width ;
-      max_height = geometry->max_height;
-    }
-
-  if (flags & GDK_HINT_RESIZE_INC)
-    {
-      xinc = MAX (xinc, geometry->width_inc);
-      yinc = MAX (yinc, geometry->height_inc);
-    }
-  
-  /* clamp width and height to min and max values
-   */
-  width = CLAMP (width, min_width, max_width);
-  height = CLAMP (height, min_height, max_height);
-  
-  /* shrink to base + N * inc
-   */
-  width = base_width + FLOOR (width - base_width, xinc);
-  height = base_height + FLOOR (height - base_height, yinc);
-
-  /* constrain aspect ratio, according to:
-   *
-   *                width     
-   * min_aspect <= -------- <= max_aspect
-   *                height    
-   */
-  
-  if (flags & GDK_HINT_ASPECT &&
-      geometry->min_aspect > 0 &&
-      geometry->max_aspect > 0)
-    {
-      gint delta;
-
-      if (geometry->min_aspect * height > width)
-	{
-	  delta = FLOOR (height - width * geometry->min_aspect, yinc);
-	  if (height - delta >= min_height)
-	    height -= delta;
-	  else
-	    { 
-	      delta = FLOOR (height * geometry->min_aspect - width, xinc);
-	      if (width + delta <= max_width) 
-		width += delta;
-	    }
-	}
-      
-      if (geometry->max_aspect * height < width)
-	{
-	  delta = FLOOR (width - height * geometry->max_aspect, xinc);
-	  if (width - delta >= min_width) 
-	    width -= delta;
-	  else
-	    {
-	      delta = FLOOR (width / geometry->max_aspect - height, yinc);
-	      if (height + delta <= max_height)
-		height += delta;
-	    }
-	}
-    }
-
-#undef FLOOR
-  
-  *new_width = width;
-  *new_height = height;
+  gdk_window_constrain_size (geometry, flags, width, height,
+                             new_width, new_height);
 }
 
 /* Compute the set of geometry hints and flags for a window
@@ -3195,9 +3097,87 @@
   if (toplevel != NULL)
     gdk_window_unmaximize (toplevel);
 }
-
 
-
-
+/**
+ * gtk_window_begin_resize_drag:
+ * @window: a #GtkWindow
+ * @button: mouse button that initiated the drag
+ * @edge: position of the resize control
+ * @root_x: X position where the user clicked to initiate the drag, in root window coordinates
+ * @root_y: Y position where the user clicked to initiate the drag
+ * @timestamp: timestamp from the click event that initiated the drag
+ *
+ * Starts resizing a window. This function is used if an application
+ * has window resizing controls. When GDK can support it, the resize
+ * will be done using the standard mechanism for the window manager or
+ * windowing system. Otherwise, GDK will try to emulate window
+ * resizing, potentially not all that well, depending on the windowing system.
+ * 
+ **/
+void
+gtk_window_begin_resize_drag  (GtkWindow    *window,
+                               GdkWindowEdge edge,
+                               gint          button,
+                               gint          root_x,
+                               gint          root_y,
+                               guint32       timestamp)
+{
+  GtkWidget *widget;
+  GdkWindow *toplevel;
+  
+  g_return_if_fail (GTK_IS_WINDOW (window));
+  g_return_if_fail (GTK_WIDGET_VISIBLE (window));
+  
+  widget = GTK_WIDGET (window);
+  
+  if (window->frame)
+    toplevel = window->frame;
+  else
+    toplevel = widget->window;
+  
+  gdk_window_begin_resize_drag (toplevel,
+                                edge, button,
+                                root_x, root_y,
+                                timestamp);
+}
 
 
+/**
+ * gtk_window_begin_move_drag:
+ * @button: mouse button that initiated the drag
+ * @root_x: X position where the user clicked to initiate the drag, in root window coordinates
+ * @root_y: Y position where the user clicked to initiate the drag
+ * @timestamp: timestamp from the click event that initiated the drag
+ *
+ * Starts moving a window. This function is used if an application
+ * has window movement grips. When GDK can support it, the window movement
+ * will be done using the standard mechanism for the window manager or
+ * windowing system. Otherwise, GDK will try to emulate window
+ * movement, potentially not all that well, depending on the windowing system.
+ * 
+ **/
+void
+gtk_window_begin_move_drag  (GtkWindow *window,
+                             gint       button,
+                             gint       root_x,
+                             gint       root_y,
+                             guint32    timestamp)
+{
+  GtkWidget *widget;
+  GdkWindow *toplevel;
+  
+  g_return_if_fail (GTK_IS_WINDOW (window));
+  g_return_if_fail (GTK_WIDGET_VISIBLE (window));
+  
+  widget = GTK_WIDGET (window);
+  
+  if (window->frame)
+    toplevel = window->frame;
+  else
+    toplevel = widget->window;
+  
+  gdk_window_begin_move_drag (toplevel,
+                              button,
+                              root_x, root_y,
+                              timestamp);
+}
Index: gtk/gtkwindow.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwindow.h,v
retrieving revision 1.28
diff -u -u -r1.28 gtkwindow.h
--- gtk/gtkwindow.h	2001/03/07 21:10:44	1.28
+++ gtk/gtkwindow.h	2001/03/09 23:45:05
@@ -167,7 +167,7 @@
 /* If window is set modal, input will be grabbed when show and released when hide */
 void       gtk_window_set_modal                (GtkWindow           *window,
                                                 gboolean             modal);
-GList*	   gtk_window_list_toplevels	       (void);
+GList*	   gtk_window_list_toplevels           (void);
 
 /* Get the "built-in" accel group (convenience thing) */
 GtkAccelGroup* gtk_window_get_default_accel_group (GtkWindow *window);
@@ -180,7 +180,19 @@
 void     gtk_window_maximize      (GtkWindow *window);
 void     gtk_window_unmaximize    (GtkWindow *window);
 
+void gtk_window_begin_resize_drag (GtkWindow     *window,
+                                   GdkWindowEdge  edge,
+                                   gint           button,
+                                   gint           root_x,
+                                   gint           root_y,
+                                   guint32        timestamp);
+void gtk_window_begin_move_drag   (GtkWindow     *window,
+                                   gint           button,
+                                   gint           root_x,
+                                   gint           root_y,
+                                   guint32        timestamp);
 
+
 /* --- internal functions --- */
 void       gtk_window_set_focus                (GtkWindow           *window,
 						GtkWidget           *focus);
@@ -198,6 +210,7 @@
 						gint                 height,
 						gint                *new_width,
 						gint                *new_height);
+GList*	   _gtk_window_reference_toplevels     (void);
 
 #ifdef __cplusplus
 }





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