Index: src/common.h
===================================================================
RCS file: /cvs/gnome/metacity/src/common.h,v
retrieving revision 1.42
diff -u -p -r1.42 common.h
--- src/common.h	1 Oct 2006 22:30:01 -0000	1.42
+++ src/common.h	7 Oct 2006 03:57:48 -0000
@@ -46,7 +46,8 @@ typedef enum
   META_FRAME_ALLOWS_SHADE             = 1 << 10,
   META_FRAME_ALLOWS_MOVE              = 1 << 11,
   META_FRAME_FULLSCREEN               = 1 << 12,
-  META_FRAME_IS_FLASHING              = 1 << 13
+  META_FRAME_IS_FLASHING              = 1 << 13,
+  META_FRAME_ABOVE                    = 1 << 14
 } MetaFrameFlags;
 
 typedef enum
@@ -131,7 +132,13 @@ typedef enum
   META_GRAB_OP_CLICKING_MAXIMIZE,
   META_GRAB_OP_CLICKING_UNMAXIMIZE,
   META_GRAB_OP_CLICKING_DELETE,
-  META_GRAB_OP_CLICKING_MENU
+  META_GRAB_OP_CLICKING_MENU,
+  META_GRAB_OP_CLICKING_SHADE,
+  META_GRAB_OP_CLICKING_UNSHADE,
+  META_GRAB_OP_CLICKING_ABOVE,
+  META_GRAB_OP_CLICKING_UNABOVE,
+  META_GRAB_OP_CLICKING_STICK,
+  META_GRAB_OP_CLICKING_UNSTICK
 } MetaGrabOp;
 
 typedef enum
@@ -226,6 +233,12 @@ typedef enum
   META_BUTTON_FUNCTION_MINIMIZE,
   META_BUTTON_FUNCTION_MAXIMIZE,
   META_BUTTON_FUNCTION_CLOSE,
+  META_BUTTON_FUNCTION_SHADE,
+  META_BUTTON_FUNCTION_ABOVE,
+  META_BUTTON_FUNCTION_STICK,
+  META_BUTTON_FUNCTION_UNSHADE,
+  META_BUTTON_FUNCTION_UNABOVE,
+  META_BUTTON_FUNCTION_UNSTICK,
   META_BUTTON_FUNCTION_LAST
 } MetaButtonFunction;
 
Index: src/core.c
===================================================================
RCS file: /cvs/gnome/metacity/src/core.c,v
retrieving revision 1.52
diff -u -p -r1.52 core.c
--- src/core.c	1 Oct 2006 22:30:01 -0000	1.52
+++ src/core.c	7 Oct 2006 03:57:49 -0000
@@ -29,6 +29,17 @@
 #include "workspace.h"
 #include "prefs.h"
 
+/* Looks up the MetaWindow representing the frame of the given X window.
+ * Used as a helper function by a bunch of the functions below.
+ *
+ * FIXME: The functions that use this function throw the result away
+ * after use. Many of these functions tend to be called in small groups,
+ * which results in get_window() getting called several times in succession
+ * with the same parameters. We should profile to see whether this wastes
+ * much time, and if it does we should look into a generalised
+ * meta_core_get_window_info() which takes a bunch of pointers to variables
+ * to put its results in, and only fills in the non-null ones.
+ */
 static MetaWindow *
 get_window (Display *xdisplay,
 	    Window   frame_xwindow)
@@ -63,6 +74,19 @@ meta_core_get_client_size (Display *xdis
 }
 
 gboolean
