Index: src/metacity.schemas.in
===================================================================
RCS file: /cvs/gnome/metacity/src/metacity.schemas.in,v
retrieving revision 1.6
diff -U5 -r1.6 metacity.schemas.in
--- src/metacity.schemas.in	2002/07/11 04:10:44	1.6
+++ src/metacity.schemas.in	2002/07/29 06:56:14
@@ -119,10 +119,33 @@
           your desktop by asking for 34 million workspaces).
          </long>
       </locale>
     </schema>
 
+    <schema>
+      <key>/schemas/apps/metacity/general/wrap_style</key>
+      <applyto>/apps/metacity/general/wrap_style</applyto>
+      <owner>metacity</owner>
+      <type>string</type>
+      <default>no wrap</default>
+      <locale name="C">
+         <short>Workspace wrap style</short>
+         <long>
+	  The wrap style is used to determine how to switch from one
+	  workspace to another at the border of the workspace switcher.
+	  When set to "no wrap", nothing will happen if you try to
+	  switch to a workspace past the border of the workspace switcher.
+	  If set to "classic", the old metacity behavior is used:
+	  the end of one row leads to the beginning of the next and
+	  the end of a column leads to the beginning of the next.
+	  If set to "toroidal", workspaces are connected like a doughnut:
+	  the end of each row leads to its own beginning and the
+	  end of each column leads to its own beginning.
+         </long>
+      </locale>
+    </schema>
+
     <!-- Window Keybindings -->
 
     <schema>
       <key>/schemas/apps/metacity/window_keybindings/activate_window_menu</key>
       <applyto>/apps/metacity/window_keybindings/activate_window_menu</applyto>
Index: src/prefs.c
===================================================================
RCS file: /cvs/gnome/metacity/src/prefs.c,v
retrieving revision 1.19
diff -U5 -r1.19 prefs.c
--- src/prefs.c	2002/07/14 03:16:41	1.19
+++ src/prefs.c	2002/07/29 06:56:15
@@ -35,10 +35,11 @@
 #define KEY_AUTO_RAISE_DELAY "/apps/metacity/general/auto_raise_delay"
 #define KEY_THEME "/apps/metacity/general/theme"
 #define KEY_USE_SYSTEM_FONT  "/apps/metacity/general/titlebar_uses_system_font"
 #define KEY_TITLEBAR_FONT "/apps/metacity/general/titlebar_font"
 #define KEY_NUM_WORKSPACES "/apps/metacity/general/num_workspaces"
+#define KEY_WRAP_STYLE "/apps/metacity/general/wrap_style"
 #define KEY_APPLICATION_BASED "/apps/metacity/general/application_based"
 #define KEY_DISABLE_WORKAROUNDS "/apps/metacity/general/disable_workarounds"
 
 #define KEY_COMMAND_PREFIX "/apps/metacity/keybinding_commands/command_"
 #define KEY_SCREEN_BINDINGS_PREFIX "/apps/metacity/global_keybindings"
@@ -51,10 +52,11 @@
 static gboolean use_system_font = TRUE;
 static PangoFontDescription *titlebar_font = NULL;
 static MetaFocusMode focus_mode = META_FOCUS_MODE_CLICK;
 static char* current_theme = NULL;
 static int num_workspaces = 4;
+static MetaWrapStyle wrap_style = META_WRAP_NONE ;
 static gboolean application_based = FALSE;
 static gboolean disable_workarounds = FALSE;
 static gboolean auto_raise = FALSE;
 static gboolean auto_raise_delay = 500;
 #define NUM_COMMANDS 12
@@ -64,10 +66,11 @@
 static gboolean update_use_system_font   (gboolean    value);
 static gboolean update_titlebar_font      (const char *value);
 static gboolean update_focus_mode         (const char *value);
 static gboolean update_theme              (const char *value);
 static gboolean update_num_workspaces     (int         value);