+meta_core_window_has_frame (Display *xdisplay,
+                            Window frame_xwindow)
+{
+  MetaDisplay *display;
+  MetaWindow *window;
+  
+  display = meta_display_for_x_display (xdisplay);
+  window = meta_display_lookup_x_window (display, frame_xwindow);
+
+  return window != NULL && window->frame != NULL;
+}
+
+gboolean
 meta_core_titlebar_is_onscreen (Display *xdisplay,
                                 Window   frame_xwindow)
 {
@@ -369,6 +393,24 @@ meta_core_unstick (Display *xdisplay,
 }
 
 void
+meta_core_make_above (Display *xdisplay,
+                      Window   frame_xwindow)
+{
+  MetaWindow *window = get_window (xdisplay, frame_xwindow);
+
+  meta_window_make_above (window);
+}
+
+void
+meta_core_unmake_above (Display *xdisplay,
+                        Window   frame_xwindow)
+{
+  MetaWindow *window = get_window (xdisplay, frame_xwindow);
+
+  meta_window_unmake_above (window);
+}
+
+void
 meta_core_stick (Display *xdisplay,
                  Window   frame_xwindow)
 {
@@ -710,5 +752,33 @@ meta_core_increment_event_serial (Displa
   display = meta_display_for_x_display (xdisplay);
 
   meta_display_increment_event_serial (display);
+}
+
+void
+meta_invalidate_default_icons (void)
+{
+  GSList *displays, *windows;
+
+  for (displays = meta_displays_list ();
+       displays != NULL;
+       displays = displays->next)
+    {
+
+      for (windows = meta_display_list_windows (displays->data);
+           windows != NULL;
+           windows = windows->next)
+        {
+
+          MetaWindow *window = (MetaWindow*)windows->data;
+
+          if (window->icon_cache.origin == USING_FALLBACK_ICON)
+            {
+              meta_icon_cache_free (&(window->icon_cache));
+              meta_window_update_icon_now (window);
+            }
+        }
+
+      g_slist_free (windows);
+    }
 }
 
Index: src/core.h
===================================================================
RCS file: /cvs/gnome/metacity/src/core.h,v
retrieving revision 1.30
diff -u -p -r1.30 core.h
--- src/core.h	1 Oct 2006 22:30:01 -0000	1.30
+++ src/core.h	7 Oct 2006 03:57:49 -0000
@@ -37,6 +37,10 @@ void meta_core_get_client_size (Display 
 gboolean meta_core_titlebar_is_onscreen (Display *xdisplay,
                                          Window   frame_xwindow);
 
+gboolean meta_core_window_has_frame (Display *xdisplay,
+                                     Window   frame_xwindow);
+
+
 Window meta_core_get_client_xwindow (Display *xdisplay,
                                      Window   frame_xwindow);
 
@@ -106,6 +110,10 @@ void meta_core_unstick          (Display
                                  Window   frame_xwindow);
 void meta_core_stick            (Display *xdisplay,
                                  Window   frame_xwindow);
+void meta_core_unmake_above     (Display *xdisplay,
+                                 Window   frame_xwindow);
+void meta_core_make_above       (Display *xdisplay,
+                                 Window   frame_xwindow);
 void meta_core_change_workspace (Display *xdisplay,
                                  Window   frame_xwindow,
                                  int      new_workspace);
@@ -174,6 +182,8 @@ void       meta_core_get_screen_size (Di
 void meta_core_increment_event_serial (Display *display);
 
 int meta_ui_get_last_event_serial (Display *xdisplay);
+
+void meta_invalidate_default_icons (void);
 
 #endif
 
Index: src/frame.c
===================================================================
RCS file: /cvs/gnome/metacity/src/frame.c,v
retrieving revision 1.65
diff -u -p -r1.65 frame.c
--- src/frame.c	1 Oct 2006 22:30:01 -0000	1.65
+++ src/frame.c	7 Oct 2006 03:57:49 -0000
@@ -279,6 +279,9 @@ meta_frame_get_flags (MetaFrame *frame)
   if (frame->is_flashing)
     flags |= META_FRAME_IS_FLASHING;
 
+  if (frame->window->wm_state_above)
+    flags |= META_FRAME_ABOVE;
+  
   return flags;
 }
 
Index: src/frames.c
===================================================================
RCS file: /cvs/gnome/metacity/src/frames.c,v
retrieving revision 1.95
diff -u -p -r1.95 frames.c
--- src/frames.c	1 Oct 2006 22:30:01 -0000	1.95
+++ src/frames.c	7 Oct 2006 03:57:52 -0000
@@ -24,6 +24,7 @@
  */
 
 #include <config.h>
+#include <math.h>
 #include "boxes.h"
 #include "frames.h"
 #include "util.h"
@@ -757,10 +758,10 @@ meta_frames_apply_shapes (MetaFrames *fr
 
   meta_frames_calc_geometry (frames, frame, &fgeom);
 
-  if (!(fgeom.top_left_corner_rounded ||
-        fgeom.top_right_corner_rounded ||
-        fgeom.bottom_left_corner_rounded ||
-        fgeom.bottom_right_corner_rounded ||
+  if (!(fgeom.top_left_corner_rounded_radius != 0 ||
+        fgeom.top_right_corner_rounded_radius != 0 ||
+        fgeom.bottom_left_corner_rounded_radius != 0 ||
+        fgeom.bottom_right_corner_rounded_radius != 0 ||
         window_has_shape))
     {
       if (frame->shape_applied)
@@ -785,102 +786,72 @@ meta_frames_apply_shapes (MetaFrames *fr
   
   corners_xregion = XCreateRegion ();
   
-  if (fgeom.top_left_corner_rounded)
+  if (fgeom.top_left_corner_rounded_radius != 0)
     {
-      xrect.x = 0;
-      xrect.y = 0;
-      xrect.width = 5;
-      xrect.height = 1;
-      
-      XUnionRectWithRegion (&xrect, corners_xregion, corners_xregion);
-
-      xrect.y = 1;
-      xrect.width = 3;
-      XUnionRectWithRegion (&xrect, corners_xregion, corners_xregion);
-      
-      xrect.y = 2;
-      xrect.width = 2;
-      XUnionRectWithRegion (&xrect, corners_xregion, corners_xregion);
+      const int radius = fgeom.top_left_corner_rounded_radius;
+      int i;
 
-      xrect.y = 3;
-      xrect.width = 1;
-      xrect.height = 2;
-      XUnionRectWithRegion (&xrect, corners_xregion, corners_xregion);
+      for (i=0; i<radius; i++)
+        {
+          const int width = 1 + (radius - floor(sqrt(radius*radius - (radius-i)*(radius-i))));
+          xrect.x = 0;
+          xrect.y = i;
+          xrect.width = width;
+          xrect.height = 1;
+          
+          XUnionRectWithRegion (&xrect, corners_xregion, corners_xregion);
+        }
     }
 
-  if (fgeom.top_right_corner_rounded)
+  if (fgeom.top_right_corner_rounded_radius != 0)
     {
-      xrect.x = new_window_width - 5;
-      xrect.y = 0;
-      xrect.width = 5;
-      xrect.height = 1;
-      
-      XUnionRectWithRegion (&xrect, corners_xregion, corners_xregion);
-
-      xrect.y = 1;
-      xrect.x = new_window_width - 3;
-      xrect.width = 3;
-      XUnionRectWithRegion (&xrect, corners_xregion, corners_xregion);
-      
-      xrect.y = 2;
-      xrect.x = new_window_width - 2;
-      xrect.width = 2;
-      XUnionRectWithRegion (&xrect, corners_xregion, corners_xregion);
+      const int radius = fgeom.top_right_corner_rounded_radius;
+      int i;
 
-      xrect.y = 3;
-      xrect.x = new_window_width - 1;
-      xrect.width = 1;
-      xrect.height = 2;
-      XUnionRectWithRegion (&xrect, corners_xregion, corners_xregion);
+      for (i=0; i<radius; i++)
+        {
+          const int width = 1 + (radius - floor(sqrt(radius*radius - (radius-i)*(radius-i))));
+          xrect.x = new_window_width - width;
+          xrect.y = i;
+          xrect.width = width;
+          xrect.height = 1;
+          
+          XUnionRectWithRegion (&xrect, corners_xregion, corners_xregion);
+        }
     }
 
-  if (fgeom.bottom_left_corner_rounded)
+  if (fgeom.bottom_left_corner_rounded_radius != 0)
     {
-      xrect.x = 0;
-      xrect.y = new_window_height - 1;
-      xrect.width = 5;
-      xrect.height = 1;
-      
-      XUnionRectWithRegion (&xrect, corners_xregion, corners_xregion);
-      
-      xrect.y = new_window_height - 2;
-      xrect.width = 3;
-      XUnionRectWithRegion (&xrect, corners_xregion, corners_xregion);
-      
-      xrect.y = new_window_height - 3;
-      xrect.width = 2;
-      XUnionRectWithRegion (&xrect, corners_xregion, corners_xregion);
+      const int radius = fgeom.bottom_left_corner_rounded_radius;
+      int i;
 
-      xrect.y = new_window_height - 5;
-      xrect.width = 1;
-      xrect.height = 2;
-      XUnionRectWithRegion (&xrect, corners_xregion, corners_xregion);
+      for (i=0; i<radius; i++)
+        {
+          const int width = 1 + (radius - floor(sqrt(radius*radius - (radius-i)*(radius-i))));
+          xrect.x = 0;
+          xrect.y = new_window_height - i;
+          xrect.width = width;
+          xrect.height = 1;
+          
+          XUnionRectWithRegion (&xrect, corners_xregion, corners_xregion);
+        }
     }
 
-  if (fgeom.bottom_right_corner_rounded)
+  if (fgeom.bottom_right_corner_rounded_radius != 0)
     {
-      xrect.x = new_window_width - 5;
-      xrect.y = new_window_height - 1;
-      xrect.width = 5;
-      xrect.height = 1;
-      
-      XUnionRectWithRegion (&xrect, corners_xregion, corners_xregion);
-      
-      xrect.y = new_window_height - 2;
-      xrect.x = new_window_width - 3;
-      xrect.width = 3;
-      XUnionRectWithRegion (&xrect, corners_xregion, corners_xregion);
-      
-      xrect.y = new_window_height - 3;
-      xrect.x = new_window_width - 2;
-      xrect.width = 2;
-      XUnionRectWithRegion (&xrect, corners_xregion, corners_xregion);
+      const int radius = fgeom.bottom_right_corner_rounded_radius;
+      int i;
 
-      xrect.y = new_window_height - 5;
-      xrect.x = new_window_width - 1;
-      xrect.width = 1;
-      xrect.height = 2;
-      XUnionRectWithRegion (&xrect, corners_xregion, corners_xregion);
+      for (i=0; i<radius; i++)
+        {
+          const int width = 1 + (radius - floor(sqrt(radius*radius - (radius-i)*(radius-i))));
+          xrect.x = new_window_width - width;
+          xrect.y = new_window_height - i;
+          xrect.width = width;
+          xrect.height = 1;
+          
+          XUnionRectWithRegion (&xrect, corners_xregion, corners_xregion);
+        }
     }
   
   window_xregion = XCreateRegion ();
@@ -1102,6 +1073,24 @@ show_tip_now (MetaFrames *frames)
     case META_FRAME_CONTROL_UNMAXIMIZE:
       tiptext = _("Unmaximize Window");
       break;
+    case META_FRAME_CONTROL_SHADE:
+      tiptext = _("Roll Up Window");
+      break;
+    case META_FRAME_CONTROL_UNSHADE:
+      tiptext = _("Unroll Window");
+      break;
+    case META_FRAME_CONTROL_ABOVE:
+      tiptext = _("Keep Window On Top");
+      break;
+    case META_FRAME_CONTROL_UNABOVE:
+      tiptext = _("Remove Window From Top");
+      break;
+    case META_FRAME_CONTROL_STICK:
+      tiptext = _("Always On Visible Workspace");
+      break;
+    case META_FRAME_CONTROL_UNSTICK:
+      tiptext = _("Put Window On Only One Workspace");
+      break;
     case META_FRAME_CONTROL_RESIZE_SE:
       break;
     case META_FRAME_CONTROL_RESIZE_S:
@@ -1308,6 +1297,12 @@ meta_frames_button_press_event (GtkWidge
        control == META_FRAME_CONTROL_UNMAXIMIZE ||
        control == META_FRAME_CONTROL_MINIMIZE ||
        control == META_FRAME_CONTROL_DELETE ||
+       control == META_FRAME_CONTROL_SHADE ||
+       control == META_FRAME_CONTROL_UNSHADE ||
+       control == META_FRAME_CONTROL_ABOVE ||
+       control == META_FRAME_CONTROL_UNABOVE ||
+       control == META_FRAME_CONTROL_STICK ||
+       control == META_FRAME_CONTROL_UNSTICK ||
        control == META_FRAME_CONTROL_MENU))
     {
       MetaGrabOp op = META_GRAB_OP_NONE;
@@ -1329,6 +1324,24 @@ meta_frames_button_press_event (GtkWidge
         case META_FRAME_CONTROL_MENU:
           op = META_GRAB_OP_CLICKING_MENU;
           break;
+        case META_FRAME_CONTROL_SHADE:
+          op = META_GRAB_OP_CLICKING_SHADE;
+          break;
+        case META_FRAME_CONTROL_UNSHADE:
+          op = META_GRAB_OP_CLICKING_UNSHADE;
+          break;
+        case META_FRAME_CONTROL_ABOVE:
+          op = META_GRAB_OP_CLICKING_ABOVE;
+          break;
+        case META_FRAME_CONTROL_UNABOVE:
+          op = META_GRAB_OP_CLICKING_UNABOVE;
+          break;
+        case META_FRAME_CONTROL_STICK:
+          op = META_GRAB_OP_CLICKING_STICK;
+          break;
+        case META_FRAME_CONTROL_UNSTICK:
+          op = META_GRAB_OP_CLICKING_UNSTICK;
+          break;
         default:
           g_assert_not_reached ();
           break;
@@ -1567,6 +1580,48 @@ meta_frames_button_release_event    (Gtk
           meta_core_end_grab_op (gdk_display, event->time);
           break;
 
+        case META_GRAB_OP_CLICKING_SHADE:
+          if (control == META_FRAME_CONTROL_SHADE)
+            meta_core_shade (gdk_display, frame->xwindow, event->time);
+          
+          meta_core_end_grab_op (gdk_display, event->time);
+          break;
+ 
+        case META_GRAB_OP_CLICKING_UNSHADE:
+          if (control == META_FRAME_CONTROL_UNSHADE)
+            meta_core_unshade (gdk_display, frame->xwindow, event->time);
+
+          meta_core_end_grab_op (gdk_display, event->time);
+          break;
+
+        case META_GRAB_OP_CLICKING_ABOVE:
+          if (control == META_FRAME_CONTROL_ABOVE)
+            meta_core_make_above (gdk_display, frame->xwindow);
+          
+          meta_core_end_grab_op (gdk_display, event->time);
+          break;
+ 
+        case META_GRAB_OP_CLICKING_UNABOVE:
+          if (control == META_FRAME_CONTROL_UNABOVE)
+            meta_core_unmake_above (gdk_display, frame->xwindow);
+
+          meta_core_end_grab_op (gdk_display, event->time);
+          break;
+
+        case META_GRAB_OP_CLICKING_STICK:
+          if (control == META_FRAME_CONTROL_STICK)
+            meta_core_stick (gdk_display, frame->xwindow);
+
+          meta_core_end_grab_op (gdk_display, event->time);
+          break;
+ 
+        case META_GRAB_OP_CLICKING_UNSTICK:
+          if (control == META_FRAME_CONTROL_UNSTICK)
+            meta_core_unstick (gdk_display, frame->xwindow);
+
+          meta_core_end_grab_op (gdk_display, event->time);
+          break;
+          
         default:
           break;
         }
@@ -1590,6 +1645,7 @@ meta_frames_update_prelit_control (MetaF
   MetaFrameControl old_control;
   MetaCursor cursor;
 
+
   meta_verbose ("Updating prelit control from %u to %u\n",
                 frame->prelit_control, control);
   
@@ -1613,6 +1669,18 @@ meta_frames_update_prelit_control (MetaF
       break;
     case META_FRAME_CONTROL_UNMAXIMIZE:
       break;
+    case META_FRAME_CONTROL_SHADE:
+      break;
+    case META_FRAME_CONTROL_UNSHADE:
+      break;
+    case META_FRAME_CONTROL_ABOVE:
+      break;
+    case META_FRAME_CONTROL_UNABOVE:
+      break;
+    case META_FRAME_CONTROL_STICK:
+      break;
+    case META_FRAME_CONTROL_UNSTICK:
+      break;
     case META_FRAME_CONTROL_RESIZE_SE:
       cursor = META_CURSOR_SE_RESIZE;
       break;
@@ -1650,6 +1718,12 @@ meta_frames_update_prelit_control (MetaF
     case META_FRAME_CONTROL_MINIMIZE:
     case META_FRAME_CONTROL_MAXIMIZE:
     case META_FRAME_CONTROL_DELETE:
+    case META_FRAME_CONTROL_SHADE:
+    case META_FRAME_CONTROL_UNSHADE:
+    case META_FRAME_CONTROL_ABOVE:
+    case META_FRAME_CONTROL_UNABOVE:
+    case META_FRAME_CONTROL_STICK:
+    case META_FRAME_CONTROL_UNSTICK:
     case META_FRAME_CONTROL_UNMAXIMIZE:
       /* leave control set */
       break;
@@ -1698,6 +1772,12 @@ meta_frames_motion_notify_event     (Gtk
     case META_GRAB_OP_CLICKING_MINIMIZE:
     case META_GRAB_OP_CLICKING_MAXIMIZE:
     case META_GRAB_OP_CLICKING_UNMAXIMIZE:
+    case META_GRAB_OP_CLICKING_SHADE:
+    case META_GRAB_OP_CLICKING_UNSHADE:
+    case META_GRAB_OP_CLICKING_ABOVE:
+    case META_GRAB_OP_CLICKING_UNABOVE:
+    case META_GRAB_OP_CLICKING_STICK:
+    case META_GRAB_OP_CLICKING_UNSTICK:
       {
         MetaFrameControl control;
         int x, y;
@@ -1716,8 +1796,20 @@ meta_frames_motion_notify_event     (Gtk
                 grab_op == META_GRAB_OP_CLICKING_MINIMIZE) ||
                (control == META_FRAME_CONTROL_MAXIMIZE &&
                 (grab_op == META_GRAB_OP_CLICKING_MAXIMIZE ||
-                 grab_op == META_GRAB_OP_CLICKING_UNMAXIMIZE))))
-          control = META_FRAME_CONTROL_NONE;
+                 grab_op == META_GRAB_OP_CLICKING_UNMAXIMIZE)) ||
+               (control == META_FRAME_CONTROL_SHADE &&
+                grab_op == META_GRAB_OP_CLICKING_SHADE) ||
+               (control == META_FRAME_CONTROL_UNSHADE &&
+                grab_op == META_GRAB_OP_CLICKING_UNSHADE) ||
+               (control == META_FRAME_CONTROL_ABOVE &&
+                grab_op == META_GRAB_OP_CLICKING_ABOVE) ||
+               (control == META_FRAME_CONTROL_UNABOVE &&
+                grab_op == META_GRAB_OP_CLICKING_UNABOVE) ||
+               (control == META_FRAME_CONTROL_STICK &&
+                grab_op == META_GRAB_OP_CLICKING_STICK) ||
+               (control == META_FRAME_CONTROL_UNSTICK &&
+                grab_op == META_GRAB_OP_CLICKING_UNSTICK)))
+           control = META_FRAME_CONTROL_NONE;
         
         /* Update prelit control and cursor */
 	meta_frames_update_prelit_control (frames, frame, control);
@@ -2097,6 +2189,42 @@ meta_frames_paint_to_drawable (MetaFrame
       else
         button_states[META_BUTTON_TYPE_MAXIMIZE] = META_BUTTON_STATE_PRELIGHT;
       break;
+    case META_FRAME_CONTROL_SHADE:
+      if (grab_op == META_GRAB_OP_CLICKING_SHADE)
+        button_states[META_BUTTON_TYPE_SHADE] = META_BUTTON_STATE_PRESSED;
+      else
+        button_states[META_BUTTON_TYPE_SHADE] = META_BUTTON_STATE_PRELIGHT;
+      break;
+    case META_FRAME_CONTROL_UNSHADE:
+      if (grab_op == META_GRAB_OP_CLICKING_UNSHADE)
+        button_states[META_BUTTON_TYPE_UNSHADE] = META_BUTTON_STATE_PRESSED;
+      else
+        button_states[META_BUTTON_TYPE_UNSHADE] = META_BUTTON_STATE_PRELIGHT;
+      break;
+    case META_FRAME_CONTROL_ABOVE:
+      if (grab_op == META_GRAB_OP_CLICKING_ABOVE)
+        button_states[META_BUTTON_TYPE_ABOVE] = META_BUTTON_STATE_PRESSED;
+      else
+        button_states[META_BUTTON_TYPE_ABOVE] = META_BUTTON_STATE_PRELIGHT;
+      break;
+    case META_FRAME_CONTROL_UNABOVE:
+      if (grab_op == META_GRAB_OP_CLICKING_UNABOVE)
+        button_states[META_BUTTON_TYPE_UNABOVE] = META_BUTTON_STATE_PRESSED;
+      else
+        button_states[META_BUTTON_TYPE_UNABOVE] = META_BUTTON_STATE_PRELIGHT;
+      break;
+    case META_FRAME_CONTROL_STICK:
+      if (grab_op == META_GRAB_OP_CLICKING_STICK)
+        button_states[META_BUTTON_TYPE_STICK] = META_BUTTON_STATE_PRESSED;
+      else
+        button_states[META_BUTTON_TYPE_STICK] = META_BUTTON_STATE_PRELIGHT;
+      break;
+    case META_FRAME_CONTROL_UNSTICK:
+      if (grab_op == META_GRAB_OP_CLICKING_UNSTICK)
+        button_states[META_BUTTON_TYPE_UNSTICK] = META_BUTTON_STATE_PRESSED;
+      else
+        button_states[META_BUTTON_TYPE_UNSTICK] = META_BUTTON_STATE_PRELIGHT;
+      break;
     case META_FRAME_CONTROL_DELETE:
       if (grab_op == META_GRAB_OP_CLICKING_DELETE)
         button_states[META_BUTTON_TYPE_CLOSE] = META_BUTTON_STATE_PRESSED;
@@ -2182,17 +2310,52 @@ static void
 meta_frames_set_window_background (MetaFrames   *frames,
                                    MetaUIFrame  *frame)
 {
-  gtk_style_set_background (GTK_WIDGET (frames)->style,
-                            frame->window, GTK_STATE_NORMAL);
+  MetaFrameFlags flags;
+  MetaFrameType type;
+  MetaFrameStyle *style;
+  gboolean frame_exists;
 
-#if 0
-  /* This is what we want for transparent background */
-  {
-    col.pixel = 0;
-    gdk_window_set_background (window, &col);
-  }
-#endif
-}
+  frame_exists = meta_core_window_has_frame (gdk_display, frame->xwindow);
+
+  if (frame_exists)
+    {
+      flags = meta_core_get_frame_flags (gdk_display, frame->xwindow);
+      type = meta_core_get_frame_type (gdk_display, frame->xwindow);
+      style = meta_theme_get_frame_style (meta_theme_get_current (),
+                                          type, flags);
+    }
+
+  if (frame_exists && style->window_background_color != NULL)
+    {
+      GdkColor color;
+      GdkVisual *visual;
+
+      meta_color_spec_render (style->window_background_color,
+                              GTK_WIDGET (frames),
+                              &color);
+
+      /* Fill in color.pixel */
+
+      gdk_rgb_find_color (gtk_widget_get_colormap (GTK_WIDGET (frames)),
+                          &color);
+
+      /* Set A in ARGB to window_background_alpha, if we have ARGB */
+
+      visual = gtk_widget_get_visual (GTK_WIDGET (frames));
+      if (visual->depth == 32) /* we have ARGB */
+        {
+          color.pixel = (color.pixel & 0xffffff) &
+            style->window_background_alpha << 24;
+        }
+
+      gdk_window_set_background (frame->window, &color);
+    }
+  else
+    {
+      gtk_style_set_background (GTK_WIDGET (frames)->style,
+                                frame->window, GTK_STATE_NORMAL);
+    }
+ }
 
 static gboolean
 meta_frames_enter_notify_event      (GtkWidget           *widget,
@@ -2259,6 +2422,24 @@ control_rect (MetaFrameControl control,
     case META_FRAME_CONTROL_UNMAXIMIZE:
       rect = &fgeom->max_rect.visible;
       break;
+    case META_FRAME_CONTROL_SHADE:
+      rect = &fgeom->shade_rect.visible;
+      break;
+    case META_FRAME_CONTROL_UNSHADE:
+      rect = &fgeom->unshade_rect.visible;
+      break;
+    case META_FRAME_CONTROL_ABOVE:
+      rect = &fgeom->above_rect.visible;
+      break;
+    case META_FRAME_CONTROL_UNABOVE:
+      rect = &fgeom->unabove_rect.visible;
+      break;
+    case META_FRAME_CONTROL_STICK:
+      rect = &fgeom->stick_rect.visible;
+      break;
+    case META_FRAME_CONTROL_UNSTICK:
+      rect = &fgeom->unstick_rect.visible;
+      break;
     case META_FRAME_CONTROL_RESIZE_SE:
       break;
     case META_FRAME_CONTROL_RESIZE_S:
@@ -2336,6 +2517,36 @@ get_control (MetaFrames *frames,
         return META_FRAME_CONTROL_MAXIMIZE;
     }
       
+  if (POINT_IN_RECT (x, y, fgeom.shade_rect.clickable))
+    {
+      return META_FRAME_CONTROL_SHADE;
+    }
+
+  if (POINT_IN_RECT (x, y, fgeom.unshade_rect.clickable))
+    {
+      return META_FRAME_CONTROL_UNSHADE;
+    }
+
+  if (POINT_IN_RECT (x, y, fgeom.above_rect.clickable))
+    {
+      return META_FRAME_CONTROL_ABOVE;
+    }
+
+  if (POINT_IN_RECT (x, y, fgeom.unabove_rect.clickable))
+    {
+      return META_FRAME_CONTROL_UNABOVE;
+    }
+
+  if (POINT_IN_RECT (x, y, fgeom.stick_rect.clickable))
+    {
+      return META_FRAME_CONTROL_STICK;
+    }
+
+  if (POINT_IN_RECT (x, y, fgeom.unstick_rect.clickable))
+    {
+      return META_FRAME_CONTROL_UNSTICK;
+    }
+
   /* South resize always has priority over north resize,
    * in case of overlap.
    */
Index: src/frames.h
===================================================================
RCS file: /cvs/gnome/metacity/src/frames.h,v
retrieving revision 1.29
diff -u -p -r1.29 frames.h
--- src/frames.h	1 Oct 2006 22:30:01 -0000	1.29
+++ src/frames.h	7 Oct 2006 03:57:52 -0000
@@ -38,6 +38,12 @@ typedef enum
   META_FRAME_CONTROL_MINIMIZE,
   META_FRAME_CONTROL_MAXIMIZE,
   META_FRAME_CONTROL_UNMAXIMIZE,
+  META_FRAME_CONTROL_SHADE,
+  META_FRAME_CONTROL_UNSHADE,
+  META_FRAME_CONTROL_ABOVE,
+  META_FRAME_CONTROL_UNABOVE,
+  META_FRAME_CONTROL_STICK,
+  META_FRAME_CONTROL_UNSTICK,
   META_FRAME_CONTROL_RESIZE_SE,
   META_FRAME_CONTROL_RESIZE_S,
   META_FRAME_CONTROL_RESIZE_SW,
Index: src/iconcache.c
===================================================================
RCS file: /cvs/gnome/metacity/src/iconcache.c,v
retrieving revision 1.9
diff -u -p -r1.9 iconcache.c
--- src/iconcache.c	1 Oct 2006 22:30:01 -0000	1.9
+++ src/iconcache.c	7 Oct 2006 03:57:52 -0000
@@ -25,6 +25,7 @@
 #include "iconcache.h"
 #include "ui.h"
 #include "errors.h"
+#include "theme.h"
 
 #include <X11/Xatom.h>
 
@@ -499,19 +500,6 @@ get_kwm_win_icon (MetaDisplay *display,
   return;
 }
 
-typedef enum
-{
-  /* These MUST be in ascending order of preference;
-   * i.e. if we get _NET_WM_ICON and already have
-   * WM_HINTS, we prefer _NET_WM_ICON
-   */
-  USING_NO_ICON,
-  USING_FALLBACK_ICON,
-  USING_KWM_WIN_ICON,
-  USING_WM_HINTS,
-  USING_NET_WM_ICON
-} IconOrigin;
-
 void
 meta_icon_cache_init (MetaIconCache *icon_cache)
 {
@@ -830,13 +818,23 @@ meta_read_icons (MetaScreen     *screen,
   if (icon_cache->want_fallback &&
       icon_cache->origin < USING_FALLBACK_ICON)
     {
-      get_fallback_icons (screen,
-                          iconp,
-                          ideal_width,
-                          ideal_height,
-                          mini_iconp,
-                          ideal_mini_width,
-                          ideal_mini_height);
+      MetaTheme *theme = meta_theme_get_current ();
+
+      if (theme->fallback_icon == NULL || theme->fallback_mini_icon == NULL)
+        {
+          get_fallback_icons (screen,
+                              iconp,
+                              ideal_width,
+                              ideal_height,
+                              mini_iconp,
+                              ideal_mini_width,
+                              ideal_mini_height);
+        }
+
+      if (theme->fallback_icon != NULL)
+        *iconp = theme->fallback_icon;
+      if (theme->fallback_mini_icon != NULL)
+        *mini_iconp = theme->fallback_mini_icon;
 
       replace_cache (icon_cache, USING_FALLBACK_ICON,
                      *iconp, *mini_iconp);
Index: src/iconcache.h
===================================================================
RCS file: /cvs/gnome/metacity/src/iconcache.h,v
retrieving revision 1.2
diff -u -p -r1.2 iconcache.h
--- src/iconcache.h	1 Oct 2006 22:30:01 -0000	1.2
+++ src/iconcache.h	7 Oct 2006 03:57:52 -0000
@@ -28,6 +28,19 @@
 
 typedef struct _MetaIconCache MetaIconCache;
 
+typedef enum
+{
+  /* These MUST be in ascending order of preference;
+   * i.e. if we get _NET_WM_ICON and already have
+   * WM_HINTS, we prefer _NET_WM_ICON
+   */
+  USING_NO_ICON,
+  USING_FALLBACK_ICON,
+  USING_KWM_WIN_ICON,
+  USING_WM_HINTS,
+  USING_NET_WM_ICON
+} IconOrigin;
+
 struct _MetaIconCache
 {
   int origin;
Index: src/main.c
===================================================================
RCS file: /cvs/gnome/metacity/src/main.c,v
retrieving revision 1.63
diff -u -p -r1.63 main.c
--- src/main.c	1 Oct 2006 22:30:01 -0000	1.63
+++ src/main.c	7 Oct 2006 03:57:52 -0000
@@ -365,7 +365,7 @@ main (int argc, char **argv)
     }
   
   if (!meta_ui_have_a_theme ())
-    meta_fatal (_("Could not find a theme! Be sure %s exists and contains the usual themes."),
+    meta_fatal (_("Could not find a theme! Be sure %s exists and contains the usual themes.\n"),
                 METACITY_DATADIR"/themes");
   
   /* Connect to SM as late as possible - but before managing display,
Index: src/prefs.c
===================================================================
RCS file: /cvs/gnome/metacity/src/prefs.c,v
retrieving revision 1.69
diff -u -p -r1.69 prefs.c
--- src/prefs.c	1 Oct 2006 22:30:02 -0000	1.69
+++ src/prefs.c	7 Oct 2006 03:57:54 -0000
@@ -1381,10 +1381,42 @@ button_function_from_string (const char 
     return META_BUTTON_FUNCTION_MAXIMIZE;
   else if (strcmp (str, "close") == 0)
     return META_BUTTON_FUNCTION_CLOSE;
-  else
+  else if (strcmp (str, "shade") == 0)
+    return META_BUTTON_FUNCTION_SHADE;
+  else if (strcmp (str, "above") == 0)
+    return META_BUTTON_FUNCTION_ABOVE;
+  else if (strcmp (str, "stick") == 0)
+    return META_BUTTON_FUNCTION_STICK;
+  else 
+    /* don't know; give up */
     return META_BUTTON_FUNCTION_LAST;
 }
 
+static MetaButtonFunction
+button_opposite_function (MetaButtonFunction ofwhat)
+{
+  switch (ofwhat)
+    {
+    case META_BUTTON_FUNCTION_SHADE:
+      return META_BUTTON_FUNCTION_UNSHADE;
+    case META_BUTTON_FUNCTION_UNSHADE:
+      return META_BUTTON_FUNCTION_SHADE;
+
+    case META_BUTTON_FUNCTION_ABOVE:
+      return META_BUTTON_FUNCTION_UNABOVE;
+    case META_BUTTON_FUNCTION_UNABOVE:
+      return META_BUTTON_FUNCTION_ABOVE;
+
+    case META_BUTTON_FUNCTION_STICK:
+      return META_BUTTON_FUNCTION_UNSTICK;
+    case META_BUTTON_FUNCTION_UNSTICK:
+      return META_BUTTON_FUNCTION_STICK;
+
+    default:
+      return META_BUTTON_FUNCTION_LAST;
+    }
+}
+
 static gboolean
 update_button_layout (const char *value)
 {
@@ -1435,6 +1467,11 @@ update_button_layout (const char *value)
               new_layout.left_buttons[i] = f;
               used[f] = TRUE;
               ++i;
+
+              f = button_opposite_function (f);
+
+              if (f != META_BUTTON_FUNCTION_LAST)
+                new_layout.left_buttons[i++] = f;
             }
           else
             {
@@ -1473,7 +1510,12 @@ update_button_layout (const char *value)
               new_layout.right_buttons[i] = f;
               used[f] = TRUE;
               ++i;
-            }
+
+              f = button_opposite_function (f);
+
+              if (f != META_BUTTON_FUNCTION_LAST)
+                new_layout.right_buttons[i++] = f;
+             }
           else
             {
               meta_topic (META_DEBUG_PREFS, "Ignoring unknown or already-used button name \"%s\"\n",
Index: src/theme-parser.c
===================================================================
RCS file: /cvs/gnome/metacity/src/theme-parser.c,v
retrieving revision 1.23
diff -u -p -r1.23 theme-parser.c
--- src/theme-parser.c	1 Oct 2006 22:30:06 -0000	1.23
+++ src/theme-parser.c	7 Oct 2006 03:57:58 -0000
@@ -73,7 +73,9 @@ typedef enum
   /* assigning style sets to windows */
   STATE_WINDOW,
   /* and menu icons */
-  STATE_MENU_ICON
+  STATE_MENU_ICON,
+  /* fallback icons */
+  STATE_FALLBACK
 } ParseState;
 
 typedef struct
@@ -84,6 +86,7 @@ typedef struct
   char *theme_file;             /* theme filename */
   char *theme_dir;              /* dir the theme is inside */
   MetaTheme *theme;             /* theme being parsed */
+  guint format_version;         /* version of format of theme file */  
   char *name;                   /* name of named thing being parsed */
   MetaFrameLayout *layout;      /* layout being parsed if any */
   MetaDrawOpList *op_list;      /* op list being parsed if any */
@@ -93,8 +96,6 @@ typedef struct
   MetaFramePiece piece;         /* position of piece being parsed */
   MetaButtonType button_type;   /* type of button/menuitem being parsed */
   MetaButtonState button_state; /* state of button being parsed */
-  MetaMenuIconType menu_icon_type; /* type of menu icon being parsed */
-  GtkStateType menu_icon_state; /* state of menu icon being parsed */
 } ParseInfo;
 
 static void set_error (GError             **err,
@@ -451,33 +452,48 @@ static gboolean
 parse_positive_integer (const char          *str,
                         int                 *val,
                         GMarkupParseContext *context,
+                        MetaTheme           *theme,
                         GError             **error)
 {
   char *end;
   long l;
+  int j;
 
   *val = 0;
   
   end = NULL;
   
-  l = strtol (str, &end, 10);
+  /* Is str a constant? */
 
-  if (end == NULL || end == str)
+  if (META_THEME_ALLOWS (theme, META_THEME_UBIQUITOUS_CONSTANTS) &&
+      meta_theme_lookup_int_constant (theme, str, &j))
     {
-      set_error (error, context, G_MARKUP_ERROR,
-                 G_MARKUP_ERROR_PARSE,
-                 _("Could not parse \"%s\" as an integer"),
-                 str);
-      return FALSE;
+      /* Yes. */
+      l = j;
     }
-
-  if (*end != '\0')
+  else
     {
-      set_error (error, context, G_MARKUP_ERROR,
-                 G_MARKUP_ERROR_PARSE,
-                 _("Did not understand trailing characters \"%s\" in string \"%s\""),
-                 end, str);
-      return FALSE;
+      /* No. Let's try parsing it instead. */
+
+      l = strtol (str, &end, 10);
+
+      if (end == NULL || end == str)
+      {
+        set_error (error, context, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("Could not parse \"%s\" as an integer"),
+                   str);
+        return FALSE;
+      }
+
+    if (*end != '\0')
+      {
+        set_error (error, context, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("Did not understand trailing characters \"%s\" in string \"%s\""),
+                   end, str);
+        return FALSE;
+      }
     }
 
   if (l < 0)
@@ -560,6 +576,41 @@ parse_boolean (const char          *str,
 }
 
 static gboolean
+parse_rounding (const char          *str,
+                guint               *val,
+                GMarkupParseContext *context,
+                MetaTheme           *theme,
+                GError             **error)
+{
+  if (strcmp ("true", str) == 0)
+    *val = 5; /* historical "true" value */
+  else if (strcmp ("false", str) == 0)
+    *val = 0;
+  else
+    {
+      int tmp;
+      gboolean result;
+       if (!META_THEME_ALLOWS (theme, META_THEME_VARIED_ROUND_CORNERS))
+         {
+           /* Not known in this version, so bail. */
+           set_error (error, context, G_MARKUP_ERROR,
+                      G_MARKUP_ERROR_PARSE,
+                      _("Boolean values must be \"true\" or \"false\" not \"%s\""),
+                      str);
+           return FALSE;
+         }
+   
+      result = parse_positive_integer (str, &tmp, context, theme, error);
+
+      *val = tmp;
+
+      return result;    
+    }
+  
+  return TRUE;
+}
+
+static gboolean
 parse_angle (const char          *str,
              double              *val,
              GMarkupParseContext *context,
@@ -624,17 +675,14 @@ parse_alpha (const char             *str
       
       if (!parse_double (split[i], &v, context, error))
         {
-          set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
-                     _("Could not parse \"%s\" as a floating point number"),
-                     split[i]);
-          
+          /* clear up, but don't set error: it was set by parse_double */
           g_strfreev (split);
           meta_alpha_gradient_spec_free (spec);
           
           return FALSE;
         }
 
-      if (v < (0.0 - 1e6) || v > (1.0 + 1e6))
+      if (v < (0.0 - 1e-6) || v > (1.0 + 1e-6))
         {
           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
                      _("Alpha must be between 0.0 (invisible) and 1.0 (fully opaque), was %g\n"),
@@ -658,6 +706,25 @@ parse_alpha (const char             *str
   return TRUE;
 }
 
+static MetaColorSpec*
+parse_color (MetaTheme *theme,
+             const char        *str,
+             GError           **err)
+{
+  char* referent;
+
+  if (META_THEME_ALLOWS (theme, META_THEME_COLOR_CONSTANTS) &&
+      meta_theme_lookup_color_constant (theme, str, &referent))
+    {
+      if (referent)
+        return meta_color_spec_new_from_string (referent, err);
+      
+      /* no need to free referent: it's a pointer into the actual hash table */
+    }
+  
+  return meta_color_spec_new_from_string (str, err);
+}
+
 static gboolean
 parse_title_scale (const char          *str,
                    double              *val,
@@ -716,8 +783,8 @@ parse_toplevel_element (GMarkupParseCont
     {
       const char *name;
       const char *value;
-      int ival;
-      double dval;
+      int ival = 0;
+      double dval = 0.0;
       
       if (!locate_attributes (context, element_name, attribute_names, attribute_values,
                               error,
@@ -741,11 +808,9 @@ parse_toplevel_element (GMarkupParseCont
           return;
         }
 
-      if (strchr (value, '.'))
+      if (strchr (value, '.') && parse_double (value, &dval, context, error))
         {
-          dval = 0.0;
-          if (!parse_double (value, &dval, context, error))
-            return;
+          g_clear_error (error);
 
           if (!meta_theme_define_float_constant (info->theme,
                                                  name,
@@ -756,11 +821,9 @@ parse_toplevel_element (GMarkupParseCont
               return;
             }
         }
-      else
+      else if (parse_positive_integer (value, &ival, context, info->theme, error))
         {
-          ival = 0;
-          if (!parse_positive_integer (value, &ival, context, error))
-            return;
+          g_clear_error (error);
 
           if (!meta_theme_define_int_constant (info->theme,
                                                name,
@@ -771,6 +834,19 @@ parse_toplevel_element (GMarkupParseCont
               return;
             }
         }
+      else
+        {
+          g_clear_error (error);
+
+          if (!meta_theme_define_color_constant (info->theme,
+                                                 name,
+                                                 value,
+                                                 error))
+            {
+              add_context_to_error (error, context);
+              return;
+            }
+        }
 
       push_state (info, STATE_CONSTANT);
     }
@@ -784,11 +860,13 @@ parse_toplevel_element (GMarkupParseCont
       const char *rounded_top_right = NULL;
       const char *rounded_bottom_left = NULL;
       const char *rounded_bottom_right = NULL;
+      const char *hide_buttons = NULL;
       gboolean has_title_val;
-      gboolean rounded_top_left_val;
-      gboolean rounded_top_right_val;
-      gboolean rounded_bottom_left_val;
-      gboolean rounded_bottom_right_val;
+      guint rounded_top_left_val;
+      guint rounded_top_right_val;
+      guint rounded_bottom_left_val;
+      guint rounded_bottom_right_val;
+      gboolean hide_buttons_val;
       double title_scale_val;
       MetaFrameLayout *parent_layout;
 
@@ -800,6 +878,7 @@ parse_toplevel_element (GMarkupParseCont
                               "rounded_top_right", &rounded_top_right,
                               "rounded_bottom_left", &rounded_bottom_left,
                               "rounded_bottom_right", &rounded_bottom_right,
+                              "hide_buttons", &hide_buttons,
                               NULL))
         return;
 
@@ -815,18 +894,22 @@ parse_toplevel_element (GMarkupParseCont
       if (has_title && !parse_boolean (has_title, &has_title_val, context, error))
         return;
 
-      rounded_top_left_val = FALSE;
-      rounded_top_right_val = FALSE;
-      rounded_bottom_left_val = FALSE;
-      rounded_bottom_right_val = FALSE;
+      hide_buttons_val = FALSE;
+      if (hide_buttons && !parse_boolean (hide_buttons, &hide_buttons_val, context, error))
+        return;
+
+      rounded_top_left_val = 0;
+      rounded_top_right_val = 0;
+      rounded_bottom_left_val = 0;
+      rounded_bottom_right_val = 0;
 
-      if (rounded_top_left && !parse_boolean (rounded_top_left, &rounded_top_left_val, context, error))
+      if (rounded_top_left && !parse_rounding (rounded_top_left, &rounded_top_left_val, context, info->theme, error))
         return;
-      if (rounded_top_right && !parse_boolean (rounded_top_right, &rounded_top_right_val, context, error))
+      if (rounded_top_right && !parse_rounding (rounded_top_right, &rounded_top_right_val, context, info->theme, error))
         return;
-      if (rounded_bottom_left && !parse_boolean (rounded_bottom_left, &rounded_bottom_left_val, context, error))
+      if (rounded_bottom_left && !parse_rounding (rounded_bottom_left, &rounded_bottom_left_val, context, info->theme, error))
         return;      
-      if (rounded_bottom_right && !parse_boolean (rounded_bottom_right, &rounded_bottom_right_val, context, error))
+      if (rounded_bottom_right && !parse_rounding (rounded_bottom_right, &rounded_bottom_right_val, context, info->theme, error))
         return;
       
       title_scale_val = 1.0;
@@ -864,20 +947,23 @@ parse_toplevel_element (GMarkupParseCont
       if (has_title) /* only if explicit, otherwise inherit */
         info->layout->has_title = has_title_val;
 
+      if (META_THEME_ALLOWS (info->theme, META_THEME_HIDDEN_BUTTONS) && hide_buttons_val)
+          info->layout->hide_buttons = hide_buttons_val;
+
       if (title_scale)
 	info->layout->title_scale = title_scale_val;
 
       if (rounded_top_left)
-        info->layout->top_left_corner_rounded = rounded_top_left_val;
+        info->layout->top_left_corner_rounded_radius = rounded_top_left_val;
 
       if (rounded_top_right)
-        info->layout->top_right_corner_rounded = rounded_top_right_val;
+        info->layout->top_right_corner_rounded_radius = rounded_top_right_val;
 
       if (rounded_bottom_left)
-        info->layout->bottom_left_corner_rounded = rounded_bottom_left_val;
+        info->layout->bottom_left_corner_rounded_radius = rounded_bottom_left_val;
 
       if (rounded_bottom_right)
-        info->layout->bottom_right_corner_rounded = rounded_bottom_right_val;
+        info->layout->bottom_right_corner_rounded_radius = rounded_bottom_right_val;
       
       meta_theme_insert_layout (info->theme, name, info->layout);
 
@@ -921,6 +1007,8 @@ parse_toplevel_element (GMarkupParseCont
       const char *name = NULL;
       const char *parent = NULL;
       const char *geometry = NULL;
+      const char *background = NULL;
+      const char *alpha = NULL;
       MetaFrameStyle *parent_style;
       MetaFrameLayout *layout;
 
@@ -928,6 +1016,8 @@ parse_toplevel_element (GMarkupParseCont
                               error,
                               "name", &name, "parent", &parent,
                               "geometry", &geometry,
+                              "background", &background,
+                              "alpha", &alpha,
                               NULL))
         return;
 
@@ -992,6 +1082,40 @@ parse_toplevel_element (GMarkupParseCont
       meta_frame_layout_ref (layout);
       info->style->layout = layout;
 
+      if (background != NULL && META_THEME_ALLOWS (info->theme, META_THEME_FRAME_BACKGROUNDS))
+        {
+          info->style->window_background_color = meta_color_spec_new_from_string (background, error);
+          if (!info->style->window_background_color)
+            return;
+
+          if (alpha != NULL)
+            {
+            
+               gboolean success;
+               MetaAlphaGradientSpec *alpha_vector;
+               
+               g_clear_error (error);
+               /* fortunately, we already have a routine to parse alpha values,
+                * though it produces a vector of them, which is a superset of
+                * what we want.
+                */
+               success = parse_alpha (alpha, &alpha_vector, context, error); 
+               if (!success)
+                 return;
+
+               /* alpha_vector->alphas must contain at least one element */
+               info->style->window_background_alpha = alpha_vector->alphas[0];
+
+               meta_alpha_gradient_spec_free (alpha_vector);
+            }
+        }
+      else if (alpha != NULL)
+        {
+          set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                     _("You must specify a background for an alpha value to be meaningful"));
+          return;
+        }
+
       meta_theme_insert_style (info->theme, name, info->style);
 
       push_state (info, STATE_FRAME_STYLE);
@@ -1110,84 +1234,52 @@ parse_toplevel_element (GMarkupParseCont
     }
   else if (ELEMENT_IS ("menu_icon"))
     {
-      const char *function = NULL;
-      const char *state = NULL;
-      const char *draw_ops = NULL;
+      /* Not supported any more, but we have to parse it if they include it,
+       * for backwards compatibility.
+       */
+      g_assert (info->op_list == NULL);
+      
+      push_state (info, STATE_MENU_ICON);
+    }
+  else if (ELEMENT_IS ("fallback"))
+    {
+      const char *icon = NULL;
+      const char *mini_icon = NULL;
       
       if (!locate_attributes (context, element_name, attribute_names, attribute_values,
                               error,
-                              "function", &function,
-                              "state", &state,
-                              "draw_ops", &draw_ops,
+                              "icon", &icon,
+                              "mini_icon", &mini_icon,
                               NULL))
         return;
 
-      if (function == NULL)
-        {
-          set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
-                     _("No \"%s\" attribute on <%s> element"),
-                     "function", element_name);
-          return;
-        }
-
-      if (state == NULL)
-        {
-          set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
-                     _("No \"%s\" attribute on <%s> element"),
-                     "state", element_name);
-          return;
-        }
-      
-      info->menu_icon_type = meta_menu_icon_type_from_string (function);
-      if (info->menu_icon_type == META_BUTTON_TYPE_LAST)
+      if (icon)
         {
-          set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
-                     _("Unknown function \"%s\" for menu icon"),
-                     function);
-          return;
-        }
+          if (info->theme->fallback_icon != NULL)
+            {
+              set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                         _("Theme already has a fallback icon"));
+              return;
+            }
 
-      info->menu_icon_state = meta_gtk_state_from_string (state);
-      if (((int) info->menu_icon_state) == -1)
-        {
-          set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
-                     _("Unknown state \"%s\" for menu icon"),
-                     state);
-          return;
-        }
-      
-      if (info->theme->menu_icons[info->menu_icon_type][info->menu_icon_state] != NULL)
-        {
-          set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
-                     _("Theme already has a menu icon for function %s state %s"),
-                     function, state);
-          return;
+          info->theme->fallback_icon = meta_theme_load_image(info->theme, icon, 64, error);
         }
 
-      g_assert (info->op_list == NULL);
-      
-      if (draw_ops)
+      if (mini_icon)
         {
-          MetaDrawOpList *op_list;
-
-          op_list = meta_theme_lookup_draw_op_list (info->theme,
-                                                    draw_ops);
-
-          if (op_list == NULL)
+          if (info->theme->fallback_mini_icon != NULL)
             {
               set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
-                         _("No <draw_ops> with the name \"%s\" has been defined"),
-                         draw_ops);
+                         _("Theme already has a fallback mini_icon"));
               return;
             }
 
-          meta_draw_op_list_ref (op_list);
-          info->op_list = op_list;
+          info->theme->fallback_mini_icon = meta_theme_load_image(info->theme, mini_icon, 16, error);
         }
 
-      push_state (info, STATE_MENU_ICON);
+      push_state (info, STATE_FALLBACK);
     }
-  else
+   else
     {
       set_error (error, context,
                  G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
@@ -1293,7 +1385,7 @@ parse_distance (GMarkupParseContext  *co
     }
 
   val = 0;
-  if (!parse_positive_integer (value, &val, context, error))
+  if (!parse_positive_integer (value, &val, context, info->theme, error))
     return;
 
   g_assert (val >= 0); /* yeah, "non-negative" not "positive" get over it */
@@ -1471,19 +1563,19 @@ parse_border (GMarkupParseContext  *cont
     }
 
   top_val = 0;
-  if (!parse_positive_integer (top, &top_val, context, error))
+  if (!parse_positive_integer (top, &top_val, context, info->theme, error))
     return;
 
   bottom_val = 0;
-  if (!parse_positive_integer (bottom, &bottom_val, context, error))
+  if (!parse_positive_integer (bottom, &bottom_val, context, info->theme, error))
     return;
 
   left_val = 0;
-  if (!parse_positive_integer (left, &left_val, context, error))
+  if (!parse_positive_integer (left, &left_val, context, info->theme, error))
     return;
 
   right_val = 0;
-  if (!parse_positive_integer (right, &right_val, context, error))
+  if (!parse_positive_integer (right, &right_val, context, info->theme, error))
     return;
   
   g_assert (info->layout);
@@ -1697,23 +1789,23 @@ parse_draw_op_element (GMarkupParseConte
       
       dash_on_val = 0;
       if (dash_on_length &&
-          !parse_positive_integer (dash_on_length, &dash_on_val, context, error))
+          !parse_positive_integer (dash_on_length, &dash_on_val, context, info->theme, error))
         return;
 
       dash_off_val = 0;
       if (dash_off_length &&
-          !parse_positive_integer (dash_off_length, &dash_off_val, context, error))
+          !parse_positive_integer (dash_off_length, &dash_off_val, context, info->theme, error))
         return;
 
       width_val = 0;
       if (width &&
-          !parse_positive_integer (width, &width_val, context, error))
+          !parse_positive_integer (width, &width_val, context, info->theme, error))
         return;
 
       /* Check last so we don't have to free it when other
        * stuff fails
        */
-      color_spec = meta_color_spec_new_from_string (color, error);
+      color_spec = parse_color (info->theme, color, error);
       if (color_spec == NULL)
         {
           add_context_to_error (error, context);
@@ -1812,7 +1904,7 @@ parse_draw_op_element (GMarkupParseConte
       /* Check last so we don't have to free it when other
        * stuff fails
        */
-      color_spec = meta_color_spec_new_from_string (color, error);
+      color_spec = parse_color (info->theme, color, error);
       if (color_spec == NULL)
         {
           add_context_to_error (error, context);
@@ -1845,6 +1937,8 @@ parse_draw_op_element (GMarkupParseConte
       const char *filled;
       const char *start_angle;
       const char *extent_angle;
+      const char *from;
+      const char *to;
       gboolean filled_val;
       double start_angle_val;
       double extent_angle_val;
@@ -1858,6 +1952,8 @@ parse_draw_op_element (GMarkupParseConte
                               "filled", &filled,
                               "start_angle", &start_angle,
                               "extent_angle", &extent_angle,
+                              "from", &from,
+                              "to", &to,
                               NULL))
         return;
 
@@ -1896,20 +1992,40 @@ parse_draw_op_element (GMarkupParseConte
           return;
         }
 
-      if (start_angle == NULL)
+      if (META_THEME_ALLOWS (info->theme, META_THEME_DEGREES_IN_ARCS) )
         {
-          set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
-                     _("No \"start_angle\" attribute on element <%s>"), element_name);
-          return;
-        }
+          if (start_angle == NULL && from == NULL)
+            {
+              set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                         _("No \"start_angle\" or \"from\" attribute on element <%s>"), element_name);
+              return;
+            }
 
-      if (extent_angle == NULL)
+          if (extent_angle == NULL && to == NULL)
+            {
+              set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                         _("No \"extent_angle\" or \"to\" attribute on element <%s>"), element_name);
+              return;
+            }
+        }
+      else
         {
-          set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
-                     _("No \"extent_angle\" attribute on element <%s>"), element_name);
-          return;
+          if (start_angle == NULL)
+            {
+              set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                         _("No \"start_angle\" attribute on element <%s>"), element_name);
+              return;
+            }
+
+          if (extent_angle == NULL)
+            {
+              set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                         _("No \"extent_angle\" attribute on element <%s>"), element_name);
+              return;
+            }
         }
-      
+
+     
       if (!check_expression (x, FALSE, info->theme, context, error))
         return;
 
@@ -1922,12 +2038,32 @@ parse_draw_op_element (GMarkupParseConte
       if (!check_expression (height, FALSE, info->theme, context, error))
         return;
 
-      if (!parse_angle (start_angle, &start_angle_val, context, error))
-        return;
-
-      if (!parse_angle (extent_angle, &extent_angle_val, context, error))
-        return;
+      if (start_angle == NULL)
+        {
+          if (!parse_angle (from, &start_angle_val, context, error))
+            return;
+          
+          start_angle_val = (180-start_angle_val)/360.0;
+        }
+      else
+        {
+          if (!parse_angle (start_angle, &start_angle_val, context, error))
+            return;
+        }
       
+      if (extent_angle == NULL)
+        {
+          if (!parse_angle (to, &extent_angle_val, context, error))
+            return;
+          
+          extent_angle_val = ((180-extent_angle_val)/360.0) - start_angle_val;
+        }
+      else
+        {
+           if (!parse_angle (extent_angle, &extent_angle_val, context, error))
+             return;
+        }
+     
       filled_val = FALSE;
       if (filled && !parse_boolean (filled, &filled_val, context, error))
         return;
@@ -1935,7 +2071,7 @@ parse_draw_op_element (GMarkupParseConte
       /* Check last so we don't have to free it when other
        * stuff fails
        */
-      color_spec = meta_color_spec_new_from_string (color, error);
+      color_spec = parse_color (info->theme, color, error);
       if (color_spec == NULL)
         {
           add_context_to_error (error, context);
@@ -2109,7 +2245,7 @@ parse_draw_op_element (GMarkupParseConte
       /* Check last so we don't have to free it when other
        * stuff fails
        */
-      color_spec = meta_color_spec_new_from_string (color, error);
+      color_spec = parse_color (info->theme, color, error);
       if (color_spec == NULL)
         {
           if (alpha_spec)
@@ -2254,7 +2390,7 @@ parse_draw_op_element (GMarkupParseConte
                               "x", &x, "y", &y,
                               "width", &width, "height", &height,
                               "alpha", &alpha, "filename", &filename,
-			      "colorize", &colorize,
+                              "colorize", &colorize,
                               "fill_type", &fill_type,
                               NULL))
         return;
@@ -2321,9 +2457,12 @@ parse_draw_op_element (GMarkupParseConte
         }
       
       /* Check last so we don't have to free it when other
-       * stuff fails
+       * stuff fails.
+       *
+       * If it's a theme image, ask for it at 64px, which is
+       * the largest possible. We scale it anyway.
        */
-      pixbuf = meta_theme_load_image (info->theme, filename, error);
+      pixbuf = meta_theme_load_image (info->theme, filename, 64, error);
 
       if (pixbuf == NULL)
         {
@@ -2333,7 +2472,7 @@ parse_draw_op_element (GMarkupParseConte
 
       if (colorize)
         {
-          colorize_spec = meta_color_spec_new_from_string (colorize, error);
+          colorize_spec = parse_color (info->theme, colorize, error);
           
           if (colorize_spec == NULL)
             {
@@ -2892,7 +3031,7 @@ parse_draw_op_element (GMarkupParseConte
       /* Check last so we don't have to free it when other
        * stuff fails
        */
-      color_spec = meta_color_spec_new_from_string (color, error);
+      color_spec = parse_color (info->theme, color, error);
       if (color_spec == NULL)
         {
           add_context_to_error (error, context);
@@ -3154,7 +3293,7 @@ parse_gradient_element (GMarkupParseCont
           return;
         }
 
-      color_spec = meta_color_spec_new_from_string (value, error);
+      color_spec = parse_color (info->theme, value, error);
       if (color_spec == NULL)
         {
           add_context_to_error (error, context);
@@ -3281,7 +3420,7 @@ parse_style_element (GMarkupParseContext
           return;
         }
       
-      info->button_type = meta_button_type_from_string (function);
+      info->button_type = meta_button_type_from_string (function, info->theme);
       if (info->button_type == META_BUTTON_TYPE_LAST)
         {
           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
@@ -3290,6 +3429,18 @@ parse_style_element (GMarkupParseContext
           return;
         }
 
+      if (meta_theme_earliest_version_with_button (info->button_type) >
+          info->theme->format_version)
+        {
+          set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                     _("Button function \"%s\" does not exist in this version (%d, need %d)"),
+                     function,
+                     info->theme->format_version,
+                     meta_theme_earliest_version_with_button (info->button_type)
+                     );
+          return;
+        }
+
       info->button_state = meta_button_state_from_string (state);
       if (info->button_state == META_BUTTON_STATE_LAST)
         {
@@ -3421,8 +3572,9 @@ parse_style_set_element (GMarkupParseCon
           return;
         }
 
-      if (frame_state == META_FRAME_STATE_NORMAL)
+      switch (frame_state)
         {
+        case META_FRAME_STATE_NORMAL:
           if (resize == NULL)
             {
               set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
@@ -3440,13 +3592,51 @@ parse_style_set_element (GMarkupParseCon
                          focus);
               return;
             }
-        }
-      else
-        {
+          
+          break;
+
+        case META_FRAME_STATE_SHADED:
+          if (META_THEME_ALLOWS (info->theme, META_THEME_UNRESIZABLE_SHADED_STYLES))
+            {
+              if (resize == NULL)
+                /* In state="normal" we would complain here. But instead we accept
+                 * not having a resize attribute and default to resize="both", since
+                 * that most closely mimics what we did in v1, and thus people can
+                 * upgrade a theme to v2 without as much hassle.
+                 */
+                frame_resize = META_FRAME_RESIZE_BOTH;
+              else
+                {
+                  frame_resize = meta_frame_resize_from_string (resize);
+                  if (frame_resize == META_FRAME_RESIZE_LAST)
+                    {
+                      set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                                 _("\"%s\" is not a valid value for resize attribute"),
+                                 focus);
+                      return;
+                    }
+                }
+            }
+          else /* v1 theme */
+            {
+              if (resize != NULL)
+                {
+                  set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                       _("Should not have \"resize\" attribute on <%s> element for maximized/shaded states"),
+                      element_name);
+                  return;
+                }
+
+              /* resize="both" is equivalent to the old behaviour */
+              frame_resize = META_FRAME_RESIZE_BOTH;
+            }
+          break;
+          
+        default:
           if (resize != NULL)
             {
               set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
-                         _("Should not have \"resize\" attribute on <%s> element for maximized/shaded states"),
+                         _("Should not have \"resize\" attribute on <%s> element for maximized states"),
                          element_name);
               return;
             }
@@ -3479,15 +3669,15 @@ parse_style_set_element (GMarkupParseCon
           info->style_set->maximized_styles[frame_focus] = frame_style;
           break;
         case META_FRAME_STATE_SHADED:
-          if (info->style_set->shaded_styles[frame_focus])
+          if (info->style_set->shaded_styles[frame_resize][frame_focus])
             {
               set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
-                         _("Style has already been specified for state %s focus %s"),
-                         state, focus);
+                         _("Style has already been specified for state %s resize %s focus %s"),
+                         state, resize, focus);
               return;
             }
           meta_frame_style_ref (frame_style);
-          info->style_set->shaded_styles[frame_focus] = frame_style;
+          info->style_set->shaded_styles[frame_resize][frame_focus] = frame_style;
           break;
         case META_FRAME_STATE_MAXIMIZED_AND_SHADED:
           if (info->style_set->maximized_and_shaded_styles[frame_focus])
@@ -3650,6 +3840,7 @@ start_element_handler (GMarkupParseConte
           info->theme->name = g_strdup (info->theme_name);
           info->theme->filename = g_strdup (info->theme_file);
           info->theme->dirname = g_strdup (info->theme_dir);
+          info->theme->format_version = info->format_version;
           
           push_state (info, STATE_THEME);
         }
@@ -3762,6 +3953,11 @@ start_element_handler (GMarkupParseConte
                  _("Element <%s> is not allowed inside a <%s> element"),
                  element_name, "window");
       break;
+    case STATE_FALLBACK:
+      set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                 _("Element <%s> is not allowed inside a <%s> element"),
+                 element_name, "fallback");
+      break;
     }
 }
 
@@ -3962,6 +4158,7 @@ end_element_handler (GMarkupParseContext
       g_assert (info->style);
 
       if (!meta_frame_style_validate (info->style,
+                                      info->theme->format_version,
                                       error))
         {
           add_context_to_error (error, context);
@@ -4007,16 +4204,9 @@ end_element_handler (GMarkupParseContext
       break;
     case STATE_MENU_ICON:
       g_assert (info->theme);
-      if (info->op_list == NULL)
-        {
-          set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
-                     _("No draw_ops provided for menu icon"));
-        }
-      else
+      if (info->op_list != NULL)
         {
-          g_assert (info->theme->menu_icons[info->menu_icon_type][info->menu_icon_state] == NULL);
-          info->theme->menu_icons[info->menu_icon_type][info->menu_icon_state] =
-            info->op_list;
+          meta_draw_op_list_unref (info->op_list);
           info->op_list = NULL;
         }
       pop_state (info);
@@ -4047,6 +4237,10 @@ end_element_handler (GMarkupParseContext
       pop_state (info);
       g_assert (peek_state (info) == STATE_THEME);
       break;
+    case STATE_FALLBACK:
+      pop_state (info);
+      g_assert (peek_state (info) == STATE_THEME);
+      break;
     }
 }
 
@@ -4239,19 +4433,26 @@ text_handler (GMarkupParseContext *conte
     case STATE_WINDOW:
       NO_TEXT ("window");
       break;
+    case STATE_FALLBACK:
+      NO_TEXT ("fallback");
+      break;
     }
 }
 
-/* We change the filename when we break the format,
- * so themes can work with various metacity versions
- * (note, this is obsolete now because we are versioning
- * the directory this file is inside, so oh well)
+/* We were intending to put the version number
+ * in the subdirectory name, but we ended up
+ * using the filename instead.  The "-1" survives
+ * as a fossil.
  */
-#define THEME_FILENAME "metacity-theme-1.xml"
-
-/* now this is versioned, /usr/share/themes/NAME/THEME_SUBDIR/THEME_FILENAME */
 #define THEME_SUBDIR "metacity-1"
 
+/* Highest version of the theme format to
+ * look out for.
+ */
+#define THEME_VERSION 2
+
+#define METACITY_THEME_FILENAME_FORMAT "metacity-theme-%d.xml"
+
 MetaTheme*
 meta_theme_load (const char *theme_name,
                  GError    **err)
@@ -4264,6 +4465,7 @@ meta_theme_load (const char *theme_name,
   char *theme_file;
   char *theme_dir;
   MetaTheme *retval;
+  guint version;
 
   text = NULL;
   length = 0;
@@ -4275,11 +4477,14 @@ meta_theme_load (const char *theme_name,
   
   if (meta_is_debugging ())
     {
+      gchar *theme_filename = g_strdup_printf (METACITY_THEME_FILENAME_FORMAT,
+                                               THEME_VERSION);
+
       /* Try in themes in our source tree */
       theme_dir = g_build_filename ("./themes", theme_name, NULL);
       
       theme_file = g_build_filename (theme_dir,
-                                     THEME_FILENAME,
+                                     theme_filename,
                                      NULL);
       
       error = NULL;
@@ -4295,12 +4500,19 @@ meta_theme_load (const char *theme_name,
           g_free (theme_file);
           theme_file = NULL;
         }
+      version = THEME_VERSION;
+
+      g_free (theme_filename);
     }
   
-  /* We try in home dir, then system dir for themes */
-  
-  if (text == NULL)
+  /* We try all supported versions from current to oldest */
+  for (version = THEME_VERSION; (version > 0) && (text == NULL); version--)
     {
+      gchar *theme_filename = g_strdup_printf (METACITY_THEME_FILENAME_FORMAT,
+                                               version);
+      
+      /* We try in home dir, then system dir for themes */
+  
       theme_dir = g_build_filename (g_get_home_dir (),
                                     ".themes",
                                     theme_name,
@@ -4308,7 +4520,7 @@ meta_theme_load (const char *theme_name,
                                     NULL);
       
       theme_file = g_build_filename (theme_dir,
-                                     THEME_FILENAME,
+                                     theme_filename,
                                      NULL);
 
       error = NULL;
@@ -4324,45 +4536,57 @@ meta_theme_load (const char *theme_name,
           g_free (theme_file);
           theme_file = NULL;
         }
+
+      if (text == NULL)
+        {
+          theme_dir = g_build_filename (METACITY_DATADIR,
+                                        "themes",
+                                        theme_name,
+                                        THEME_SUBDIR,
+                                        NULL);
+      
+          theme_file = g_build_filename (theme_dir,
+                                         theme_filename,
+                                         NULL);
+
+          error = NULL;
+          if (!g_file_get_contents (theme_file,
+                                    &text,
+                                    &length,
+                                    &error))
+            {
+              meta_topic (META_DEBUG_THEMES, "Failed to read theme from file %s: %s\n",
+                            theme_file, error->message);
+              g_error_free (error);
+              g_free (theme_dir);
+              g_free (theme_file);
+              theme_file = NULL;
+            }
+        }
+
+      g_free (theme_filename);
     }
 
   if (text == NULL)
     {
-      theme_dir = g_build_filename (METACITY_DATADIR,
-                                    "themes",
-                                    theme_name,
-                                    THEME_SUBDIR,
-                                    NULL);
-      
-      theme_file = g_build_filename (theme_dir,
-                                     THEME_FILENAME,
-                                     NULL);
+      g_set_error (err, META_THEME_ERROR, META_THEME_ERROR_FAILED,
+          _("Failed to find a valid file for theme %s\n"),
+          theme_name);
 
-      error = NULL;
-      if (!g_file_get_contents (theme_file,
-                                &text,
-                                &length,
-                                &error))
-        {
-          meta_warning (_("Failed to read theme from file %s: %s\n"),
-                        theme_file, error->message);
-          g_propagate_error (err, error);
-          g_free (theme_file);
-          g_free (theme_dir);
-          return NULL; /* all fallbacks failed */
-        }
+      return NULL; /* all fallbacks failed */
     }
 
-  g_assert (text);
-
   meta_topic (META_DEBUG_THEMES, "Parsing theme file %s\n", theme_file);
 
+
   parse_info_init (&info);
   info.theme_name = theme_name;
   
   /* pass ownership to info so we free it with the info */
   info.theme_file = theme_file;
   info.theme_dir = theme_dir;
+
+  info.format_version = version + 1;
   
   context = g_markup_parse_context_new (&metacity_theme_parser,
                                         0, &info, NULL);
@@ -4385,7 +4609,9 @@ meta_theme_load (const char *theme_name,
   if (context)
     g_markup_parse_context_free (context);
   g_free (text);
-  
+
+  info.theme->format_version = info.format_version;
+
   if (error)
     {
       g_propagate_error (err, error);
Index: src/theme.c
===================================================================
RCS file: /cvs/gnome/metacity/src/theme.c,v
retrieving revision 1.81
diff -u -p -r1.81 theme.c
--- src/theme.c	1 Oct 2006 22:30:06 -0000	1.81
+++ src/theme.c	7 Oct 2006 03:58:02 -0000
@@ -27,6 +27,8 @@
 #include "util.h"
 #include "gradient.h"
 #include <gtk/gtkwidget.h>
+#include <gtk/gtkimage.h>
+#include <gtk/gtkicontheme.h>
 #include <string.h>
 #include <stdlib.h>
 #include <math.h>
@@ -394,8 +396,51 @@ meta_frame_layout_get_borders (const Met
 static MetaButtonSpace*
 rect_for_function (MetaFrameGeometry *fgeom,
                    MetaFrameFlags     flags,
-                   MetaButtonFunction function)
+                   MetaButtonFunction function,
+                   MetaTheme         *theme)
 {
+
+  /* Firstly, check version-specific things. */
+  
+  if (META_THEME_ALLOWS(theme, META_THEME_SHADE_STICK_ABOVE_BUTTONS))
+    {
+      switch (function)
+        {
+        case META_BUTTON_FUNCTION_SHADE:
+          if ((flags & META_FRAME_ALLOWS_SHADE) && !(flags & META_FRAME_SHADED))
+            return &fgeom->shade_rect;
+          else
+            return NULL;
+        case META_BUTTON_FUNCTION_ABOVE:
+          if (!(flags & META_FRAME_ABOVE))
+            return &fgeom->above_rect;
+          else
+            return NULL;
+        case META_BUTTON_FUNCTION_STICK:
+          if (!(flags & META_FRAME_STUCK))
+            return &fgeom->stick_rect;
+          else
+            return NULL;
+        case META_BUTTON_FUNCTION_UNSHADE:
+          if ((flags & META_FRAME_ALLOWS_SHADE) && (flags & META_FRAME_SHADED))
+            return &fgeom->unshade_rect;
+          else
+            return NULL;
+        case META_BUTTON_FUNCTION_UNABOVE:
+          if (flags & META_FRAME_ABOVE)
+            return &fgeom->unabove_rect;
+          else
+            return NULL;
+        case META_BUTTON_FUNCTION_UNSTICK:
+          if (flags & META_FRAME_STUCK)
+            return &fgeom->unstick_rect;
+        default:
+          /* just go on to the next switch block */;
+        }
+    }
+
+  /* now consider the buttons which exist in all versions */
+
   switch (function)
     {
     case META_BUTTON_FUNCTION_MENU:
@@ -418,6 +463,19 @@ rect_for_function (MetaFrameGeometry *fg
         return &fgeom->close_rect;
       else
         return NULL;
+    case META_BUTTON_FUNCTION_STICK:
+    case META_BUTTON_FUNCTION_SHADE:
+    case META_BUTTON_FUNCTION_ABOVE:
+    case META_BUTTON_FUNCTION_UNSTICK:
+    case META_BUTTON_FUNCTION_UNSHADE:
+    case META_BUTTON_FUNCTION_UNABOVE:
+      /* we are being asked for a >v1 button which hasn't been handled yet,
+       * so obviously we're not in a theme which supports that version.
+       * therefore, we don't show the button. return NULL and all will
+       * be well.
+       */
+      return NULL;
+      
     case META_BUTTON_FUNCTION_LAST:
       return NULL;
     }
@@ -468,7 +526,8 @@ meta_frame_layout_calc_geometry (const M
                                  int                     client_width,
                                  int                     client_height,
                                  const MetaButtonLayout *button_layout,
-                                 MetaFrameGeometry      *fgeom)
+                                 MetaFrameGeometry      *fgeom,
+                                 MetaTheme              *theme)
 {
   int i, n_left, n_right;
   int x;
@@ -544,18 +603,20 @@ meta_frame_layout_calc_geometry (const M
       right_func_rects[i] = NULL;
 
       /* Try to fill in rects */
-      if (button_layout->left_buttons[i] != META_BUTTON_FUNCTION_LAST)
+      if (button_layout->left_buttons[i] != META_BUTTON_FUNCTION_LAST && !layout->hide_buttons)
         {
           left_func_rects[n_left] = rect_for_function (fgeom, flags,
-                                                       button_layout->left_buttons[i]);
+                                                       button_layout->left_buttons[i],
+                                                       theme);
           if (left_func_rects[n_left] != NULL)
             ++n_left;
         }
       
-      if (button_layout->right_buttons[i] != META_BUTTON_FUNCTION_LAST)
+      if (button_layout->right_buttons[i] != META_BUTTON_FUNCTION_LAST && !layout->hide_buttons)
         {
           right_func_rects[n_right] = rect_for_function (fgeom, flags,
-                                                         button_layout->right_buttons[i]);
+                                                         button_layout->right_buttons[i],
+                                                         theme);
           if (right_func_rects[n_right] != NULL)
             ++n_right;
         }
@@ -610,10 +671,28 @@ meta_frame_layout_calc_geometry (const M
         break; /* Everything fits, bail out */
       
       /* Otherwise we need to shave out a button. Shave
-       * min, max, close, then menu (menu is most useful);
+       * above, stick, shade, min, max, close, then menu (menu is most useful);
        * prefer the default button locations.
        */
       if (strip_button (left_func_rects, left_bg_rects,
+                        &n_left, &fgeom->above_rect))
+        continue;
+      else if (strip_button (right_func_rects, right_bg_rects,
+                             &n_right, &fgeom->above_rect))
+        continue;
+      else if (strip_button (left_func_rects, left_bg_rects,
+                        &n_left, &fgeom->stick_rect))
+        continue;
+      else if (strip_button (right_func_rects, right_bg_rects,
+                             &n_right, &fgeom->stick_rect))
+        continue;
+      else if (strip_button (left_func_rects, left_bg_rects,
+                        &n_left, &fgeom->shade_rect))
+        continue;
+      else if (strip_button (right_func_rects, right_bg_rects,
+                             &n_right, &fgeom->shade_rect))
+        continue;
+      else if (strip_button (left_func_rects, left_bg_rects,
                         &n_left, &fgeom->min_rect))
         continue;
       else if (strip_button (right_func_rects, right_bg_rects,
@@ -751,20 +830,20 @@ meta_frame_layout_calc_geometry (const M
   else
     min_size_for_rounding = 5;
   
-  fgeom->top_left_corner_rounded = FALSE;
-  fgeom->top_right_corner_rounded = FALSE;
-  fgeom->bottom_left_corner_rounded = FALSE;
-  fgeom->bottom_right_corner_rounded = FALSE;
+  fgeom->top_left_corner_rounded_radius = 0;
+  fgeom->top_right_corner_rounded_radius = 0;
+  fgeom->bottom_left_corner_rounded_radius = 0;
+  fgeom->bottom_right_corner_rounded_radius = 0;
 
   if (fgeom->top_height + fgeom->left_width >= min_size_for_rounding)
-    fgeom->top_left_corner_rounded = layout->top_left_corner_rounded;
+    fgeom->top_left_corner_rounded_radius = layout->top_left_corner_rounded_radius;
   if (fgeom->top_height + fgeom->right_width >= min_size_for_rounding)
-    fgeom->top_right_corner_rounded = layout->top_right_corner_rounded;
+    fgeom->top_right_corner_rounded_radius = layout->top_right_corner_rounded_radius;
 
   if (fgeom->bottom_height + fgeom->left_width >= min_size_for_rounding)
-    fgeom->bottom_left_corner_rounded = layout->bottom_left_corner_rounded;
+    fgeom->bottom_left_corner_rounded_radius = layout->bottom_left_corner_rounded_radius;
   if (fgeom->bottom_height + fgeom->right_width >= min_size_for_rounding)
-    fgeom->bottom_right_corner_rounded = layout->bottom_right_corner_rounded;
+    fgeom->bottom_right_corner_rounded_radius = layout->bottom_right_corner_rounded_radius;
 }
 
 MetaGradientSpec*
@@ -3743,6 +3822,9 @@ meta_frame_style_new (MetaFrameStyle *pa
 
   style->refcount = 1;
 
+  /* Default alpha is fully opaque */
+  style->window_background_alpha = 255;
+
   style->parent = parent;
   if (parent)
     meta_frame_style_ref (parent);
@@ -3790,6 +3872,9 @@ meta_frame_style_unref (MetaFrameStyle *
       if (style->layout)
         meta_frame_layout_unref (style->layout);
 
+      if (style->window_background_color)
+        meta_color_spec_free (style->window_background_color);
+
       /* we hold a reference to any parent style */
       if (style->parent)
         meta_frame_style_unref (style->parent);
@@ -3841,6 +3926,7 @@ get_button (MetaFrameStyle *style,
 
 gboolean
 meta_frame_style_validate (MetaFrameStyle    *style,
+                           guint              current_theme_version,
                            GError           **error)
 {
   int i, j;
@@ -3855,7 +3941,9 @@ meta_frame_style_validate (MetaFrameStyl
         {
           for (j = 0; j < META_BUTTON_STATE_LAST; j++)
             {
-              if (get_button (style, i, j) == NULL)
+              if (get_button (style, i, j) == NULL &&
+                  meta_theme_earliest_version_with_button (i) <= current_theme_version
+                  )
                 {
                   g_set_error (error, META_THEME_ERROR,
                                META_THEME_ERROR_FAILED,
@@ -3907,6 +3995,30 @@ button_rect (MetaButtonType           ty
       *rect = fgeom->close_rect.visible;
       break;
 
+    case META_BUTTON_TYPE_SHADE:
+      *rect = fgeom->shade_rect.visible;
+      break;
+
+    case META_BUTTON_TYPE_UNSHADE:
+      *rect = fgeom->unshade_rect.visible;
+      break;
+
+    case META_BUTTON_TYPE_ABOVE:
+      *rect = fgeom->above_rect.visible;
+      break;
+
+    case META_BUTTON_TYPE_UNABOVE:
+      *rect = fgeom->unabove_rect.visible;
+      break;
+
+    case META_BUTTON_TYPE_STICK:
+      *rect = fgeom->stick_rect.visible;
+      break;
+
+    case META_BUTTON_TYPE_UNSTICK:
+      *rect = fgeom->unstick_rect.visible;
+      break;
+
     case META_BUTTON_TYPE_MAXIMIZE:
       *rect = fgeom->max_rect.visible;
       break;
@@ -4218,10 +4330,12 @@ meta_frame_style_set_unref (MetaFrameSty
       int i;
 
       for (i = 0; i < META_FRAME_RESIZE_LAST; i++)
-        free_focus_styles (style_set->normal_styles[i]);
+        {
+          free_focus_styles (style_set->normal_styles[i]);
+          free_focus_styles (style_set->shaded_styles[i]);
+        }
 
       free_focus_styles (style_set->maximized_styles);
-      free_focus_styles (style_set->shaded_styles);
       free_focus_styles (style_set->maximized_and_shaded_styles);
 
       if (style_set->parent)
@@ -4243,47 +4357,53 @@ get_style (MetaFrameStyleSet *style_set,
   
   style = NULL;
 
-  if (state == META_FRAME_STATE_NORMAL)
+  switch (state)
     {
-      style = style_set->normal_styles[resize][focus];
+    case META_FRAME_STATE_NORMAL:
+    case META_FRAME_STATE_SHADED:
+      {
+        if (state == META_FRAME_STATE_SHADED)
+          style = style_set->shaded_styles[resize][focus];
+        else
+          style = style_set->normal_styles[resize][focus];
 
-      /* Try parent if we failed here */
-      if (style == NULL && style_set->parent)
-        style = get_style (style_set->parent, state, resize, focus);
-      
-      /* Allow people to omit the vert/horz/none resize modes */
-      if (style == NULL &&
-          resize != META_FRAME_RESIZE_BOTH)
-        style = get_style (style_set, state, META_FRAME_RESIZE_BOTH, focus);
-    }
-  else
-    {
-      MetaFrameStyle **styles;
+        /* Try parent if we failed here */
+        if (style == NULL && style_set->parent)
+          style = get_style (style_set->parent, state, resize, focus);
+      
+        /* Allow people to omit the vert/horz/none resize modes */
+        if (style == NULL &&
+            resize != META_FRAME_RESIZE_BOTH)
+          style = get_style (style_set, state, META_FRAME_RESIZE_BOTH, focus);
+      }
+      break;
+    default:
+      {
+        MetaFrameStyle **styles;
 
-      styles = NULL;
+        styles = NULL;
       
-      switch (state)
-        {
-        case META_FRAME_STATE_SHADED:
-          styles = style_set->shaded_styles;
-          break;
-        case META_FRAME_STATE_MAXIMIZED:
-          styles = style_set->maximized_styles;
-          break;
-        case META_FRAME_STATE_MAXIMIZED_AND_SHADED:
-          styles = style_set->maximized_and_shaded_styles;
-          break;
-        case META_FRAME_STATE_NORMAL:
-        case META_FRAME_STATE_LAST:
-          g_assert_not_reached ();
-          break;
-        }
+        switch (state)
+          {
+          case META_FRAME_STATE_MAXIMIZED:
+            styles = style_set->maximized_styles;
+            break;
+          case META_FRAME_STATE_MAXIMIZED_AND_SHADED:
+            styles = style_set->maximized_and_shaded_styles;
+            break;
+          case META_FRAME_STATE_NORMAL:
+          case META_FRAME_STATE_SHADED:
+          case META_FRAME_STATE_LAST:
+            g_assert_not_reached ();
+            break;
+          }
 
-      style = styles[focus];
+        style = styles[focus];
 
-      /* Try parent if we failed here */
-      if (style == NULL && style_set->parent)
-        style = get_style (style_set->parent, state, resize, focus);      
+        /* Try parent if we failed here */
+        if (style == NULL && style_set->parent)
+          style = get_style (style_set->parent, state, resize, focus);      
+      }
     }
 
   return style;
@@ -4430,17 +4550,6 @@ meta_theme_new (void)
 }
 
 
-static void
-free_menu_ops (MetaDrawOpList *op_lists[META_MENU_ICON_TYPE_LAST][N_GTK_STATES])
-{
-  int i, j;
-
-  for (i = 0; i < META_MENU_ICON_TYPE_LAST; i++)
-    for (j = 0; j < N_GTK_STATES; j++)
-      if (op_lists[i][j])
-        meta_draw_op_list_unref (op_lists[i][j]);
-}
-
 void
 meta_theme_free (MetaTheme *theme)
 {
@@ -4476,34 +4585,15 @@ meta_theme_free (MetaTheme *theme)
     if (theme->style_sets_by_type[i])
       meta_frame_style_set_unref (theme->style_sets_by_type[i]);
 
-  free_menu_ops (theme->menu_icons);
-  
   DEBUG_FILL_STRUCT (theme);
   g_free (theme);
 }
 
-static MetaDrawOpList*
-get_menu_icon (MetaTheme       *theme,
-               MetaMenuIconType type,
-               GtkStateType     state)
-{
-  MetaDrawOpList *op_list;
-
-  op_list = theme->menu_icons[type][state];
-
-  /* We fall back to normal if other states aren't found */
-  if (op_list == NULL &&
-      state != GTK_STATE_NORMAL)
-    return get_menu_icon (theme, type, GTK_STATE_NORMAL);
-  
-  return op_list;
-}
-
 gboolean
 meta_theme_validate (MetaTheme *theme,
                      GError   **error)
 {
-  int i, j;
+  int i;
   
   g_return_val_if_fail (theme != NULL, FALSE);
 
@@ -4558,24 +4648,13 @@ meta_theme_validate (MetaTheme *theme,
         return FALSE;          
       }
 
-  for (i = 0; i < META_MENU_ICON_TYPE_LAST; i++)
-    for (j = 0; j < N_GTK_STATES; j++)
-      if (get_menu_icon (theme, i, j) == NULL)
-        {
-          g_set_error (error, META_THEME_ERROR,
-                       META_THEME_ERROR_FAILED,
-                       _("<menu_icon function=\"%s\" state=\"%s\" draw_ops=\"whatever\"/> must be specified for this theme"),
-                       meta_menu_icon_type_to_string (i),
-                       meta_gtk_state_to_string (j));
-          return FALSE;
-        }
-  
   return TRUE;
 }
 
 GdkPixbuf*
 meta_theme_load_image (MetaTheme  *theme,
                        const char *filename,
+                       guint size_of_theme_icons,
                        GError    **error)
 {
   GdkPixbuf *pixbuf;
@@ -4585,19 +4664,32 @@ meta_theme_load_image (MetaTheme  *theme
 
   if (pixbuf == NULL)
     {
-      char *full_path;
-      
-      full_path = g_build_filename (theme->dirname, filename, NULL);
-      
-      pixbuf = gdk_pixbuf_new_from_file (full_path, error);
-      if (pixbuf == NULL)
+       
+      if (g_str_has_prefix (filename, "theme:") &&
+          META_THEME_ALLOWS (theme, META_THEME_IMAGES_FROM_ICON_THEMES))
+        {
+          pixbuf = gtk_icon_theme_load_icon (
+              gtk_icon_theme_get_default (),
+              filename+6,
+              size_of_theme_icons,
+              0,
+              error);
+          if (pixbuf == NULL) return NULL;
+         }
+      else
         {
-          g_free (full_path);
-          return NULL;
-        }
+          char *full_path;
+          full_path = g_build_filename (theme->dirname, filename, NULL);
       
-      g_free (full_path);
+          pixbuf = gdk_pixbuf_new_from_file (full_path, error);
+          if (pixbuf == NULL)
+            {
+              g_free (full_path);
+              return NULL;
+            }
       
+          g_free (full_path);
+        }      
       g_hash_table_replace (theme->images_by_filename,
                             g_strdup (filename),
                             pixbuf);
@@ -4749,7 +4841,8 @@ meta_theme_draw_frame (MetaTheme        
                                    flags,
                                    client_width, client_height,
                                    button_layout,
-                                   &fgeom);  
+                                   &fgeom,
+                                   theme);  
 
   meta_frame_style_draw (style,
                          widget,
@@ -4765,37 +4858,6 @@ meta_theme_draw_frame (MetaTheme        
 }
 
 void
-meta_theme_draw_menu_icon (MetaTheme          *theme,
-                           GtkWidget          *widget,
-                           GdkDrawable        *drawable,
-                           const GdkRectangle *clip,
-                           MetaRectangle       offset_rect,
-                           MetaMenuIconType    type)
-{  
-  MetaDrawInfo info;
-  MetaDrawOpList *op_list;
-  
-  g_return_if_fail (type < META_BUTTON_TYPE_LAST);  
-
-  op_list = get_menu_icon (theme, type,
-                           GTK_WIDGET_STATE (widget));
-  
-  info.mini_icon = NULL;
-  info.icon = NULL;
-  info.title_layout = NULL;
-  info.title_layout_width = 0;
-  info.title_layout_height = 0;
-  info.fgeom = NULL;
-  
-  meta_draw_op_list_draw (op_list,
-                          widget,
-                          drawable,
-                          clip,
-                          &info,
-                          offset_rect);
-}
-
-void
 meta_theme_get_frame_borders (MetaTheme      *theme,
                               MetaFrameType   type,
                               int             text_height,
@@ -4856,7 +4918,8 @@ meta_theme_calc_geometry (MetaTheme     
                                    flags,
                                    client_width, client_height,
                                    button_layout,
-                                   fgeom);
+                                   fgeom,
+                                   theme);
 }
 
 MetaFrameLayout*
@@ -5054,6 +5117,68 @@ meta_theme_lookup_float_constant (MetaTh
     }
 }
 
+gboolean
+meta_theme_define_color_constant (MetaTheme   *theme,
+                                  const char  *name,
+                                  const char  *value,
+                                  GError     **error)
+{
+  if (theme->color_constants == NULL)
+    theme->color_constants = g_hash_table_new_full (g_str_hash,
+                                                    g_str_equal,
+                                                    g_free,
+                                                    NULL);
+
+  if (!first_uppercase (name))
+    {
+      g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
+                   _("User-defined constants must begin with a capital letter; \"%s\" does not"),
+                   name);
+      return FALSE;
+    }
+  
+  if (g_hash_table_lookup_extended (theme->color_constants, name, NULL, NULL))
+    {
+      g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
+                   _("Constant \"%s\" has already been defined"),
+                   name);
+      
+      return FALSE;
+    }
+
+  g_hash_table_insert (theme->color_constants,
+                       g_strdup (name),
+                       g_strdup (value));
+
+  return TRUE;
+}
+
+gboolean
+meta_theme_lookup_color_constant (MetaTheme   *theme,
+                                  const char  *name,
+                                  char       **value)
+{
+  char *result;
+
+  *value = NULL;
+  
+  if (theme->color_constants == NULL)
+    return FALSE;
+
+  result = g_hash_table_lookup (theme->color_constants, name);
+
+  if (result)
+    {
+      *value = result;
+      return TRUE;
+    }
+  else
+    {
+      return FALSE;
+    }
+}
+
+
 PangoFontDescription*
 meta_gtk_widget_get_font_desc (GtkWidget *widget,
                                double     scale,
@@ -5176,8 +5301,24 @@ meta_button_state_to_string (MetaButtonS
 }
 
 MetaButtonType
-meta_button_type_from_string (const char *str)
+meta_button_type_from_string (const char *str, MetaTheme *theme)
 {
+  if (META_THEME_ALLOWS(theme, META_THEME_SHADE_STICK_ABOVE_BUTTONS))
+    {
+      if (strcmp ("shade", str) == 0)
+        return META_BUTTON_TYPE_SHADE;
+      else if (strcmp ("above", str) == 0)
+        return META_BUTTON_TYPE_ABOVE;
+      else if (strcmp ("stick", str) == 0)
+        return META_BUTTON_TYPE_STICK;
+      else if (strcmp ("unshade", str) == 0)
+        return META_BUTTON_TYPE_UNSHADE;
+      else if (strcmp ("unabove", str) == 0)
+        return META_BUTTON_TYPE_UNABOVE;
+      else if (strcmp ("unstick", str) == 0)
+        return META_BUTTON_TYPE_UNSTICK;
+     }
+
   if (strcmp ("close", str) == 0)
     return META_BUTTON_TYPE_CLOSE;
   else if (strcmp ("maximize", str) == 0)
@@ -5213,7 +5354,19 @@ meta_button_type_to_string (MetaButtonTy
       return "maximize";
     case META_BUTTON_TYPE_MINIMIZE:
       return "minimize";
-    case META_BUTTON_TYPE_MENU:
+    case META_BUTTON_TYPE_SHADE:
+     return "shade";
+    case META_BUTTON_TYPE_ABOVE:
+      return "above";
+    case META_BUTTON_TYPE_STICK:
+      return "stick";
+    case META_BUTTON_TYPE_UNSHADE:
+      return "unshade";
+    case META_BUTTON_TYPE_UNABOVE:
+      return "unabove";
+    case META_BUTTON_TYPE_UNSTICK:
+      return "unstick";
+     case META_BUTTON_TYPE_MENU:
       return "menu";
     case META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND:
       return "left_left_background";
@@ -5234,41 +5387,6 @@ meta_button_type_to_string (MetaButtonTy
   return "<unknown>";
 }
 
-MetaMenuIconType
-meta_menu_icon_type_from_string (const char *str)
-{
-  if (strcmp ("close", str) == 0)
-    return META_MENU_ICON_TYPE_CLOSE;
-  else if (strcmp ("maximize", str) == 0)
-    return META_MENU_ICON_TYPE_MAXIMIZE;
-  else if (strcmp ("minimize", str) == 0)
-    return META_MENU_ICON_TYPE_MINIMIZE;
-  else if (strcmp ("unmaximize", str) == 0)
-    return META_MENU_ICON_TYPE_UNMAXIMIZE;
-  else
-    return META_MENU_ICON_TYPE_LAST;
-}
-
-const char*
-meta_menu_icon_type_to_string (MetaMenuIconType type)
-{
-  switch (type)
-    {
-    case META_MENU_ICON_TYPE_CLOSE:
-      return "close";
-    case META_MENU_ICON_TYPE_MAXIMIZE:
-      return "maximize";
-    case META_MENU_ICON_TYPE_MINIMIZE:
-      return "minimize";
-    case META_MENU_ICON_TYPE_UNMAXIMIZE:
-      return "unmaximize";
-    case META_MENU_ICON_TYPE_LAST:
-      break;
-    }
-
-  return "<unknown>";
-}
-
 MetaFramePiece
 meta_frame_piece_from_string (const char *str)
 {
@@ -6044,3 +6162,34 @@ draw_bg_gradient_composite (const MetaTe
     }
 }
 #endif
+
+guint
+meta_theme_earliest_version_with_button (MetaButtonType type)
+{
+  switch (type)
+    {
+    case META_BUTTON_TYPE_CLOSE:
+    case META_BUTTON_TYPE_MAXIMIZE:
+    case META_BUTTON_TYPE_MINIMIZE:
+    case META_BUTTON_TYPE_MENU:
+    case META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND:
+    case META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND:
+    case META_BUTTON_TYPE_LEFT_RIGHT_BACKGROUND:
+    case META_BUTTON_TYPE_RIGHT_LEFT_BACKGROUND:
+    case META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND:
+    case META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND:
+      return 1;
+      
+    case META_BUTTON_TYPE_SHADE:
+    case META_BUTTON_TYPE_ABOVE:
+    case META_BUTTON_TYPE_STICK:
+    case META_BUTTON_TYPE_UNSHADE:
+    case META_BUTTON_TYPE_UNABOVE:
+    case META_BUTTON_TYPE_UNSTICK:
+      return 2;
+
+    default:
+      meta_warning("Unknown button %d\n", type);
+      return 1; 
+    }
+}
Index: src/theme.h
===================================================================
RCS file: /cvs/gnome/metacity/src/theme.h,v
retrieving revision 1.41
diff -u -p -r1.41 theme.h
--- src/theme.h	1 Oct 2006 22:30:09 -0000	1.41
+++ src/theme.h	7 Oct 2006 03:58:03 -0000
@@ -100,11 +100,14 @@ struct _MetaFrameLayout
   /* Whether title text will be displayed */
   guint has_title : 1;
 
+  /* Whether we should hide the buttons */
+  guint hide_buttons : 1;
+
   /* Round corners */
-  guint top_left_corner_rounded : 1;
-  guint top_right_corner_rounded : 1;
-  guint bottom_left_corner_rounded : 1;
-  guint bottom_right_corner_rounded : 1;
+  guint top_left_corner_rounded_radius;
+  guint top_right_corner_rounded_radius;
+  guint bottom_left_corner_rounded_radius;
+  guint bottom_right_corner_rounded_radius;
 };
 
 struct _MetaButtonSpace
@@ -145,6 +148,12 @@ struct _MetaFrameGeometry
   MetaButtonSpace max_rect;
   MetaButtonSpace min_rect;
   MetaButtonSpace menu_rect;
+  MetaButtonSpace shade_rect;
+  MetaButtonSpace above_rect;
+  MetaButtonSpace stick_rect;
+  MetaButtonSpace unshade_rect;
+  MetaButtonSpace unabove_rect;
+  MetaButtonSpace unstick_rect;
 
 #define MAX_MIDDLE_BACKGROUNDS (MAX_BUTTONS_PER_CORNER - 2)
   GdkRectangle left_left_background;
@@ -156,10 +165,10 @@ struct _MetaFrameGeometry
   /* End of button rects (if changed adjust memset hack) */
   
   /* Round corners */
-  guint top_left_corner_rounded : 1;
-  guint top_right_corner_rounded : 1;
-  guint bottom_left_corner_rounded : 1;
-  guint bottom_right_corner_rounded : 1;
+  guint top_left_corner_rounded_radius;
+  guint top_right_corner_rounded_radius;
+  guint bottom_left_corner_rounded_radius;
+  guint bottom_right_corner_rounded_radius;
 };
 
 typedef enum
@@ -438,6 +447,12 @@ typedef enum
   META_BUTTON_TYPE_MAXIMIZE,
   META_BUTTON_TYPE_MINIMIZE,
   META_BUTTON_TYPE_MENU,
+  META_BUTTON_TYPE_SHADE,
+  META_BUTTON_TYPE_ABOVE,
+  META_BUTTON_TYPE_STICK,
+  META_BUTTON_TYPE_UNSHADE,
+  META_BUTTON_TYPE_UNABOVE,
+  META_BUTTON_TYPE_UNSTICK,
   META_BUTTON_TYPE_LAST
 } MetaButtonType;
 
@@ -503,6 +518,9 @@ struct _MetaFrameStyle
   MetaDrawOpList *buttons[META_BUTTON_TYPE_LAST][META_BUTTON_STATE_LAST];
   MetaDrawOpList *pieces[META_FRAME_PIECE_LAST];
   MetaFrameLayout *layout;
+  MetaColorSpec *window_background_color; /* can be NULL to use the standard
+                                             GTK theme engine */
+  guint8 window_background_alpha; /* 0=transparent; 255=opaque */
 };
 
 /* Kinds of frame...
@@ -552,7 +570,7 @@ struct _MetaFrameStyleSet
   MetaFrameStyleSet *parent;
   MetaFrameStyle *normal_styles[META_FRAME_RESIZE_LAST][META_FRAME_FOCUS_LAST];
   MetaFrameStyle *maximized_styles[META_FRAME_FOCUS_LAST];
-  MetaFrameStyle *shaded_styles[META_FRAME_FOCUS_LAST];
+  MetaFrameStyle *shaded_styles[META_FRAME_RESIZE_LAST][META_FRAME_FOCUS_LAST];
   MetaFrameStyle *maximized_and_shaded_styles[META_FRAME_FOCUS_LAST];
 };
 
@@ -566,16 +584,19 @@ struct _MetaTheme
   char *copyright;
   char *date;
   char *description;
+  guint format_version;
 
   GHashTable *integer_constants;
   GHashTable *float_constants;
+  GHashTable *color_constants;
   GHashTable *images_by_filename;
   GHashTable *layouts_by_name;
   GHashTable *draw_op_lists_by_name;
   GHashTable *styles_by_name;
   GHashTable *style_sets_by_name;
   MetaFrameStyleSet *style_sets_by_type[META_FRAME_TYPE_LAST];
-  MetaDrawOpList *menu_icons[META_MENU_ICON_TYPE_LAST][N_GTK_STATES];
+
+  GdkPixbuf *fallback_icon, *fallback_mini_icon;
 };
 
 struct _MetaPositionExprEnv
@@ -616,7 +637,8 @@ void             meta_frame_layout_calc_
                                                   int                     client_width,
                                                   int                     client_height,
                                                   const MetaButtonLayout *button_layout,
-                                                  MetaFrameGeometry      *fgeom);
+                                                  MetaFrameGeometry      *fgeom,
+                                                  MetaTheme              *theme);
 
 gboolean         meta_frame_layout_validate      (const MetaFrameLayout *layout,
                                                   GError               **error);
@@ -703,6 +725,7 @@ void meta_frame_style_draw (MetaFrameSty
 
 
 gboolean       meta_frame_style_validate (MetaFrameStyle    *style,
+                                          guint              current_theme_version,
                                           GError           **error);
 
 MetaFrameStyleSet* meta_frame_style_set_new   (MetaFrameStyleSet *parent);
@@ -722,6 +745,7 @@ gboolean   meta_theme_validate (MetaThem
                                 GError   **error);
 GdkPixbuf* meta_theme_load_image (MetaTheme  *theme,
                                   const char *filename,
+                                  guint       size_of_theme_icons,
                                   GError    **error);
 
 MetaFrameStyle* meta_theme_get_frame_style (MetaTheme     *theme,
@@ -749,13 +773,6 @@ void meta_theme_draw_frame (MetaTheme   
                             GdkPixbuf              *mini_icon,
                             GdkPixbuf              *icon);
 
-void meta_theme_draw_menu_icon (MetaTheme          *theme,
-                                GtkWidget          *widget,
-                                GdkDrawable        *drawable,
-                                const GdkRectangle *clip,
-                                MetaRectangle       offset_rect,
-                                MetaMenuIconType    type);
-     
 void meta_theme_get_frame_borders (MetaTheme         *theme,
                                    MetaFrameType      type,
                                    int                text_height,
@@ -808,6 +825,14 @@ gboolean meta_theme_lookup_float_constan
                                            const char  *name,
                                            double      *value);
 
+gboolean meta_theme_define_color_constant (MetaTheme   *theme,
+                                           const char  *name,
+                                           const char  *value,
+                                           GError     **error);
+gboolean meta_theme_lookup_color_constant (MetaTheme   *theme,
+                                           const char  *name,
+                                           char       **value);
+
 char*    meta_theme_replace_constants     (MetaTheme   *theme,
                                            const char  *expr,
                                            GError     **err);
@@ -826,10 +851,9 @@ MetaGtkColorComponent meta_color_compone
 const char*           meta_color_component_to_string   (MetaGtkColorComponent  component);
 MetaButtonState       meta_button_state_from_string    (const char            *str);
 const char*           meta_button_state_to_string      (MetaButtonState        state);
-MetaButtonType        meta_button_type_from_string     (const char            *str);
+MetaButtonType        meta_button_type_from_string     (const char            *str,
+                                                        MetaTheme             *theme);
 const char*           meta_button_type_to_string       (MetaButtonType         type);
-MetaMenuIconType      meta_menu_icon_type_from_string  (const char            *str);
-const char*           meta_menu_icon_type_to_string    (MetaMenuIconType       type);
 MetaFramePiece        meta_frame_piece_from_string     (const char            *str);
 const char*           meta_frame_piece_to_string       (MetaFramePiece         piece);
 MetaFrameState        meta_frame_state_from_string     (const char            *str);
@@ -851,5 +875,19 @@ const char*           meta_gtk_arrow_to_
 MetaImageFillType     meta_image_fill_type_from_string (const char            *str);
 const char*           meta_image_fill_type_to_string   (MetaImageFillType      fill_type);
 
+guint meta_theme_earliest_version_with_button (MetaButtonType type);
+
+#define META_THEME_ALLOWS(theme, feature) (theme->format_version >= feature)
+
+/* What version of the theme file format were various features introduced in? */
+#define META_THEME_SHADE_STICK_ABOVE_BUTTONS 2
+#define META_THEME_UBIQUITOUS_CONSTANTS 2
+#define META_THEME_VARIED_ROUND_CORNERS 2
+#define META_THEME_IMAGES_FROM_ICON_THEMES 2
+#define META_THEME_UNRESIZABLE_SHADED_STYLES 2
+#define META_THEME_DEGREES_IN_ARCS 2
+#define META_THEME_HIDDEN_BUTTONS 2
+#define META_THEME_COLOR_CONSTANTS 2
+#define META_THEME_FRAME_BACKGROUNDS 2
 
 #endif
Index: src/ui.c
===================================================================
RCS file: /cvs/gnome/metacity/src/ui.c,v
retrieving revision 1.65
diff -u -p -r1.65 ui.c
--- src/ui.c	1 Oct 2006 22:30:10 -0000	1.65
+++ src/ui.c	7 Oct 2006 03:58:04 -0000
@@ -30,6 +30,7 @@
 #include "menu.h"
 #include "core.h"
 #include "theme.h"
+#include "iconcache.h"
 
 #include "inlinepixbufs.h"
 
@@ -736,6 +737,7 @@ meta_ui_set_current_theme (const char *n
                            gboolean    force_reload)
 {
   meta_theme_set_current (name, force_reload);
+  meta_invalidate_default_icons ();
 }
 
 gboolean
Index: src/window.c
===================================================================
RCS file: /cvs/gnome/metacity/src/window.c,v
retrieving revision 1.454
diff -u -p -r1.454 window.c
--- src/window.c	2 Oct 2006 04:08:33 -0000	1.454
+++ src/window.c	7 Oct 2006 03:58:10 -0000
@@ -110,7 +110,6 @@ static void meta_window_flush_calc_showi
 
 static void meta_window_unqueue_move_resize  (MetaWindow *window);
 
-static void meta_window_update_icon_now (MetaWindow *window);
 static void meta_window_unqueue_update_icon    (MetaWindow *window);
 
 static gboolean queue_calc_showing_func (MetaWindow *window,
@@ -5484,7 +5483,7 @@ redraw_icon (MetaWindow *window)
     meta_ui_queue_frame_draw (window->screen->ui, window->frame->xwindow);
 }
 
-static void
+void
 meta_window_update_icon_now (MetaWindow *window)
 {
   GdkPixbuf *icon;
Index: src/window.h
===================================================================
RCS file: /cvs/gnome/metacity/src/window.h,v
retrieving revision 1.136
diff -u -p -r1.136 window.h
--- src/window.h	1 Oct 2006 22:30:10 -0000	1.136
+++ src/window.h	7 Oct 2006 03:58:11 -0000
@@ -607,4 +607,7 @@ void meta_window_set_user_time (MetaWind
 void meta_window_set_demands_attention (MetaWindow *window);
 
 void meta_window_unset_demands_attention (MetaWindow *window);
+
+void meta_window_update_icon_now (MetaWindow *window);
+
 #endif