+static gboolean update_wrap_style         (const char* value);
 static gboolean update_application_based  (gboolean    value);
 static gboolean update_disable_workarounds (gboolean   value);
 static gboolean update_auto_raise          (gboolean   value);
 static gboolean update_auto_raise_delay    (int        value);
 static gboolean update_window_binding     (const char *name,
@@ -278,10 +281,16 @@
   int_val = gconf_client_get_int (default_client, KEY_NUM_WORKSPACES,
                                   &err);
   cleanup_error (&err);
   update_num_workspaces (int_val);
 
+  str_val = gconf_client_get_string (default_client, KEY_WRAP_STYLE,
+                                     &err);
+  cleanup_error (&err);
+  update_wrap_style (str_val);
+  g_free (str_val);
+
   bool_val = gconf_client_get_bool (default_client, KEY_APPLICATION_BASED,
                                     &err);
   cleanup_error (&err);
   update_application_based (bool_val);
 
@@ -419,10 +428,26 @@
       d = value ? gconf_value_get_int (value) : num_workspaces;
 
       if (update_num_workspaces (d))
         queue_changed (META_PREF_NUM_WORKSPACES);
     }
+  else if (strcmp (key, KEY_WRAP_STYLE) == 0)
+    {
+      const char* str;
+
+      if (value && value->type != GCONF_VALUE_STRING)
+        {
+          meta_warning (_("GConf key \"%s\" is set to an invalid type\n"),
+                        KEY_WRAP_STYLE);
+          goto out;
+        }
+
+      str = value ? gconf_value_get_string (value) : NULL ;
+
+      if (update_wrap_style (str))
+        queue_changed (META_PREF_WRAP_STYLE);
+    }
   else if (strcmp (key, KEY_APPLICATION_BASED) == 0)
     {
       gboolean b;
 
       if (value && value->type != GCONF_VALUE_BOOL)
@@ -687,10 +712,34 @@
 {
   return num_workspaces;
 }
 
 static gboolean
+update_wrap_style (const char* value)
+{
+  MetaWrapStyle old = wrap_style;
+
+	if ( !strcmp( value, META_WRAP_NONE_STRING ) )
+		wrap_style = META_WRAP_NONE ;
+	else if ( !strcmp( value, META_WRAP_CLASSIC_STRING ) )
+		wrap_style = META_WRAP_CLASSIC ;
+	else if ( !strcmp( value, META_WRAP_TOROIDAL_STRING ) )
+		wrap_style = META_WRAP_TOROIDAL ;
+	else
+		meta_warning (_("%s is not a valid wrap style. Try \"%s\", \"%s\", or \"%s\".\n"),
+				value, META_WRAP_NONE_STRING, META_WRAP_CLASSIC_STRING, META_WRAP_TOROIDAL_STRING );
+  
+  return old != wrap_style ;
+}
+
+MetaWrapStyle
+meta_prefs_get_wrap_style (void)
+{
+  return wrap_style;
+}
+
+static gboolean
 update_application_based (gboolean value)
 {
   gboolean old = application_based;
 
   application_based = value;
@@ -775,10 +824,13 @@
     case META_PREF_TITLEBAR_FONT:
       return "TITLEBAR_FONT";
 
     case META_PREF_NUM_WORKSPACES:
       return "NUM_WORKSPACES";
+
+    case META_PREF_WRAP_STYLE:
+      return "WRAP_STYLE";
 
     case META_PREF_APPLICATION_BASED:
       return "APPLICATION_BASED";
 
     case META_PREF_SCREEN_KEYBINDINGS:
Index: src/prefs.h
===================================================================
RCS file: /cvs/gnome/metacity/src/prefs.h,v
retrieving revision 1.16
diff -U5 -r1.16 prefs.h
--- src/prefs.h	2002/07/14 03:16:41	1.16
+++ src/prefs.h	2002/07/29 06:56:16
@@ -36,13 +36,26 @@
   META_PREF_NUM_WORKSPACES,
   META_PREF_APPLICATION_BASED,
   META_PREF_WINDOW_KEYBINDINGS,
   META_PREF_SCREEN_KEYBINDINGS,
   META_PREF_DISABLE_WORKAROUNDS,
-  META_PREF_COMMANDS
+  META_PREF_COMMANDS,
+  META_PREF_WRAP_STYLE
 } MetaPreference;
 
+/* Wrap styles */
+#define META_WRAP_NONE_STRING     "no wrap"
+#define META_WRAP_CLASSIC_STRING  "classic"
+#define META_WRAP_TOROIDAL_STRING "toroidal"
+
+typedef enum 
+{
+	META_WRAP_NONE,
+	META_WRAP_CLASSIC,
+	META_WRAP_TOROIDAL
+} MetaWrapStyle ;
+
 typedef void (* MetaPrefsChangedFunc) (MetaPreference pref,
                                        gpointer       data);
 
 void meta_prefs_add_listener    (MetaPrefsChangedFunc func,
                                  gpointer             data);
@@ -59,10 +72,11 @@
 int                         meta_prefs_get_num_workspaces     (void);
 gboolean                    meta_prefs_get_application_based  (void);
 gboolean                    meta_prefs_get_disable_workarounds (void);
 gboolean                    meta_prefs_get_auto_raise         (void);
 int                         meta_prefs_get_auto_raise_delay   (void);
+MetaWrapStyle               meta_prefs_get_wrap_style         (void);
 
 const char*                 meta_prefs_get_command            (int i);
 
 char*                       meta_prefs_get_gconf_key_for_command (int i);
 
Index: src/workspace.c
===================================================================
RCS file: /cvs/gnome/metacity/src/workspace.c,v
retrieving revision 1.28
diff -U5 -r1.28 workspace.c
--- src/workspace.c	2002/07/14 03:59:18	1.28
+++ src/workspace.c	2002/07/29 06:56:16
@@ -19,10 +19,11 @@
  * 02111-1307, USA.
  */
 
 #include <config.h>
 #include "workspace.h"
+#include "prefs.h"
 #include "errors.h"
 #include <X11/Xatom.h>
 #include <string.h>
 
 void meta_workspace_queue_calc_showing  (MetaWorkspace *workspace);
@@ -460,105 +461,156 @@
 meta_workspace_get_neighbor (MetaWorkspace      *workspace,
                              MetaMotionDirection direction)
 {
   int i, num_workspaces, grid_area;
   int rows, cols;
+  int x, y ;
+  int wrap = meta_prefs_get_wrap_style() ;
   
   i = meta_workspace_index (workspace);
   num_workspaces = meta_screen_get_n_workspaces (workspace->screen);
   
- meta_screen_calc_workspace_layout (workspace->screen, num_workspaces,
+  meta_screen_calc_workspace_layout (workspace->screen, num_workspaces,
                                     &rows, &cols);
 
   grid_area = rows * cols;
   
   meta_verbose ("Getting neighbor rows = %d cols = %d vert = %d "
                 "current = %d num_spaces = %d neighbor = %d\n",
                 rows, cols, workspace->screen->vertical_workspaces,
                 i, num_workspaces, direction);
 
   
-  /* FIXME someone who's better or more patient at math puzzles
-   * should clean this up. ;-)
-   */
-  if (workspace->screen->vertical_workspaces)
-    {
-      switch (direction)
-        {
-        case META_MOTION_LEFT:
-          if (i < rows)
-            {
-              i = grid_area - (i % rows) - 1;
-              while (i >= num_workspaces)
-                i -= rows;
-            }
-          else
-            i -= rows;
-          break;
-        case META_MOTION_RIGHT:
-          if ((i + rows) >= num_workspaces)
-            i = (i % rows + 1) % rows;
-          else
-            i += rows;
-          break;
-        case META_MOTION_UP:
-          --i;
-          if (i == -1)
-            i = num_workspaces - 1;
-          break;
-        case META_MOTION_DOWN:
-          ++i;
-          if (i == num_workspaces)
-            i = 0;
-          break;
-        }
-    }
-  else
-    {
-      switch (direction)
-        {
-        case META_MOTION_LEFT:
-          --i;
-          if (i == -1)
-            i = num_workspaces - 1;
-          break;
-        case META_MOTION_RIGHT:
-          ++i;
-          if (i == num_workspaces)
-            i = 0;
-          break;
-        case META_MOTION_UP:
-          if (i < cols)
-            {
-	      if (i == 0)
-		{
-		  /* special case: go to bottom right corner == grid_area - 1 */
-		  i = grid_area - 1;
-		  while (i >= num_workspaces)
-		    /* if we are in a non-existant workspace look up in the column for next workspace */
-		    i -= cols;
+  /* grid position */
+  if ( workspace->screen->vertical_workspaces ) {
+    x = i / rows ;
+    y = i % rows ;
+  } else {
+    x = i % cols ;
+    y = i / cols ;
+  }
+  switch (direction)
+    {
+      case META_MOTION_LEFT:
+	switch (wrap)
+	  {
+	    case META_WRAP_NONE:
+	      if ( x > 0 )
+		x-- ;
+	      break ;
+	    case META_WRAP_CLASSIC:
+	      if ( x == 0 ) {
+		y-- ;
+		if ( y < 0 )
+		  y = rows - 1 ;
+	      }
+	      /* fall through */
+	    case META_WRAP_TOROIDAL:
+	      x-- ;
+	      if ( x < 0 ) {
+		if ( (y*cols + cols - 1) >= num_workspaces )
+		  x = (num_workspaces-1) % cols ;
+		else
+		  x = cols - 1 ;
+	      }
+	      break ;
+	  }
+	break;
+      case META_MOTION_RIGHT:
+	switch (wrap)
+	  {
+	    case META_WRAP_NONE:
+	      if ( x < cols - 1 ) {
+		if ( (y*cols + x+1) >= num_workspaces ) {
+		  ;
+		} else {
+		  x++ ;
 		}
-	      else
-		{
-		  /* calculate new workspace based on the bottom left corner == ((rows - 1) * cols) */
-		  i = ((rows - 1) * cols) + (i - 1);
-		  while (i >= num_workspaces)
-		    /* if we are in a non-existant workspace look up in the column for next workspace */
-		    i -= cols;
+	      }
+	      break ;
+	    case META_WRAP_CLASSIC:
+	      if ( x == cols - 1 ) {
+		y++ ;
+		if ( y == rows )
+		  y = 0 ;
+	      } else if ( (y*cols + x+1) >= num_workspaces ) {
+		x = y = 0 ;
+		break ;
+	      }
+	      /* fall though */
+	    case META_WRAP_TOROIDAL:
+	      x++ ;
+	      if ( (x == cols) || ((y*cols + x) >= num_workspaces)  )
+		x = 0 ;
+	      break;
+	  }
+	break;
+      case META_MOTION_UP:
+	switch (wrap)
+	  {
+	    case META_WRAP_NONE:
+	      if ( y > 0 )
+		y-- ;
+	      break ;
+	    case META_WRAP_CLASSIC:
+	      if ( y == 0 ) {
+		x-- ;
+		if ( x < 0 ) {
+		  x = cols - 1 ;
+		  y = rows - 1 ;
+		  if ( (y*cols + x) >= num_workspaces )
+		    y-- ;
+		  break ;
 		}
-            }
-          else
-            i -= cols;
-          break;
-        case META_MOTION_DOWN:
-          if ((i + cols) >= num_workspaces)
-            i = (i % cols + 1) % cols;
-          else
-            i += cols;
-          break;
-        }
+	      }
+	      /* fall through */
+	    case META_WRAP_TOROIDAL:
+	      y-- ;
+	      if ( y < 0 ) {
+		y = rows - 1 ;
+		if ( (y*cols + x) >= num_workspaces )
+		  y-- ;
+	      }
+	      break ;
+	  }
+	break;
+      case META_MOTION_DOWN:
+	switch (wrap)
+	  {
+	    case META_WRAP_NONE:
+	      if ( y < rows - 1 ) {
+		if ( ((y+1)*cols + x) >= num_workspaces ) {
+		  ;
+		} else {
+		  y++ ;
+		}
+	      }
+	      break ;
+	    case META_WRAP_CLASSIC:
+	      if ( (y == rows - 1) || ( ((y+1)*cols + x) >= num_workspaces ) ) {
+		x++ ;
+		x = x % cols ;
+		if ( x == 0 ) {
+		  y = 0 ; 
+		  break ;
+		}
+	      }
+	      /* fall through */
+	    case META_WRAP_TOROIDAL:
+	      y++ ;
+	      if ( (y*cols + x) >= num_workspaces )
+		y = 0 ;
+	      else
+		y = y % rows ;
+	      break ;
+	  }
+	break;
     }
+  if ( workspace->screen->vertical_workspaces )
+    i = x*rows + y ;
+  else
+    i = y*cols + x ;
 
   meta_verbose ("Neighbor space is %d\n", i);
   
   return meta_display_get_workspace_by_index (workspace->screen->display,
                                               i);
