Index: configure.in
===================================================================
RCS file: /cvs/gnome/nautilus/configure.in,v
retrieving revision 1.662
diff -u -p -r1.662 configure.in
--- configure.in	2 Mar 2006 15:27:35 -0000	1.662
+++ configure.in	6 Mar 2006 11:58:03 -0000
@@ -394,6 +394,7 @@ cut-n-paste-code/libegg/Makefile
 cut-n-paste-code/gsequence/Makefile
 cut-n-paste-code/widgets/Makefile
 cut-n-paste-code/widgets/gimphwrapbox/Makefile
+cut-n-paste-code/libcoreutils/Makefile
 data/Makefile
 data/patterns/Makefile
 docs/Makefile
Index: cut-n-paste-code/Makefile.am
===================================================================
RCS file: /cvs/gnome/nautilus/cut-n-paste-code/Makefile.am,v
retrieving revision 1.5
diff -u -p -r1.5 Makefile.am
--- cut-n-paste-code/Makefile.am	27 Jun 2003 16:21:33 -0000	1.5
+++ cut-n-paste-code/Makefile.am	6 Mar 2006 11:58:04 -0000
@@ -1,2 +1,2 @@
-SUBDIRS = widgets libegg gsequence
+SUBDIRS = widgets libegg gsequence libcoreutils
 
Index: cut-n-paste-code/libcoreutils/Makefile.am
===================================================================
RCS file: cut-n-paste-code/libcoreutils/Makefile.am
diff -N cut-n-paste-code/libcoreutils/Makefile.am
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ cut-n-paste-code/libcoreutils/Makefile.am	6 Mar 2006 11:58:04 -0000
@@ -0,0 +1,12 @@
+NULL=
+
+noinst_LTLIBRARIES = libcoreutils.la
+
+# for glib
+INCLUDES = $(ALL_CFLAGS)
+
+libcoreutils_la_SOURCES = \
+	modechange.c \
+	modechange.h \
+	xalloc.h
+	$(NULL)
Index: cut-n-paste-code/libcoreutils/modechange.c
===================================================================
RCS file: cut-n-paste-code/libcoreutils/modechange.c
diff -N cut-n-paste-code/libcoreutils/modechange.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ cut-n-paste-code/libcoreutils/modechange.c	6 Mar 2006 11:58:04 -0000
@@ -0,0 +1,353 @@
+/* modechange.c -- file mode manipulation
+
+   Copyright (C) 1989, 1990, 1997, 1998, 1999, 2001, 2003, 2004, 2005
+   Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+/* Written by David MacKenzie <djm@ai.mit.edu> */
+
+/* The ASCII mode string is compiled into an array of `struct
+   modechange', which can then be applied to each file to be changed.
+   We do this instead of re-parsing the ASCII string for each file
+   because the compiled form requires less computation to use; when
+   changing the mode of many files, this probably results in a
+   performance gain.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "modechange.h"
+#include <sys/stat.h>
+#include "stat-macros.h"
+#include "xalloc.h"
+#include <stdlib.h>
+
+/* The traditional octal values corresponding to each mode bit.  */
+#define SUID 04000
+#define SGID 02000
+#define SVTX 01000
+#define RUSR 00400
+#define WUSR 00200
+#define XUSR 00100
+#define RGRP 00040
+#define WGRP 00020
+#define XGRP 00010
+#define ROTH 00004
+#define WOTH 00002
+#define XOTH 00001
+#define ALLM 07777 /* all octal mode bits */
+
+/* Special operations flags.  */
+enum
+  {
+    /* For the sentinel at the end of the mode changes array.  */
+    MODE_DONE,
+
+    /* The typical case.  */
+    MODE_ORDINARY_CHANGE,
+
+    /* In addition to the typical case, affect the execute bits if at
+       least one execute bit is set already, or if the file is a
+       directory.  */
+    MODE_X_IF_ANY_X,
+
+    /* Instead of the typical case, copy some existing permissions for
+       u, g, or o onto the other two.  Which of u, g, or o is copied
+       is determined by which bits are set in the `value' field.  */
+    MODE_COPY_EXISTING
+  };
+
+/* Description of a mode change.  */
+struct mode_change
+{
+  char op;			/* One of "=+-".  */
+  char flag;			/* Special operations flag.  */
+  mode_t affected;		/* Set for u, g, o, or a.  */
+  mode_t value;			/* Bits to add/remove.  */
+};
+
+/* Return a mode_change array with the specified `=ddd'-style
+   mode change operation, where NEW_MODE is `ddd'.  */
+
+static struct mode_change *
+make_node_op_equals (mode_t new_mode)
+{
+  struct mode_change *p = xmalloc (2 * sizeof *p);
+  p->op = '=';
+  p->flag = MODE_ORDINARY_CHANGE;
+  p->affected = CHMOD_MODE_BITS;
+  p->value = new_mode;
+  p[1].flag = MODE_DONE;
+  return p;
+}
+
+/* Return a pointer to an array of file mode change operations created from
+   MODE_STRING, an ASCII string that contains either an octal number
+   specifying an absolute mode, or symbolic mode change operations with
+   the form:
+   [ugoa...][[+-=][rwxXstugo...]...][,...]
+
+   Return NULL if `mode_string' does not contain a valid
+   representation of file mode change operations.  */
+
+struct mode_change *
+mode_compile (char const *mode_string)
+{
+  /* The array of mode-change directives to be returned.  */
+  struct mode_change *mc;
+  size_t used = 0;
+
+  if ('0' <= *mode_string && *mode_string < '8')
+    {
+      mode_t mode;
+      unsigned int octal_value = 0;
+
+      do
+	{
+	  octal_value = 8 * octal_value + *mode_string++ - '0';
+	  if (ALLM < octal_value)
+	    return NULL;
+	}
+      while ('0' <= *mode_string && *mode_string < '8');
+
+      if (*mode_string)
+	return NULL;
+
+      /* Help the compiler optimize the usual case where mode_t uses
+	 the traditional octal representation.  */
+      mode = ((S_ISUID == SUID && S_ISGID == SGID && S_ISVTX == SVTX
+	       && S_IRUSR == RUSR && S_IWUSR == WUSR && S_IXUSR == XUSR
+	       && S_IRGRP == RGRP && S_IWGRP == WGRP && S_IXGRP == XGRP
+	       && S_IROTH == ROTH && S_IWOTH == WOTH && S_IXOTH == XOTH)
+	      ? octal_value
+	      : (mode_t) ((octal_value & SUID ? S_ISUID : 0)
+			  | (octal_value & SGID ? S_ISGID : 0)
+			  | (octal_value & SVTX ? S_ISVTX : 0)
+			  | (octal_value & RUSR ? S_IRUSR : 0)
+			  | (octal_value & WUSR ? S_IWUSR : 0)
+			  | (octal_value & XUSR ? S_IXUSR : 0)
+			  | (octal_value & RGRP ? S_IRGRP : 0)
+			  | (octal_value & WGRP ? S_IWGRP : 0)
+			  | (octal_value & XGRP ? S_IXGRP : 0)
+			  | (octal_value & ROTH ? S_IROTH : 0)
+			  | (octal_value & WOTH ? S_IWOTH : 0)
+			  | (octal_value & XOTH ? S_IXOTH : 0)));
+
+      return make_node_op_equals (mode);
+    }
+
+  /* Allocate enough space to hold the result.  */
+  {
+    size_t needed = 1;
+    char const *p;
+    for (p = mode_string; *p; p++)
+      needed += (*p == '=' || *p == '+' || *p == '-');
+    mc = xnmalloc (needed, sizeof *mc);
+  }
+
+  /* One loop iteration for each `[ugoa]*([-+=]([rwxXst]*|[ugo]))+'.  */
+  for (;; mode_string++)
+    {
+      /* Which bits in the mode are operated on.  */
+      mode_t affected = 0;
+
+      /* Turn on all the bits in `affected' for each group given.  */
+      for (;; mode_string++)
+	switch (*mode_string)
+	  {
+	  default:
+	    goto invalid;
+	  case 'u':
+	    affected |= S_ISUID | S_IRWXU;
+	    break;
+	  case 'g':
+	    affected |= S_ISGID | S_IRWXG;
+	    break;
+	  case 'o':
+	    affected |= S_ISVTX | S_IRWXO;
+	    break;
+	  case 'a':
+	    affected |= CHMOD_MODE_BITS;
+	    break;
+	  case '=': case '+': case '-':
+	    goto no_more_affected;
+	  }
+    no_more_affected:;
+
+      do
+	{
+	  char op = *mode_string++;
+	  mode_t value;
+	  char flag = MODE_COPY_EXISTING;
+	  struct mode_change *change;
+
+	  switch (*mode_string++)
+	    {
+	    case 'u':
+	      /* Set the affected bits to the value of the `u' bits
+		 on the same file.  */
+	      value = S_IRWXU;
+	      break;
+	    case 'g':
+	      /* Set the affected bits to the value of the `g' bits
+		 on the same file.  */
+	      value = S_IRWXG;
+	      break;
+	    case 'o':
+	      /* Set the affected bits to the value of the `o' bits
+		 on the same file.  */
+	      value = S_IRWXO;
+	      break;
+
+	    default:
+	      value = 0;
+	      flag = MODE_ORDINARY_CHANGE;
+
+	      for (mode_string--;; mode_string++)
+		switch (*mode_string)
+		  {
+		  case 'r':
+		    value |= S_IRUSR | S_IRGRP | S_IROTH;
+		    break;
+		  case 'w':
+		    value |= S_IWUSR | S_IWGRP | S_IWOTH;
+		    break;
+		  case 'x':
+		    value |= S_IXUSR | S_IXGRP | S_IXOTH;
+		    break;
+		  case 'X':
+		    flag = MODE_X_IF_ANY_X;
+		    break;
+		  case 's':
+		    /* Set the setuid/gid bits if `u' or `g' is selected.  */
+		    value |= S_ISUID | S_ISGID;
+		    break;
+		  case 't':
+		    /* Set the "save text image" bit if `o' is selected.  */
+		    value |= S_ISVTX;
+		    break;
+		  default:
+		    goto no_more_values;
+		  }
+	    no_more_values:;
+	    }
+
+	  change = &mc[used++];
+	  change->op = op;
+	  change->flag = flag;
+	  change->affected = affected;
+	  change->value = value;
+	}
+      while (*mode_string == '=' || *mode_string == '+'
+	     || *mode_string == '-');
+
+      if (*mode_string != ',')
+	break;
+    }
+
+  if (*mode_string == 0)
+    {
+      mc[used].flag = MODE_DONE;
+      return mc;
+    }
+
+invalid:
+  free (mc);
+  return NULL;
+}
+
+/* Return a file mode change operation that sets permissions to match those
+   of REF_FILE.  Return NULL (setting errno) if REF_FILE can't be accessed.  */
+
+struct mode_change *
+mode_create_from_ref (const char *ref_file)
+{
+  struct stat ref_stats;
+
+  if (stat (ref_file, &ref_stats) != 0)
+    return NULL;
+  return make_node_op_equals (ref_stats.st_mode);
+}
+
+/* Return file mode OLDMODE, adjusted as indicated by the list of change
+   operations CHANGES, which are interpreted assuming the umask is
+   UMASK_VALUE.  If OLDMODE is a directory, the type `X'
+   change affects it even if no execute bits were set in OLDMODE.
+   The returned value has the S_IFMT bits cleared.  */
+
+mode_t
+mode_adjust (mode_t oldmode, struct mode_change const *changes,
+	     mode_t umask_value)
+{
+  /* The adjusted mode.  */
+  mode_t newmode = oldmode & CHMOD_MODE_BITS;
+
+  for (; changes->flag != MODE_DONE; changes++)
+    {
+      mode_t affected = changes->affected;
+      mode_t value = changes->value;
+
+      switch (changes->flag)
+	{
+	case MODE_ORDINARY_CHANGE:
+	  break;
+
+	case MODE_COPY_EXISTING:
+	  /* Isolate in `value' the bits in `newmode' to copy.  */
+	  value &= newmode;
+
+	  /* Copy the isolated bits to the other two parts.  */
+	  value |= ((value & (S_IRUSR | S_IRGRP | S_IROTH)
+		     ? S_IRUSR | S_IRGRP | S_IROTH : 0)
+		    | (value & (S_IWUSR | S_IWGRP | S_IWOTH)
+		       ? S_IWUSR | S_IWGRP | S_IWOTH : 0)
+		    | (value & (S_IXUSR | S_IXGRP | S_IXOTH)
+		       ? S_IXUSR | S_IXGRP | S_IXOTH : 0));
+	  break;
+
+	case MODE_X_IF_ANY_X:
+	  /* Affect the execute bits if execute bits are already set
+	     or if the file is a directory.  */
+	  if ((newmode & (S_IXUSR | S_IXGRP | S_IXOTH)) || S_ISDIR (oldmode))
+	    value |= S_IXUSR | S_IXGRP | S_IXOTH;
+	  break;
+	}
+
+      /* If WHO was specified, limit the change to the affected bits.
+	 Otherwise, apply the umask.  */
+      value &= (affected ? affected : ~umask_value);
+
+      switch (changes->op)
+	{
+	case '=':
+	  /* If WHO was specified, preserve the previous values of
+	     bits that are not affected by this change operation.
+	     Otherwise, clear all the bits.  */
+	  newmode = (affected ? newmode & ~affected : 0);
+	  /* Fall through.  */
+	case '+':
+	  newmode |= value;
+	  break;
+
+	case '-':
+	  newmode &= ~value;
+	  break;
+	}
+    }
+
+  return newmode;
+}
Index: cut-n-paste-code/libcoreutils/modechange.h
===================================================================
RCS file: cut-n-paste-code/libcoreutils/modechange.h
diff -N cut-n-paste-code/libcoreutils/modechange.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ cut-n-paste-code/libcoreutils/modechange.h	6 Mar 2006 11:58:04 -0000
@@ -0,0 +1,31 @@
+/* modechange.h -- definitions for file mode manipulation
+
+   Copyright (C) 1989, 1990, 1997, 2003, 2004, 2005 Free Software
+   Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+/* Masks for the `flags' field in a `struct mode_change'. */
+
+#if ! defined MODECHANGE_H_
+# define MODECHANGE_H_
+
+# include <sys/types.h>
+
+struct mode_change *mode_compile (const char *);
+struct mode_change *mode_create_from_ref (const char *);
+mode_t mode_adjust (mode_t, struct mode_change const *, mode_t);
+
+#endif
Index: cut-n-paste-code/libcoreutils/stat-macros.h
===================================================================
RCS file: cut-n-paste-code/libcoreutils/stat-macros.h
diff -N cut-n-paste-code/libcoreutils/stat-macros.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ cut-n-paste-code/libcoreutils/stat-macros.h	6 Mar 2006 11:58:04 -0000
@@ -0,0 +1,255 @@
+/* stat-related macros
+
+   Copyright (C) 1993, 1994, 2001, 2002, 2004 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+/* Written by Paul Eggert and Jim Meyering.  */
+
+#ifndef STAT_MACROS_H
+# define STAT_MACROS_H 1
+
+# if ! defined S_ISREG && ! defined S_IFREG
+#  error "you must include <sys/stat.h> before including this file"
+# endif
+
+# ifndef S_IFMT
+#  define S_IFMT 0170000
+# endif
+
+# if STAT_MACROS_BROKEN
+#  undef S_ISBLK
+#  undef S_ISCHR
+#  undef S_ISDIR
+#  undef S_ISDOOR
+#  undef S_ISFIFO
+#  undef S_ISLNK
+#  undef S_ISNAM
+#  undef S_ISMPB
+#  undef S_ISMPC
+#  undef S_ISNWK
+#  undef S_ISREG
+#  undef S_ISSOCK
+# endif
+
+
+# ifndef S_ISBLK
+#  ifdef S_IFBLK
+#   define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
+#  else
+#   define S_ISBLK(m) 0
+#  endif
+# endif
+
+# ifndef S_ISCHR
+#  ifdef S_IFCHR
+#   define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
+#  else
+#   define S_ISCHR(m) 0
+#  endif
+# endif
+
+# ifndef S_ISDIR
+#  ifdef S_IFDIR
+#   define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#  else
+#   define S_ISDIR(m) 0
+#  endif
+# endif
+
+# ifndef S_ISDOOR /* Solaris 2.5 and up */
+#  ifdef S_IFDOOR
+#   define S_ISDOOR(m) (((m) & S_IFMT) == S_IFDOOR)
+#  else
+#   define S_ISDOOR(m) 0
+#  endif
+# endif
+
+# ifndef S_ISFIFO
+#  ifdef S_IFIFO
+#   define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
+#  else
+#   define S_ISFIFO(m) 0
+#  endif
+# endif
+
+# ifndef S_ISLNK
+#  ifdef S_IFLNK
+#   define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
+#  else
+#   define S_ISLNK(m) 0
+#  endif
+# endif
+
+# ifndef S_ISMPB /* V7 */
+#  ifdef S_IFMPB
+#   define S_ISMPB(m) (((m) & S_IFMT) == S_IFMPB)
+#   define S_ISMPC(m) (((m) & S_IFMT) == S_IFMPC)
+#  else
+#   define S_ISMPB(m) 0
+#   define S_ISMPC(m) 0
+#  endif
+# endif
+
+# ifndef S_ISNAM /* Xenix */
+#  ifdef S_IFNAM
+#   define S_ISNAM(m) (((m) & S_IFMT) == S_IFNAM)
+#  else
+#   define S_ISNAM(m) 0
+#  endif
+# endif
+
+# ifndef S_ISNWK /* HP/UX */
+#  ifdef S_IFNWK
+#   define S_ISNWK(m) (((m) & S_IFMT) == S_IFNWK)
+#  else
+#   define S_ISNWK(m) 0
+#  endif
+# endif
+
+# ifndef S_ISREG
+#  ifdef S_IFREG
+#   define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#  else
+#   define S_ISREG(m) 0
+#  endif
+# endif
+
+# ifndef S_ISSOCK
+#  ifdef S_IFSOCK
+#   define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
+#  else
+#   define S_ISSOCK(m) 0
+#  endif
+# endif
+
+
+# ifndef S_TYPEISMQ
+#  define S_TYPEISMQ(p) 0
+# endif
+
+# ifndef S_TYPEISTMO
+#  define S_TYPEISTMO(p) 0
+# endif
+
+
+# ifndef S_TYPEISSEM
+#  ifdef S_INSEM
+#   define S_TYPEISSEM(p) (S_ISNAM ((p)->st_mode) && (p)->st_rdev == S_INSEM)
+#  else
+#   define S_TYPEISSEM(p) 0
+#  endif
+# endif
+
+# ifndef S_TYPEISSHM
+#  ifdef S_INSHD
+#   define S_TYPEISSHM(p) (S_ISNAM ((p)->st_mode) && (p)->st_rdev == S_INSHD)
+#  else
+#   define S_TYPEISSHM(p) 0
+#  endif
+# endif
+
+/* contiguous */
+# ifndef S_ISCTG
+#  define S_ISCTG(p) 0
+# endif
+
+/* Cray DMF (data migration facility): off line, with data  */
+# ifndef S_ISOFD
+#  define S_ISOFD(p) 0
+# endif
+
+/* Cray DMF (data migration facility): off line, with no data  */
+# ifndef S_ISOFL
+#  define S_ISOFL(p) 0
+# endif
+
+/* If any of the following are undefined,
+   define them to their de facto standard values.  */
+# if !S_ISUID
+#  define S_ISUID 04000
+# endif
+# if !S_ISGID
+#  define S_ISGID 02000
+# endif
+
+/* S_ISVTX is a common extension to POSIX.  */
+# ifndef S_ISVTX
+#  define S_ISVTX 01000
+# endif
+
+# if !S_IRUSR && S_IREAD
+#  define S_IRUSR S_IREAD
+# endif
+# if !S_IRUSR
+#  define S_IRUSR 00400
+# endif
+# if !S_IRGRP
+#  define S_IRGRP (S_IRUSR >> 3)
+# endif
+# if !S_IROTH
+#  define S_IROTH (S_IRUSR >> 6)
+# endif
+
+# if !S_IWUSR && S_IWRITE
+#  define S_IWUSR S_IWRITE
+# endif
+# if !S_IWUSR
+#  define S_IWUSR 00200
+# endif
+# if !S_IWGRP
+#  define S_IWGRP (S_IWUSR >> 3)
+# endif
+# if !S_IWOTH
+#  define S_IWOTH (S_IWUSR >> 6)
+# endif
+
+# if !S_IXUSR && S_IEXEC
+#  define S_IXUSR S_IEXEC
+# endif
+# if !S_IXUSR
+#  define S_IXUSR 00100
+# endif
+# if !S_IXGRP
+#  define S_IXGRP (S_IXUSR >> 3)
+# endif
+# if !S_IXOTH
+#  define S_IXOTH (S_IXUSR >> 6)
+# endif
+
+# if !S_IRWXU
+#  define S_IRWXU (S_IRUSR | S_IWUSR | S_IXUSR)
+# endif
+# if !S_IRWXG
+#  define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
+# endif
+# if !S_IRWXO
+#  define S_IRWXO (S_IROTH | S_IWOTH | S_IXOTH)
+# endif
+
+/* S_IXUGO is a common extension to POSIX.  */
+# if !S_IXUGO
+#  define S_IXUGO (S_IXUSR | S_IXGRP | S_IXOTH)
+# endif
+
+# ifndef S_IRWXUGO
+#  define S_IRWXUGO (S_IRWXU | S_IRWXG | S_IRWXO)
+# endif
+
+/* All the mode bits that can be affected by chmod.  */
+# define CHMOD_MODE_BITS \
+  (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
+
+#endif /* STAT_MACROS_H */
Index: cut-n-paste-code/libcoreutils/xalloc.h
===================================================================
RCS file: cut-n-paste-code/libcoreutils/xalloc.h
diff -N cut-n-paste-code/libcoreutils/xalloc.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ cut-n-paste-code/libcoreutils/xalloc.h	6 Mar 2006 11:58:04 -0000
@@ -0,0 +1,7 @@
+/* xalloc fake header to trick libcoreutils */
+
+#include <glib.h>
+
+#define xmalloc g_malloc
+#define xnmalloc(siz,count) \
+	g_malloc (((gsize) siz) * ((gsize) (count)))
Index: src/Makefile.am
===================================================================
RCS file: /cvs/gnome/nautilus/src/Makefile.am,v
retrieving revision 1.163
diff -u -p -r1.163 Makefile.am
--- src/Makefile.am	12 Dec 2005 16:59:11 -0000	1.163
+++ src/Makefile.am	6 Mar 2006 11:58:04 -0000
@@ -30,6 +30,7 @@ LDADD =\
 	$(top_builddir)/src/file-manager/libnautilus-file-manager.la \
 	$(top_builddir)/libnautilus-private/libnautilus-private.la  \
         $(top_builddir)/cut-n-paste-code/gsequence/libgsequence.la \
+        $(top_builddir)/cut-n-paste-code/libcoreutils/libcoreutils.la \
 	$(CORE_LIBS) \
 	$(EXIF_LIBS) \
 	$(POPT_LIBS) \
Index: src/file-manager/fm-properties-window.c
===================================================================
RCS file: /cvs/gnome/nautilus/src/file-manager/fm-properties-window.c,v
retrieving revision 1.229
diff -u -p -r1.229 fm-properties-window.c
--- src/file-manager/fm-properties-window.c	15 Feb 2006 17:19:53 -0000	1.229
+++ src/file-manager/fm-properties-window.c	6 Mar 2006 11:58:06 -0000
@@ -26,6 +26,7 @@
 #include "fm-properties-window.h"
 
 #include "fm-error-reporting.h"
+#include <glib/gi18n.h>
 #include <eel/eel-accessibility.h>
 #include <eel/eel-ellipsizing-label.h>
 #include <eel/eel-gdk-pixbuf-extensions.h>
@@ -46,7 +47,6 @@
 #include <gtk/gtkfilesel.h>
 #include <gtk/gtkhbox.h>
 #include <gtk/gtkhbbox.h>
-#include <gtk/gtkhseparator.h>
 #include <gtk/gtkimage.h>
 #include <gtk/gtklabel.h>
 #include <gtk/gtkmain.h>
@@ -59,7 +59,6 @@
 #include <gtk/gtkvbox.h>
 #include <gtk/gtktreemodel.h>
 #include <gtk/gtkliststore.h>
-#include <libgnome/gnome-i18n.h>
 #include <libgnome/gnome-macros.h>
 #include <libgnomeui/gnome-dialog.h>
 #include <libgnomeui/gnome-help.h>
@@ -68,7 +67,9 @@
 #include <libgnomevfs/gnome-vfs.h>
 #include <libnautilus-extension/nautilus-property-page-provider.h>
 #include <libnautilus-private/nautilus-customization-data.h>
+#include <libnautilus-private/nautilus-directory.h>
 #include <libnautilus-private/nautilus-entry.h>
+#include <libnautilus-private/nautilus-file.h>
 #include <libnautilus-private/nautilus-file-attributes.h>
 #include <libnautilus-private/nautilus-desktop-icon-file.h>
 #include <libnautilus-private/nautilus-global-preferences.h>
@@ -82,6 +83,13 @@
 #include <libnautilus-private/nautilus-undo.h>
 #include <string.h>
 
+/* for umask, mode_t */
+#include <sys/stat.h>
+#include <sys/types.h>
+
+/* for chmod parsing */
+#include <libcoreutils/modechange.h>
+
 #define PREVIEW_IMAGE_WIDTH 96
 
 static GHashTable *windows;
@@ -109,15 +117,14 @@ struct FMPropertiesWindowDetails {	
 	guint update_directory_contents_timeout_id;
 	guint update_files_timeout_id;
 
-	GList *special_flags_widgets;
-	int first_special_flags_row;
-	int num_special_flags_rows;
-
 	GList *emblem_buttons;
 	GHashTable *initial_emblems;
 
-	GList *permission_buttons;
-	GHashTable *initial_permissions;
+	GList *permission_combo_boxes;
+	GList *executable_buttons;
+
+	GtkWidget *details_expander;
+	GtkWidget *chmod_entry;
 
 	GList *value_fields;
 
@@ -134,20 +141,6 @@ struct FMPropertiesWindowDetails {	
 };
 
 enum {
-	PERMISSIONS_CHECKBOXES_OWNER_ROW,
-	PERMISSIONS_CHECKBOXES_GROUP_ROW,
-	PERMISSIONS_CHECKBOXES_OTHERS_ROW,
-	PERMISSIONS_CHECKBOXES_ROW_COUNT
-};
-
-enum {
-	PERMISSIONS_CHECKBOXES_READ_COLUMN,
-	PERMISSIONS_CHECKBOXES_WRITE_COLUMN,
-	PERMISSIONS_CHECKBOXES_EXECUTE_COLUMN,
-	PERMISSIONS_CHECKBOXES_COLUMN_COUNT
-};
-
-enum {
 	TITLE_COLUMN,
 	VALUE_COLUMN,
 	COLUMN_COUNT
@@ -175,6 +168,22 @@ static const GtkTargetEntry target_table
 	{ "x-special/gnome-reset-background", 0, TARGET_RESET_BACKGROUND }
 };
 
+typedef struct {
+	FMPropertiesWindow *window;
+
+	GnomeVFSFilePermissions file_on;
+	GnomeVFSFilePermissions file_off;
+
+	GnomeVFSFilePermissions folder_on;
+	GnomeVFSFilePermissions folder_off;
+
+	struct mode_change *chmod_mode_change;
+
+	gboolean is_recursive;
+
+	unsigned int ref;
+} PermissionsChangeRequest;
+
 #define DIRECTORY_CONTENTS_UPDATE_INTERVAL	200 /* milliseconds */
 #define FILES_UPDATE_INTERVAL			200 /* milliseconds */
 #define STANDARD_EMBLEM_HEIGHT			52
@@ -183,12 +192,19 @@ static const GtkTargetEntry target_table
 static void directory_contents_value_field_update (FMPropertiesWindow *window);
 static void file_changed_callback                 (NautilusFile       *file,
 						   gpointer            user_data);
-static void permission_button_update              (FMPropertiesWindow *window,
-						   GtkToggleButton    *button);
+static void permission_combo_box_update           (FMPropertiesWindow *window,
+						   GtkComboBox        *combo);
+static void executable_button_update              (FMPropertiesWindow *window,
+						   GtkCheckButton     *button);
+static void executable_button_toggled             (GtkToggleButton    *button,
+						   FMPropertiesWindow *window);
 static void value_field_update                    (FMPropertiesWindow *window,
 						   GtkLabel           *field);
 static void properties_window_update              (FMPropertiesWindow *window,
 						   GList              *files);
+static void permission_change_callback            (NautilusFile       *file,
+						   GnomeVFSResult      result,
+						   PermissionsChangeRequest *request);
 static void is_directory_ready_callback           (NautilusFile       *file,
 						   gpointer            data);
 static void cancel_group_change_callback          (gpointer            callback_data);
@@ -322,6 +338,40 @@ get_target_file (FMPropertiesWindow *win
 	return NAUTILUS_FILE (window->details->target_files->data);
 }
 
+static NautilusFile *
+get_first_directory_target_file (FMPropertiesWindow *window)
+{
+	NautilusFile *file;
+	GList *l;
+
+	for (l = window->details->target_files; l != NULL; l = l->next) {
+		file = NAUTILUS_FILE (l->data);
+
+		if (nautilus_file_is_directory (file)) {
+			return file;
+		}
+	}
+
+	return NULL;
+}
+
+static NautilusFile *
+get_first_nondirectory_target_file (FMPropertiesWindow *window)
+{
+	NautilusFile *file;
+	GList *l;
+
+	for (l = window->details->target_files; l != NULL; l = l->next) {
+		file = NAUTILUS_FILE (l->data);
+
+		if (!nautilus_file_is_directory (file)) {
+			return file;
+		}
+	}
+
+	return NULL;
+}
+
 static void
 add_prompt (GtkVBox *vbox, const char *prompt_text, gboolean pack_at_start)
 {
@@ -338,18 +388,6 @@ add_prompt (GtkVBox *vbox, const char *p
 	}
 }
 
-static void
-add_prompt_and_separator (GtkVBox *vbox, const char *prompt_text)
-{
-	GtkWidget *separator_line;
-
-	add_prompt (vbox, prompt_text, FALSE);
-
- 	separator_line = gtk_hseparator_new ();
-  	gtk_widget_show (separator_line);
-  	gtk_box_pack_end (GTK_BOX (vbox), separator_line, TRUE, TRUE, GNOME_PAD_BIG);
-}
-
 static GdkPixbuf *
 get_pixbuf_for_properties_window (FMPropertiesWindow *window)
 {
@@ -1068,7 +1106,6 @@ remove_from_dialog (FMPropertiesWindow *
 	g_list_free (target_link);
 
 	g_hash_table_remove (window->details->initial_emblems, original_file);
-	g_hash_table_remove (window->details->initial_permissions, target_file);
 
 	g_signal_handlers_disconnect_by_func (original_file,
 					      G_CALLBACK (file_changed_callback),
@@ -1167,10 +1204,14 @@ properties_window_update (FMPropertiesWi
 	}
 
 	if (dirty_target) {
-		for (l = window->details->permission_buttons; l != NULL; l = l->next) {
-			permission_button_update (window, GTK_TOGGLE_BUTTON (l->data));
+		for (l = window->details->permission_combo_boxes; l != NULL; l = l->next) {
+			permission_combo_box_update (window, GTK_COMBO_BOX (l->data));
 		}
-		
+
+		for (l = window->details->executable_buttons; l != NULL; l = l->next) {
+			executable_button_update (window, GTK_CHECK_BUTTON (l->data));
+		}
+
 		for (l = window->details->value_fields; l != NULL; l = l->next) {
 			value_field_update (window, GTK_LABEL (l->data));
 		}
@@ -1299,18 +1340,26 @@ file_list_all_local (GList *file_list)
 	return TRUE;
 }
 
-static gboolean
-file_list_all_directories (GList *file_list)
+static unsigned int
+file_list_count_directories (GList *file_list)
 {
 	GList *l;
+	NautilusFile *file;
+	int i = 0;
+
 	for (l = file_list; l != NULL; l = l->next) {
-		if (!nautilus_file_is_directory (NAUTILUS_FILE (l->data))) {
-			return FALSE;
+		file = NAUTILUS_FILE (l->data);
+
+		if (nautilus_file_is_directory (file)) {
+			i++;
 		}
 	}
-	return TRUE;
+
+	return i;
 }
 
+
+
 static void
 value_field_update_internal (GtkLabel *label, 
 			     GList *file_list, 
@@ -1422,7 +1471,8 @@ attach_value_field_internal (FMPropertie
 			     const char *file_attribute_name,
 			     const char *inconsistent_string,
 			     gboolean show_original,
-			     gboolean ellipsize_text)
+			     gboolean ellipsize_text,
+			     gboolean update_now)
 {
 	GtkLabel *value_field;
 
@@ -1444,6 +1494,10 @@ attach_value_field_internal (FMPropertie
 
 	window->details->value_fields = g_list_prepend (window->details->value_fields,
 							value_field);
+
+	if (update_now) {
+		value_field_update (window, value_field);
+	}
 }			     
 
 static void
@@ -1453,14 +1507,16 @@ attach_value_field (FMPropertiesWindow *
 		    int column,
 		    const char *file_attribute_name,
 		    const char *inconsistent_string,
-		    gboolean show_original)
+		    gboolean show_original,
+		    gboolean update_now)
 {
 	attach_value_field_internal (window, 
 				     table, row, column, 
 				     file_attribute_name, 
 				     inconsistent_string,
 				     show_original,
-				     FALSE);
+				     FALSE,
+				     update_now);
 }
 
 static void
@@ -1470,14 +1526,16 @@ attach_ellipsizing_value_field (FMProper
 		    		int column,
 		    		const char *file_attribute_name,
 				const char *inconsistent_string,
-				gboolean show_original)
+				gboolean show_original,
+				gboolean update_now)
 {
 	attach_value_field_internal (window,
 				     table, row, column, 
 				     file_attribute_name, 
 				     inconsistent_string, 
 				     show_original,
-				     TRUE);
+				     TRUE,
+				     update_now);
 }
 
 static void
@@ -1616,7 +1674,6 @@ attach_combo_box (GtkTable *table,
 		    int row)
 {
 	GtkWidget *combo_box;
-	GtkWidget *aligner;
 
 	combo_box = gtk_combo_box_new_text ();
 	gtk_widget_show (combo_box);
@@ -1626,17 +1683,10 @@ attach_combo_box (GtkTable *table,
 					      NULL,
 					      NULL);
 
-	/* Put combo box in alignment to make it left-justified
-	 * but minimally sized.
-	 */	
-	aligner = gtk_alignment_new (0, 0.5, 0, 0);
-	gtk_widget_show (aligner);
-
-	gtk_container_add (GTK_CONTAINER (aligner), combo_box);
-	gtk_table_attach (table, aligner,
+	gtk_table_attach (table, combo_box,
 			  VALUE_COLUMN, VALUE_COLUMN + 1,
 			  row, row + 1,
-			  GTK_FILL, 0,
+			  GTK_FILL | GTK_EXPAND, 0,
 			  0, 0);
 
 	return GTK_COMBO_BOX (combo_box);
@@ -1830,23 +1880,6 @@ append_row (GtkTable *table)
 	return new_row_count - 1;
 }
 
-static GtkWidget *
-append_separator (GtkTable *table)
-{
-	GtkWidget *separator;
-	guint last_row;
-
-	last_row = append_row (table);
-	separator = gtk_hseparator_new ();
-	gtk_widget_show (separator);
-	gtk_table_attach (table, separator,
-			  TITLE_COLUMN, COLUMN_COUNT,
-			  last_row, last_row+1,
-			  GTK_FILL, 0,
-			  0, 0);
-	return separator;				   
-}
-
 static void
 directory_contents_value_field_update (FMPropertiesWindow *window)
 {
@@ -2053,7 +2086,8 @@ append_title_value_pair (FMPropertiesWin
 			 const char *title, 
  			 const char *file_attribute_name,
 			 const char *inconsistent_state,
-			 gboolean show_original)
+			 gboolean show_original,
+			 gboolean update_now)
 {
 	guint last_row;
 
@@ -2061,7 +2095,8 @@ append_title_value_pair (FMPropertiesWin
 	attach_value_field (window, table, last_row, VALUE_COLUMN, 
 			    file_attribute_name,
 			    inconsistent_state,
-			    show_original); 
+			    show_original,
+			    update_now); 
 
 	return last_row;
 }
@@ -2072,7 +2107,8 @@ append_title_and_ellipsizing_value (FMPr
 				    const char *title,
 				    const char *file_attribute_name,
 				    const char *inconsistent_state,
-				    gboolean show_original)
+				    gboolean show_original,
+				    gboolean update_now)
 {
 	guint last_row;
 
@@ -2080,34 +2116,12 @@ append_title_and_ellipsizing_value (FMPr
 	attach_ellipsizing_value_field (window, table, last_row, VALUE_COLUMN, 
 					file_attribute_name,
 					inconsistent_state,
-					show_original);
+					show_original,
+					update_now);
 
 	return last_row;
 }
 
-static void
-update_visibility_of_table_rows (GtkTable *table,
-		   	 	 gboolean should_show,
-		   		 int first_row, 
-		   		 int row_count,
-		   		 GList *widgets)
-{
-	GList *node;
-	int i;
-
-	for (node = widgets; node != NULL; node = node->next) {
-		if (should_show) {
-			gtk_widget_show (GTK_WIDGET (node->data));
-		} else {
-			gtk_widget_hide (GTK_WIDGET (node->data));
-		}
-	}
-
-	for (i= 0; i < row_count; ++i) {
-		gtk_table_set_row_spacing (table, first_row + i, should_show ? GNOME_PAD : 0);
-	}
-}				   
-
 static guint
 append_directory_contents_fields (FMPropertiesWindow *window,
 				  GtkTable *table)
@@ -2235,7 +2249,7 @@ should_show_accessed_date (FMPropertiesW
 	 * day decide that it is useful, we should separately
 	 * consider whether it's useful for "trash:".
 	 */
-	if (file_list_all_directories (window->details->target_files)) {
+	if (get_first_nondirectory_target_file (window) == NULL) {
 		return FALSE;
 	}
 
@@ -2245,7 +2259,7 @@ should_show_accessed_date (FMPropertiesW
 static gboolean
 should_show_mime_type (FMPropertiesWindow *window) 
 {
-	if (file_list_all_directories (window->details->target_files)) {
+	if (get_first_nondirectory_target_file (window) == NULL) {
 		return FALSE;
 	}
 
@@ -2266,8 +2280,8 @@ should_show_link_target (FMPropertiesWin
 static gboolean
 should_show_free_space (FMPropertiesWindow *window)
 {
-	if (file_list_all_local (window->details->target_files)
-	    && file_list_all_directories (window->details->target_files)) {
+	if (file_list_all_local (window->details->target_files) &&
+	    get_first_nondirectory_target_file (window) == NULL) {
 		return TRUE;
 	}
 
@@ -2337,6 +2351,7 @@ create_basic_page (FMPropertiesWindow *w
 					 table, _("Type:"), 
 					 "type",
 					 _("--"),
+					 FALSE,
 					 FALSE);
 	}
 
@@ -2347,23 +2362,27 @@ create_basic_page (FMPropertiesWindow *w
 		append_title_value_pair (window, table, _("Size:"), 
 					 "size_detail",
 					 _("--"),
+					 FALSE,
 					 FALSE);
 	}
 
 	append_title_and_ellipsizing_value (window, table, _("Location:"), 
 					    "where",
 					    _("--"),
-					    TRUE);
+					    TRUE,
+					    FALSE);
 	
 	if (should_show_free_space (window)) {
 		append_title_and_ellipsizing_value (window, table, 
 						    _("Volume:"), 
 						    "volume",
 						    _("--"),
+						    FALSE,
 						    FALSE);
 		append_title_value_pair (window, table, _("Free space:"), 
 					 "free_space",
 					 _("--"),
+					 FALSE,
 					 FALSE);
 	}
 
@@ -2372,12 +2391,14 @@ create_basic_page (FMPropertiesWindow *w
 						    _("Link target:"), 
 						    "link_target",
 						    _("--"),
+						    FALSE,
 						    FALSE);
 	}
 	if (should_show_mime_type (window)) {
 		append_title_value_pair (window, table, _("MIME type:"), 
 					 "mime_type",
 					 _("--"),
+					 FALSE,
 					 FALSE);
 	}				  
 	
@@ -2387,12 +2408,14 @@ create_basic_page (FMPropertiesWindow *w
 	append_title_value_pair (window, table, _("Modified:"), 
 				 "date_modified",
 				 _("--"),
+				 FALSE,
 				 FALSE);
 	
 	if (should_show_accessed_date (window)) {
 		append_title_value_pair (window, table, _("Accessed:"), 
 					 "date_accessed",
 					 _("--"),
+					 FALSE,
 					 FALSE);
 	}
 }
@@ -2491,347 +2514,915 @@ create_emblems_page (FMPropertiesWindow 
 	gtk_widget_show_all (emblems_table);
 }
 
-static void
-permission_change_callback (NautilusFile *file, GnomeVFSResult result, gpointer callback_data)
+static mode_t
+mode_from_permissions (GnomeVFSFilePermissions permissions)
 {
-	FMPropertiesWindow *window;
-	g_assert (callback_data != NULL);
+	mode_t mode = 0;
 
-	window = FM_PROPERTIES_WINDOW (callback_data);
-	if (GTK_WIDGET (window)->window != NULL &&
-	    window->details->long_operation_underway == 1) {
-		/* finished !! */
-		gdk_window_set_cursor (GTK_WIDGET (window)->window, NULL);
+	if (permissions & GNOME_VFS_PERM_SUID) {
+		mode |= S_ISUID;
 	}
-	window->details->long_operation_underway--;
-	
-	/* Report the error if it's an error. */
-	fm_report_error_setting_permissions (file, result, NULL);
-
-	g_object_unref (window);
-}
 
-static void
-get_initial_permission_state (FMPropertiesWindow *window,
-			      GnomeVFSFilePermissions mask,
-			      GList **on,
-			      GList **off)
-{
-	GList *l;
-	
-	*on = NULL;
-	*off = NULL;
-	
-	for (l = window->details->target_files; l != NULL; l = l->next) {
-		GnomeVFSFilePermissions permissions;
-		
-		permissions = GPOINTER_TO_INT (g_hash_table_lookup (window->details->initial_permissions,
-								    l->data));
-					       
-		if ((permissions & mask) != 0) {
-			*on = g_list_prepend (*on, l->data);
-		} else {
-			*off = g_list_prepend (*off, l->data);
-		}
+	if (permissions & GNOME_VFS_PERM_SGID) {
+		mode |= S_ISGID;
 	}
-}
 
-static void
-permission_button_toggled (GtkToggleButton *button, 
-			   FMPropertiesWindow *window)
-{
-	GList *l;
-	GnomeVFSFilePermissions permission_mask;
-	GList *files_on;
-	GList *files_off;
-	
-	permission_mask = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button),
-							      "permission"));
+#ifdef S_ISVTX
+	if (permissions & GNOME_VFS_PERM_STICKY) {
+		mode |= S_ISVTX;
+	}
+#endif
 
-	files_on = NULL;
-	files_off = NULL;
-	if (gtk_toggle_button_get_active (button)
-	    && !gtk_toggle_button_get_inconsistent (button)) {
-		/* Go to the initial state unless the initial state was 
-		   consistent */
-		get_initial_permission_state (window, permission_mask,
-					      &files_on, &files_off);
-		
-		if (!(files_on && files_off)) {
-			g_list_free (files_on);
-			g_list_free (files_off);
-			files_on = g_list_copy (window->details->target_files);
-			files_off = NULL;
-		}
-	} else if (gtk_toggle_button_get_inconsistent (button)
-		   && !gtk_toggle_button_get_active (button)) {
-		files_on = g_list_copy (window->details->target_files);
-		files_off = NULL;
-	} else {
-		files_off = g_list_copy (window->details->target_files);
-		files_on = NULL;
+	if (permissions & GNOME_VFS_PERM_USER_READ) {
+		mode |= S_IRUSR;
 	}
-	
-	g_signal_handlers_block_by_func (G_OBJECT (button), 
-					 G_CALLBACK (permission_button_toggled),
-					 window);
-	
-	gtk_toggle_button_set_active (button, files_on != NULL);
-	gtk_toggle_button_set_inconsistent (button, files_on && files_off);
 
-	g_signal_handlers_unblock_by_func (G_OBJECT (button), 
-					   G_CALLBACK (permission_button_toggled),
-					   window);
+	if (permissions & GNOME_VFS_PERM_USER_WRITE) {
+		mode |= S_IWUSR;
+	}
 
-	if (window->details->long_operation_underway == 0) {
-		/* start long operation */
-		GdkCursor * cursor;
-	       
-		cursor = gdk_cursor_new (GDK_WATCH);
-		gdk_window_set_cursor (GTK_WIDGET (window)->window, cursor);
-		gdk_cursor_unref (cursor);
+	if (permissions & GNOME_VFS_PERM_USER_EXEC) {
+		mode |= S_IXUSR;
 	}
-	window->details->long_operation_underway += g_list_length (files_on);
-	window->details->long_operation_underway += g_list_length (files_off);
 
-	for (l = files_on; l != NULL; l = l->next) {
-		NautilusFile *file;
-		
-		file = NAUTILUS_FILE (l->data);
+	if (permissions & GNOME_VFS_PERM_GROUP_READ) {
+		mode |= S_IRGRP;
+	}
 
-		if (nautilus_file_can_set_permissions (file)) {
-			GnomeVFSFilePermissions permissions;
+	if (permissions & GNOME_VFS_PERM_GROUP_WRITE) {
+		mode |= S_IWGRP;
+	}
 
-			permissions = nautilus_file_get_permissions (file);
-			permissions |= permission_mask;
-			
-			g_object_ref (window);
-			nautilus_file_set_permissions
-				(file, permissions,
-				 permission_change_callback,
-				 window);
-		}
-		
+	if (permissions & GNOME_VFS_PERM_GROUP_EXEC) {
+		mode |= S_IXGRP;
 	}
-	
-	for (l = files_off; l != NULL; l = l->next) {
-		NautilusFile *file;
-		
-		file = NAUTILUS_FILE (l->data);
 
-		if (nautilus_file_can_set_permissions (file)) {
-			GnomeVFSFilePermissions permissions;
+	if (permissions & GNOME_VFS_PERM_OTHER_READ) {
+		mode |= S_IROTH;
+	}
 
-			permissions = nautilus_file_get_permissions (file);
-			permissions &= ~permission_mask;
+	if (permissions & GNOME_VFS_PERM_OTHER_WRITE) {
+		mode |= S_IWOTH;
+	}
 
-			g_object_ref (window);
-			nautilus_file_set_permissions
-				(file, permissions,
-				 permission_change_callback,
-				 window);
-		}
-	}	
+	if (permissions & GNOME_VFS_PERM_OTHER_EXEC) {
+		mode |= S_IXOTH;
+	}
 
-	g_list_free (files_on);
-	g_list_free (files_off);
+	return mode;
 }
 
-static void
-permission_button_update (FMPropertiesWindow *window,
-			  GtkToggleButton *button)
+static GnomeVFSFilePermissions
+permissions_from_mode (mode_t mode)
 {
-	GList *l;
-	gboolean all_set;
-	gboolean all_unset;
-	gboolean all_cannot_set;
-	GnomeVFSFilePermissions button_permission;
-	
-	button_permission = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button),
-								"permission"));
-	
-	all_set = TRUE;
-	all_unset = TRUE;
-	all_cannot_set = TRUE;
-	for (l = window->details->target_files; l != NULL; l = l->next) {
-		NautilusFile *file;
-		GnomeVFSFilePermissions file_permissions;
+	GnomeVFSFilePermissions permissions = 0;
 
-		file = NAUTILUS_FILE (l->data);
+	if (mode & S_ISUID) {
+		permissions |= GNOME_VFS_PERM_SUID;
+	}
 
-		if (!nautilus_file_can_get_permissions (file)) {
-			continue;
-		}
+	if (mode & S_ISGID) {
+		permissions |= GNOME_VFS_PERM_SGID;
+	}
 
-		file_permissions = nautilus_file_get_permissions (file);
+#ifdef S_ISVTX
+	if (mode & S_ISVTX) {
+		permissions = GNOME_VFS_PERM_STICKY;
+	}
+#endif
 
-		if ((file_permissions & button_permission) != 0) {
-			all_unset = FALSE;
-		} else {
-			all_set = FALSE;
-		}
+	if (mode & S_IRUSR) {
+		permissions |= GNOME_VFS_PERM_USER_READ;
+	}
 
-		if (nautilus_file_can_set_permissions (file)) {
-			all_cannot_set = FALSE;
-		}
+	if (mode & S_IWUSR) {
+		permissions |= GNOME_VFS_PERM_USER_WRITE;
 	}
-	
-	g_signal_handlers_block_by_func (G_OBJECT (button), 
-					 G_CALLBACK (permission_button_toggled),
-					 window);
 
-	gtk_toggle_button_set_active (button, !all_unset);
-	gtk_toggle_button_set_inconsistent (button, !all_unset && !all_set);
-	gtk_widget_set_sensitive (GTK_WIDGET (button), !all_cannot_set);
+	if (mode & S_IXUSR) {
+		permissions |= GNOME_VFS_PERM_USER_EXEC;
+	}
 
-	g_signal_handlers_unblock_by_func (G_OBJECT (button), 
-					   G_CALLBACK (permission_button_toggled),
-					   window);
-}
+	if (mode & S_IRGRP) {
+		permissions |= GNOME_VFS_PERM_GROUP_READ;
+	}
 
+	if (mode & S_IWGRP) {
+		permissions |= GNOME_VFS_PERM_GROUP_WRITE;
+	}
 
-static void
-set_up_permissions_checkbox (FMPropertiesWindow *window,
-			     GtkWidget *check_button, 
-			     GnomeVFSFilePermissions permission)
-{
-	/* Load up the check_button with data we'll need when updating its state. */
-        g_object_set_data (G_OBJECT (check_button), "permission", 
-			   GINT_TO_POINTER (permission));
-        g_object_set_data (G_OBJECT (check_button), "properties_window", 
-			   window);
-	
-	window->details->permission_buttons = 
-		g_list_prepend (window->details->permission_buttons,
-				check_button);
-
-	g_signal_connect_object (check_button, "toggled",
-				 G_CALLBACK (permission_button_toggled),
-				 window,
-				 0);
-}
+	if (mode & S_IXGRP) {
+		permissions |= GNOME_VFS_PERM_GROUP_EXEC;
+	}
 
-static void
-add_permissions_checkbox (FMPropertiesWindow *window,
-			  GtkTable *table, 
-			  int row, int column, 
-			  GnomeVFSFilePermissions permission_to_check,
-			  GtkLabel *label_for)
-{
-	GtkWidget *check_button;
-	gchar *label;
-	gboolean a11y_enabled;
-
-	if (column == PERMISSIONS_CHECKBOXES_READ_COLUMN) {
-		label = _("_Read");
-	} else if (column == PERMISSIONS_CHECKBOXES_WRITE_COLUMN) {
-		label = _("_Write");
-	} else {
-		label = _("E_xecute");
+	if (mode & S_IROTH) {
+		permissions |= GNOME_VFS_PERM_OTHER_READ;
 	}
 
-	check_button = gtk_check_button_new_with_mnemonic (label);
-	gtk_widget_show (check_button);
-	gtk_table_attach (table, check_button,
-			  column, column + 1,
-			  row, row + 1,
-			  GTK_FILL, 0,
-			  0, 0);
+	if (mode & S_IWOTH) {
+		permissions |= GNOME_VFS_PERM_OTHER_WRITE;
+	}
 
-	set_up_permissions_checkbox (window, 
-				     check_button, 
-				     permission_to_check);
-
-	a11y_enabled = GTK_IS_ACCESSIBLE (gtk_widget_get_accessible (check_button));
-	if (a11y_enabled) {
-		eel_accessibility_set_up_label_widget_relation (GTK_WIDGET (label_for),
-								check_button);
+	if (mode & S_IXOTH) {
+		permissions |= GNOME_VFS_PERM_OTHER_EXEC;
 	}
+
+	return permissions;
 }
 
-static GtkWidget *
-append_special_execution_checkbox (FMPropertiesWindow *window,
-				   GtkTable *table,
-				   const char *label_text,
-				   GnomeVFSFilePermissions permission_to_check)
+static void
+directory_contents_ready (NautilusDirectory *directory,
+			  GList *files,
+			  PermissionsChangeRequest *request)
 {
-	GtkWidget *check_button;
-	guint last_row;
+	FMPropertiesWindow *window;
+	NautilusFile *file;
+	GList *l;
+ 
+	g_assert (FM_IS_PROPERTIES_WINDOW (request->window));
 
-	last_row = append_row (table);
+	window = request->window;
 
-	check_button = gtk_check_button_new_with_mnemonic (label_text);
-	gtk_widget_show (check_button);
+	for (l = files; l != NULL; l = l->next) {
+		file = NAUTILUS_FILE (l->data);
 
-	gtk_table_attach (table, check_button,
-			  VALUE_COLUMN, VALUE_COLUMN + 1,
-			  last_row, last_row + 1,
-			  GTK_FILL, 0,
-			  0, 0);
+		request->ref++;
+		window->details->long_operation_underway++;
 
-	set_up_permissions_checkbox (window, 
-				     check_button, 
-				     permission_to_check);
-	++window->details->num_special_flags_rows;
+		if (nautilus_file_can_set_permissions (file)) {
+			GnomeVFSFilePermissions permissions;
 
-	return check_button;
-}
+			permissions = nautilus_file_get_permissions (file);
+
+			/* FIXME we might be able to save I/O if we
+			 * directly call permission_change_callback
+			 * for files that weren't modified due to
+			 * our request */
+
+			if (request->chmod_mode_change != NULL) {
+				mode_t mode;
+
+				mode = mode_from_permissions (permissions);
+				if (nautilus_file_is_directory (file)) {
+					mode |= S_IFDIR;
+				}
+
+				mode = mode_adjust (mode, request->chmod_mode_change, umask (0));
+
+				permissions = permissions_from_mode (mode);
+			} else {
+				if (nautilus_file_is_directory (file)) {
+					permissions |= request->folder_on;
+					permissions &= ~request->folder_off;
+				} else {
+					permissions |= request->file_on;
+					permissions &= ~request->file_off;
+				}
+			}
+
+			nautilus_file_set_permissions
+				(file, permissions,
+				 (NautilusFileOperationCallback) permission_change_callback,
+				 request);
+		}
+	}
+
+
+	nautilus_directory_file_monitor_remove (directory, window);
+	nautilus_directory_unref (directory);
+
+	if (GTK_WIDGET (window)->window != NULL &&
+	    window->details->long_operation_underway == 1) {
+		/* finished !! */
+		gdk_window_set_cursor (GTK_WIDGET (window)->window, NULL);
+	}
+
+	window->details->long_operation_underway--;
+
+	if (--request->ref == 0) {
+		g_object_unref (window);
+		g_free (request);
+	}
+}
+
+static void
+directory_done_loading (NautilusDirectory *directory,
+			PermissionsChangeRequest *request)
+{
+	g_assert (NAUTILUS_IS_DIRECTORY (directory));
+	g_assert (FM_IS_PROPERTIES_WINDOW (request->window));
+
+	nautilus_directory_call_when_ready (directory,
+					    nautilus_mime_actions_get_minimum_file_attributes (),
+					    TRUE,
+					    (NautilusDirectoryCallback) directory_contents_ready,
+					    request);
+}
+
+static void
+permission_change_callback (NautilusFile *file,
+			    GnomeVFSResult result,
+			    PermissionsChangeRequest *request)
+{
+	NautilusDirectory *directory;
+
+	g_assert (NAUTILUS_IS_FILE (file));
+	g_assert (FM_IS_PROPERTIES_WINDOW (request->window));
+
+	if (request->is_recursive &&
+	    nautilus_file_is_directory (file)) {
+		/* will be dec. in directory_contents_ready */
+		request->ref++;
+		request->window->details->long_operation_underway++;
+
+		directory = nautilus_directory_get_for_file (file);
+		if (nautilus_directory_are_all_files_seen (directory)) {
+			directory_done_loading (directory, request);
+		} else {
+			g_signal_connect (directory, "done-loading",
+					  G_CALLBACK (directory_done_loading),
+					  request);
+			nautilus_directory_file_monitor_add (directory, request->window,
+							     TRUE, TRUE,
+							     NAUTILUS_FILE_ATTRIBUTE_CAPABILITIES |
+							     NAUTILUS_FILE_ATTRIBUTE_IS_DIRECTORY,
+							     NULL, NULL);
+		}
+	}
+
+	if (GTK_WIDGET (request->window)->window != NULL &&
+	    request->window->details->long_operation_underway == 1) {
+		/* finished !! */
+		gdk_window_set_cursor (GTK_WIDGET (request->window)->window, NULL);
+	}
+	request->window->details->long_operation_underway--;
 
+ 	/* Report the error if it's an error. */
+ 	fm_report_error_setting_permissions (file, result, NULL);
+ 
+	if (--request->ref == 0) {
+		g_object_unref (request->window);
+		g_free (request->chmod_mode_change);
+		g_free (request);
+	}
+}
+ 
 static void
-remember_special_flags_widget (FMPropertiesWindow *window, GtkWidget *widget)
+apply_permission_changes (FMPropertiesWindow *window,
+			  GnomeVFSFilePermissions file_on,
+			  GnomeVFSFilePermissions file_off,
+			  GnomeVFSFilePermissions folder_on,
+			  GnomeVFSFilePermissions folder_off,
+			  struct mode_change *chmod_mode_change,
+			  gboolean is_recursive)
 {
+	PermissionsChangeRequest *request;
+	GList *l;
+
 	g_assert (FM_IS_PROPERTIES_WINDOW (window));
-	g_assert (GTK_IS_WIDGET (widget));
-	
-	window->details->special_flags_widgets = 
-		g_list_prepend (window->details->special_flags_widgets, widget);
+
+	request = g_new0 (PermissionsChangeRequest, 1);
+	request->window = g_object_ref (window);
+	request->ref = 1;
+
+	request->file_on = file_on;
+	request->file_off = file_off;
+
+	request->folder_on = folder_on;
+	request->folder_off = folder_off;
+
+	request->chmod_mode_change = chmod_mode_change;
+
+	request->is_recursive = is_recursive;
+
+	if (window->details->long_operation_underway == 0) {
+		/* start long operation */
+		GdkCursor *cursor;
+
+		cursor = gdk_cursor_new (GDK_WATCH);
+		gdk_window_set_cursor (GTK_WIDGET (window)->window, cursor);
+		gdk_cursor_unref (cursor);
+	}
+
+	for (l = window->details->target_files; l != NULL; l = l->next) {
+		NautilusFile *file;
+
+		file = NAUTILUS_FILE (l->data);
+
+		if (nautilus_file_can_set_permissions (file)) {
+			GnomeVFSFilePermissions permissions;
+
+			request->ref++;
+			window->details->long_operation_underway++;
+
+			permissions = nautilus_file_get_permissions (file);
+
+			if (request->chmod_mode_change != NULL) {
+				mode_t mode;
+
+				mode = mode_from_permissions (permissions);
+				if (nautilus_file_is_directory (file)) {
+					mode |= S_IFDIR;
+				}
+
+				mode = mode_adjust (mode, request->chmod_mode_change, umask (0));
+
+				permissions = permissions_from_mode (mode);
+			} else {
+				if (nautilus_file_is_directory (file)) {
+					permissions |= request->folder_on;
+					permissions &= ~request->folder_off;
+				} else {
+					permissions |= request->file_on;
+					permissions &= ~request->file_off;
+				}
+			}
+
+			nautilus_file_set_permissions
+				(file, permissions,
+				 (NautilusFileOperationCallback) permission_change_callback,
+				 request);
+		}
+	}
+
+	if (--request->ref == 0) {
+		g_object_unref (window);
+		g_free (request->chmod_mode_change);
+		g_free (request);
+	}
 }
 
+typedef enum {
+	SIMPLE_PERMISSION_MODE_NONE = 0,
+	SIMPLE_PERMISSION_MODE_READ,
+	SIMPLE_PERMISSION_MODE_READ_WRITE,
+} SimplePermissionMode;
+
+typedef enum {
+	SIMPLE_PERMISSION_SCOPE_USER = 0,
+	SIMPLE_PERMISSION_SCOPE_GROUP,
+	SIMPLE_PERMISSION_SCOPE_OTHER
+} SimplePermissionScope;
+
 static void
-update_visibility_of_special_flags_widgets (FMPropertiesWindow *window)
+simple_permission_scope_to_vfs (SimplePermissionScope scope,
+				GnomeVFSFilePermissions *rp,
+				GnomeVFSFilePermissions *wp,
+				GnomeVFSFilePermissions *xp)
 {
-	update_visibility_of_table_rows 
-		(window->details->permissions_table,
-		 eel_preferences_get_boolean (NAUTILUS_PREFERENCES_SHOW_SPECIAL_FLAGS),
-		 window->details->first_special_flags_row,
-		 window->details->num_special_flags_rows,
-		 window->details->special_flags_widgets);
+	GnomeVFSFilePermissions r, w, x;
+
+	switch (scope) {
+	case SIMPLE_PERMISSION_SCOPE_USER:
+		r = GNOME_VFS_PERM_USER_READ;
+		w = GNOME_VFS_PERM_USER_WRITE;
+		x = GNOME_VFS_PERM_USER_EXEC;
+		break;
+
+	case SIMPLE_PERMISSION_SCOPE_GROUP:
+		r = GNOME_VFS_PERM_GROUP_READ;
+		w = GNOME_VFS_PERM_GROUP_WRITE;
+		x = GNOME_VFS_PERM_GROUP_EXEC;
+		break;
+
+	case SIMPLE_PERMISSION_SCOPE_OTHER:
+		r = GNOME_VFS_PERM_OTHER_READ;
+		w = GNOME_VFS_PERM_OTHER_WRITE;
+		x = GNOME_VFS_PERM_OTHER_EXEC;
+		break;
+
+	default:
+		g_assert_not_reached ();
+	}
+
+	if (rp != NULL) {
+		*rp = r;
+	}
+
+	if (wp != NULL) {
+		*wp = w;
+	}
+
+	if (xp != NULL) {
+		*xp = x;
+	}
+}
+
+static int
+file_permissions_to_combo_box_index (SimplePermissionScope scope,
+				     NautilusFile *file)
+{
+	GnomeVFSFilePermissions permissions, r, w, x;
+	int index;
+
+	g_assert (NAUTILUS_IS_FILE (file));
+
+	simple_permission_scope_to_vfs (scope, &r, &w, &x);
+
+	permissions = nautilus_file_get_permissions (file);
+
+	index = -1;
+
+	if ((permissions & (r | w | x)) == 0) {
+		index = SIMPLE_PERMISSION_MODE_NONE;
+	} else if (nautilus_file_is_directory (file)) {
+		if ((permissions & (r | w | x)) == (r | x)) {
+			index = SIMPLE_PERMISSION_MODE_READ;
+		} else if ((permissions & (r | w | x)) == (r | w | x)) {
+			index = SIMPLE_PERMISSION_MODE_READ_WRITE;
+		}
+	} else {
+		if ((permissions & (r | w)) == r) {
+			index = SIMPLE_PERMISSION_MODE_READ;
+		} else if ((permissions & (r | w)) == (r | w)) {
+			index = SIMPLE_PERMISSION_MODE_READ_WRITE;
+		}
+	}
+
+	if (scope == SIMPLE_PERMISSION_SCOPE_USER && index >= 0) {
+		index--;
+	}
+
+	return index;
+}
+
+
+static int
+file_list_permissions_to_combo_box_index (SimplePermissionScope scope,
+					  GList *files)
+{
+	GList *l;
+	NautilusFile *file;
+	int i, this_i;
+
+	i = -1;
+
+	for (l = files; l != NULL; l = l->next) {
+		file = NAUTILUS_FILE (l->data);
+
+		this_i = file_permissions_to_combo_box_index (scope, file);
+
+		if (this_i < 0 || (i >= 0 && this_i != i)) {
+			i = -1;
+			break;
+		}
+
+		i = this_i;
+	}
+
+	return i;
 }
 
 static void
-update_visibility_of_special_flags_widgets_wrapper (gpointer callback_data)
+get_combo_permissions (GtkComboBox *combo,
+		       GnomeVFSFilePermissions *file_on_permissions,
+		       GnomeVFSFilePermissions *file_off_permissions,
+		       GnomeVFSFilePermissions *folder_on_permissions,
+		       GnomeVFSFilePermissions *folder_off_permissions)
 {
-	update_visibility_of_special_flags_widgets (FM_PROPERTIES_WINDOW (callback_data));
+	SimplePermissionScope scope;
+	GnomeVFSFilePermissions r, w, x, all;
+	gboolean is_folder;
+	int index;
+
+	scope = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (combo), "scope"));
+
+	is_folder = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (combo), "is_folder"));
+
+	*file_on_permissions = *file_off_permissions = 0;
+	*folder_on_permissions = *folder_off_permissions = 0;
+
+	r = w = x = 0;
+
+	simple_permission_scope_to_vfs (scope, &r, &w, &x);
+
+	index = gtk_combo_box_get_active (combo);
+	if (scope == SIMPLE_PERMISSION_SCOPE_USER && index >= 0) {
+		index++;
+	}
+
+	switch (index) {
+	case SIMPLE_PERMISSION_MODE_NONE:
+		*file_on_permissions = 0;
+		*folder_on_permissions = 0;
+		break;
+
+	case SIMPLE_PERMISSION_MODE_READ:
+		*file_on_permissions = r;
+		*folder_on_permissions = *file_on_permissions | x;
+		break;
+
+	case SIMPLE_PERMISSION_MODE_READ_WRITE:
+		*file_on_permissions = r | w;
+		*folder_on_permissions = *file_on_permissions | x;
+		break;
+
+	default:
+		g_assert_not_reached ();
+	}
+
+	all = r | w | x;
+
+	*folder_off_permissions = all & ~*folder_on_permissions;
+	*file_off_permissions = all & ~*file_on_permissions;
+	/* Don't kill x bit of executables, except if the user decides to
+	 * set the permissions to "none" */
+	if (*file_off_permissions != all) {
+		*file_off_permissions &= ~x;
+	}
 }
 
 static void
-append_special_execution_flags (FMPropertiesWindow *window, GtkTable *table)
+permission_combo_box_changed (GtkComboBox *combo,
+			      FMPropertiesWindow *window)
 {
-	
+	GnomeVFSFilePermissions file_on;
+	GnomeVFSFilePermissions file_off;
+	GnomeVFSFilePermissions folder_on;
+	GnomeVFSFilePermissions folder_off;
+	gboolean recursive;
+	int num_folders;
 
-	remember_special_flags_widget (window, append_special_execution_checkbox 
-		(window, table, _("Set _user ID"), GNOME_VFS_PERM_SUID));
+	g_assert (FM_IS_PROPERTIES_WINDOW (window));
 
-	window->details->first_special_flags_row = table->nrows - 1;
+	recursive = FALSE;
 
-	remember_special_flags_widget (window, GTK_WIDGET (attach_title_field 
-		(table, table->nrows - 1, _("Special flags:"))));
+	num_folders = file_list_count_directories (window->details->target_files);
+	if (num_folders > 0) {
+		int res;
+
+		res = eel_run_simple_dialog
+			(GTK_WIDGET (window), FALSE,
+			 GTK_MESSAGE_QUESTION,
+			 /* translators: the plural here will be "Folder" */
+			 ngettext ("Apply Changes to Folder Contents?",
+				   "Apply Changes to Folder Contents?",
+				   num_folders),
+			 ngettext ("You are about to change the access rights to a folder. "
+				   "You can apply these changes to the folder itself only, or to its whole contents. "
+				   "Note that this action can not be reverted, so you should double-check whether you "
+				   "selected the appropriate permissions.",
+				   "You are about to change the access rights to folders. "
+				   "You can apply these changes to the folders themselves only, or to their whole contents. "
+				   "Note that this action can not be reverted, so you should double-check whether you "
+				   "selected the appropriate permissions.",
+				   num_folders),
+			 ngettext ("A_pply to Folder Only",
+				   "A_pply to Folders Only",
+				   num_folders),
+			 ngettext ("_Apply to Folder and Contents",
+				   "_Apply to Folders and Contents",
+				   num_folders),
+			 NULL);
+
+		switch (res) {
+			case 0:
+				recursive = FALSE;
+				break;
 
-	remember_special_flags_widget (window, append_special_execution_checkbox 
-		(window, table, _("Set gro_up ID"), GNOME_VFS_PERM_SGID));
-	remember_special_flags_widget (window, append_special_execution_checkbox 
-		(window, table, _("_Sticky"), GNOME_VFS_PERM_STICKY));
+			case 1:
+				recursive = TRUE;
+				break;
 
-	remember_special_flags_widget (window, append_separator (table));
-	++window->details->num_special_flags_rows;
+			default:
+				permission_combo_box_update (window, combo);
+				return;
+		}
+	}
 
-	update_visibility_of_special_flags_widgets (window);
-	eel_preferences_add_callback_while_alive 
-		(NAUTILUS_PREFERENCES_SHOW_SPECIAL_FLAGS,
-		 update_visibility_of_special_flags_widgets_wrapper,
-		 window,
-		 G_OBJECT (window));
-	
+	/* combo boxes are instant apply */
+	get_combo_permissions (combo,
+			       &file_on, &file_off,
+			       &folder_on, &folder_off);
+	apply_permission_changes (window,
+				  file_on, file_off,
+				  folder_on, folder_off,
+				  NULL,
+				  recursive);
+}
+
+static void
+permission_combo_box_destroy (GtkComboBox *combo,
+			      FMPropertiesWindow *window)
+{
+	g_assert (FM_IS_PROPERTIES_WINDOW (window));
+
+	window->details->permission_combo_boxes =
+		g_list_remove (window->details->permission_combo_boxes, combo);
+}
+
+static GtkComboBox *
+attach_permission_combo_box (FMPropertiesWindow *window,
+			     GtkTable *table,
+			     SimplePermissionScope scope,
+			     unsigned int row,
+			     GtkLabel *label)
+{
+	GtkComboBox *combo;
+
+	combo = attach_combo_box (table, row);
+	g_object_set_data (G_OBJECT (combo), "scope", GUINT_TO_POINTER (scope));
+
+	window->details->permission_combo_boxes = g_list_prepend
+		(window->details->permission_combo_boxes, combo);
+
+	g_signal_connect (combo, "changed",
+			  G_CALLBACK (permission_combo_box_changed),
+			  window);
+	g_signal_connect (combo, "destroy",
+			  G_CALLBACK (permission_combo_box_destroy),
+			  window);
+
+	gtk_label_set_mnemonic_widget (label, GTK_WIDGET (combo));
+
+	return combo;
+}
+
+static void
+append_permission_combo_box (FMPropertiesWindow *window,
+			     GtkTable *table,
+			     SimplePermissionScope scope,
+			     const char *title)
+{
+	GtkLabel *label;
+	unsigned int row;
+
+	row = append_title_field (table, title, &label);
+	attach_permission_combo_box (window, table, scope, row, label);
+}
+
+static void
+attach_executable_button (FMPropertiesWindow *window,
+			  GtkTable *table,
+			  SimplePermissionScope scope,
+			  unsigned int row)
+{
+	GtkWidget *button;
+
+	button = gtk_check_button_new_with_label ("");
+	gtk_widget_show (button);
+	gtk_table_attach (table, button,
+			  VALUE_COLUMN, VALUE_COLUMN + 1,
+			  row, row + 1,
+			  GTK_FILL, 0,
+			  0, 0);
+	g_signal_connect (button, "toggled",
+			  G_CALLBACK (executable_button_toggled),
+			  window);
+	g_object_set_data (G_OBJECT (button), "row", GUINT_TO_POINTER (row));
+	g_object_set_data (G_OBJECT (button), "scope", GUINT_TO_POINTER (scope));
+
+	window->details->executable_buttons = g_list_prepend
+		(window->details->executable_buttons, button);
+}
+
+static void
+append_executable_button (FMPropertiesWindow *window,
+			  GtkTable *table,
+			  SimplePermissionScope scope)
+{
+	unsigned int row;
+
+	row = append_row (table);
+	attach_executable_button (window, table, scope, row);
+}
+
+/* returns whether the chmod entry is visible and contains potentially valid text */
+static gboolean
+is_in_chmod_mode (FMPropertiesWindow *window)
+{
+	const char *text;
+	gboolean ret;
+
+	g_assert (FM_IS_PROPERTIES_WINDOW (window));
+	g_assert (GTK_IS_EXPANDER (window->details->details_expander));
+
+	ret = gtk_expander_get_expanded (GTK_EXPANDER (window->details->details_expander));
+
+	if (ret) {
+		g_assert (GTK_IS_ENTRY (window->details->chmod_entry));
+		text = gtk_entry_get_text (GTK_ENTRY (window->details->chmod_entry));
+		ret = text != NULL && strlen (text) != 0;
+	}
+
+	return ret;
+}
+
+static void
+permission_combo_box_update_sensitivity (FMPropertiesWindow *window,
+					 GtkComboBox *combo)
+{
+	g_assert (FM_IS_PROPERTIES_WINDOW (window));
+
+	gtk_widget_set_sensitive (GTK_WIDGET (combo), !is_in_chmod_mode (window));
+}
+
+static void
+permission_combo_box_update_sensitivity_all (FMPropertiesWindow *window)
+{
+	GList *l;
+
+	g_assert (FM_IS_PROPERTIES_WINDOW (window));
+
+	for (l = window->details->permission_combo_boxes; l != NULL; l = l->next) {
+		permission_combo_box_update_sensitivity (window, GTK_COMBO_BOX (l->data));
+	}
+}
+
+static void
+permission_combo_box_update (FMPropertiesWindow *window,
+			     GtkComboBox *combo)
+{
+	GtkTreeModel *model;
+	SimplePermissionScope scope;
+	unsigned int n_cols;
+	int i;
+
+	g_assert (FM_IS_PROPERTIES_WINDOW (window));
+
+	model = gtk_combo_box_get_model (combo);
+
+	g_signal_handlers_block_by_func (G_OBJECT (combo),
+					 G_CALLBACK (permission_combo_box_changed),
+					 window);
+
+	scope = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (combo), "scope"));
+
+	n_cols = gtk_tree_model_iter_n_children (model, NULL);
+
+	for (i = n_cols - 1; i >= 0; i--) {
+		gtk_combo_box_remove_text (combo, i);
+	}
+
+	if (scope != SIMPLE_PERMISSION_SCOPE_USER) {
+		/* translators: DON'T include "Access|" in your translation */
+		gtk_combo_box_append_text (combo, _(Q_("Access|None")));
+	}
+
+	gtk_combo_box_append_text (combo, _("Read"));
+	gtk_combo_box_append_text (combo, _("Read and Write"));
+
+	i = file_list_permissions_to_combo_box_index (scope, window->details->target_files);
+
+	gtk_combo_box_set_active (combo, i);
+
+	g_signal_handlers_unblock_by_func (G_OBJECT (combo),
+					   G_CALLBACK (permission_combo_box_changed),
+					   window);
+
+	permission_combo_box_update_sensitivity (window, combo);
+}
+
+static void
+executable_button_toggled (GtkToggleButton *button,
+			   FMPropertiesWindow *window)
+{
+	SimplePermissionScope scope;
+	GnomeVFSFilePermissions x;
+	gboolean toggled;
+
+	g_assert (FM_IS_PROPERTIES_WINDOW (window));
+
+	toggled = gtk_toggle_button_get_active (button);
+
+	scope = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (button), "scope"));
+	simple_permission_scope_to_vfs (scope, NULL, NULL, &x);
+
+	apply_permission_changes (window,
+				  toggled ? x : 0,
+				  toggled ? 0 : x,
+				  0, 0,
+				  NULL,
+				  FALSE);
+}
+
+static void
+executable_button_update_sensitivity (FMPropertiesWindow *window,
+				      GtkCheckButton     *button)
+{
+	g_assert (FM_IS_PROPERTIES_WINDOW (window));
+	g_assert (GTK_IS_CHECK_BUTTON (button));
+
+	gtk_widget_set_sensitive (GTK_WIDGET (button), !is_in_chmod_mode (window));
+}
+
+static void
+executable_button_update_sensitivity_all (FMPropertiesWindow *window)
+{
+	GList *l;
+
+	g_assert (FM_IS_PROPERTIES_WINDOW (window));
+
+	for (l = window->details->executable_buttons; l != NULL; l = l->next) {
+		executable_button_update_sensitivity (window, GTK_CHECK_BUTTON (l->data));
+	}
+}
+
+static void
+executable_button_update (FMPropertiesWindow *window,
+			  GtkCheckButton     *button)
+{
+	NautilusFile *file;
+	GList *l;
+	gboolean one_executable;
+	gboolean all_executable;
+	gboolean inconsistent;
+	gboolean active;
+	SimplePermissionScope scope;
+	GnomeVFSFilePermissions x;
+	unsigned int row, n_files;
+	const char *text;
+
+	g_assert (FM_IS_PROPERTIES_WINDOW (window));
+	g_assert (GTK_IS_CHECK_BUTTON (button));
+
+	g_signal_handlers_block_by_func (button,
+					 G_CALLBACK (executable_button_toggled),
+					 window);
+
+	file = get_first_nondirectory_target_file (window);
+
+	row = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (button), "row"));
+
+	if (file == NULL) {
+		gtk_widget_hide (GTK_WIDGET (button));
+		gtk_table_set_row_spacing (window->details->permissions_table, row, 0);
+	} else {
+		one_executable = FALSE;
+		all_executable = TRUE;
+
+		scope = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (button), "scope"));
+		simple_permission_scope_to_vfs (scope, NULL, NULL, &x);
+
+		for (l = window->details->target_files; l != NULL; l = l->next) {
+			file = NAUTILUS_FILE (l->data);
+
+			if (nautilus_file_is_directory (file)) {
+				continue;
+			}
+
+			if (nautilus_file_can_get_permissions (file) &&
+			    (nautilus_file_get_permissions (file) & x) != 0) {
+				one_executable = TRUE;
+			} else {
+				all_executable = FALSE;
+			}
+
+			if (one_executable && !all_executable) {
+				break;
+			}
+		}
+
+		/* might have fallen through */
+		if (!one_executable) {
+			all_executable = FALSE;
+		}
+
+		inconsistent = one_executable && !all_executable;
+		active = all_executable;;
+
+		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
+		gtk_toggle_button_set_inconsistent (GTK_TOGGLE_BUTTON (button), inconsistent);
+
+		text = NULL;
+
+		n_files = g_list_length (window->details->target_files)
+			  - file_list_count_directories (window->details->target_files);
+
+		switch (scope) {
+			case SIMPLE_PERMISSION_SCOPE_USER:
+				text = ngettext ("Ma_y run file as application",
+						 "Ma_y run files as application",
+						 n_files);
+				break;
+
+			case SIMPLE_PERMISSION_SCOPE_GROUP:
+				text = ngettext ("May _run file as application",
+						 "May _run files as application",
+						 n_files);
+				break;
+
+			case SIMPLE_PERMISSION_SCOPE_OTHER:
+				text = ngettext ("May ru_n file as application",
+						 "May ru_n files as application",
+						 n_files);
+				break;
+
+			default:
+				g_assert_not_reached ();
+		}
+
+		gtk_label_set_text_with_mnemonic (GTK_LABEL (GTK_BIN (button)->child), text);
+
+		gtk_widget_show (GTK_WIDGET (button));
+		gtk_table_set_row_spacing (window->details->permissions_table, row, GNOME_PAD);
+
+		executable_button_update_sensitivity (window, button);
+	}
+
+	g_signal_handlers_unblock_by_func (button,
+					   G_CALLBACK (executable_button_toggled),
+					   window);
 }
 
 static gboolean
@@ -2868,218 +3459,302 @@ all_can_set_permissions (GList *file_lis
 	return TRUE;
 }
 
-static GHashTable *
-get_initial_permissions (GList *file_list)
+static void
+chmod_entry_activated (GtkEntry *chmod_entry,
+		       FMPropertiesWindow *window)
 {
-	GHashTable *ret;
-	GList *l;
+	struct mode_change *change;
+	const char *text;
+	gboolean recursive;
+	int res;
 
-	ret = g_hash_table_new (g_direct_hash,
-				g_direct_equal);
-	
-	for (l = file_list; l != NULL; l = l->next) {
-		GnomeVFSFilePermissions permissions;
-		NautilusFile *file;
-		
-		file = NAUTILUS_FILE (l->data);
-		
-		permissions = nautilus_file_get_permissions (file);
-		g_hash_table_insert (ret, file,
-				     GINT_TO_POINTER (permissions));
+	g_assert (FM_IS_PROPERTIES_WINDOW (window));
+
+	change = NULL;
+
+	text = gtk_entry_get_text (chmod_entry);
+	if (text != NULL) {
+		change = mode_compile (text);
+	}
+
+	if (change != NULL) {
+		recursive = FALSE;
+
+		if (get_first_directory_target_file (window) != NULL) {
+			res = eel_run_simple_dialog
+				(GTK_WIDGET (window), FALSE,
+				 GTK_MESSAGE_QUESTION,
+				 /* translators: this will be shown to advanced users only */
+				 "Apply Changes Recursively?",
+				 NULL,
+				 _("_Don't Apply Recursively"),
+				 _("_Apply Recursively"),
+				 NULL);
+
+			switch (res) {
+				case 0:
+					recursive = FALSE;
+					break;
+
+				case 1:
+					recursive = TRUE;
+					break;
+
+				default:
+					g_free (change);
+					return;
+			}
+		}
+
+		apply_permission_changes (window,
+					  0, 0, 0, 0,
+					  change, recursive);
+	} else {
+		/* translators: this indicates to the advanced user that the entered
+		 * 		permission change mode was invalid */
+		eel_show_warning_dialog (_("Invalid Mode Specified"), NULL, GTK_WINDOW (window));
 	}
+}
+  
+static void
+permission_details_expander_expanded (GtkExpander *details_expander,
+				      GParamSpec *pspec,
+				      FMPropertiesWindow *window)
+{
+	g_assert (FM_IS_PROPERTIES_WINDOW (window));
 
-	return ret;
+	if (gtk_expander_get_expanded (details_expander) &&
+	    GTK_BIN (details_expander)->child == NULL) {
+		GtkWidget *alignment;
+		GtkWidget *details_table;
+
+		g_assert (window->details->chmod_entry == NULL);
+
+		alignment = gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
+		gtk_alignment_set_padding (GTK_ALIGNMENT (alignment),
+					   0, 0, 12, 0);
+		gtk_widget_show (alignment);
+		gtk_container_add (GTK_CONTAINER (details_expander), alignment);
+
+		details_table = gtk_table_new (0, COLUMN_COUNT, FALSE);
+		apply_standard_table_padding (GTK_TABLE (details_table));
+		gtk_widget_show (details_table);
+		gtk_container_add (GTK_CONTAINER (alignment), details_table);
+
+		append_title_value_pair
+			(window, GTK_TABLE (details_table), _("Text view:"), 
+			 "permissions", _("--"),
+			 FALSE,
+			 TRUE);
+		append_title_value_pair 
+			(window, GTK_TABLE (details_table), _("Number view:"), 
+			 "octal_permissions", _("--"),
+			 FALSE,
+			 TRUE);
+
+		/*
+		 * TODO decide whether this should really go in.
+		 * It involves mtime/ctime comparison, and thus is not very
+		 * verbose to 99.99% of the users.
+		append_title_value_pair
+			(window, GTK_TABLE (details_table), _("Last changed:"), 
+			 "date_permissions", _("--"),
+			 FALSE,
+			 TRUE);*/
+
+		GtkLabel *title_label;
+		GtkWidget *entry;
+		int row;
+
+		row = append_title_field (GTK_TABLE (details_table), "ch_mod:", &title_label);
+		/*
+		hbox = gtk_hbox_new (FALSE, 6);
+		gtk_widget_show (hbox);
+		gtk_table_attach (GTK_TABLE (details_table), hbox,
+				  VALUE_COLUMN, VALUE_COLUMN + 1,
+				  row, row + 1,
+				  GTK_FILL, 0,
+				  0, 0);*/
+
+		window->details->chmod_entry = entry = gtk_entry_new ();
+		gtk_widget_show (entry);
+		gtk_label_set_mnemonic_widget (title_label, entry);
+		gtk_table_attach (GTK_TABLE (details_table), entry,
+				  VALUE_COLUMN, VALUE_COLUMN + 1,
+				  row, row + 1,
+				  GTK_FILL, 0,
+				  0, 0);
+		g_signal_connect (entry, "activate",
+				  G_CALLBACK (chmod_entry_activated),
+				  window);
+		g_signal_connect_swapped (entry, "changed",
+					  G_CALLBACK (permission_combo_box_update_sensitivity_all),
+					  window);
+		g_signal_connect_swapped (entry, "changed",
+					  G_CALLBACK (executable_button_update_sensitivity_all),
+					  window);
+
+		/*
+		apply_button = gtk_button_new_from_stock (GTK_STOCK_APPLY);
+		gtk_widget_show (apply_button);
+		gtk_box_pack_start (GTK_BOX (hbox), apply_button, FALSE, FALSE, 0);
+		g_signal_connect (apply_button, "clicked",
+				  G_CALLBACK (chmod_apply_button_clicked),
+				  window);
+
+		g_signal_connect_swapped (entry, "activate",
+					  G_CALLBACK (gtk_button_clicked),
+					  apply_button);
+		TODO - decide whether to use APPLY button. Might not be neccessary - chmod users are keynav gurus */
+	}
+
+	if (gtk_expander_get_expanded (details_expander)) {
+		gtk_widget_grab_focus (window->details->chmod_entry);
+	}
+
+
+	permission_combo_box_update_sensitivity_all (window);
+	executable_button_update_sensitivity_all (window);
 }
 
 static void
 create_permissions_page (FMPropertiesWindow *window)
 {
+	GtkTable *table;
+	GtkLabel *label;
 	GtkWidget *vbox;
-	GtkTable *page_table, *check_button_table;
-	char *file_name, *prompt_text;
-	guint last_row;
-	guint checkbox_titles_row;
-	GtkLabel *group_label;
-	GtkLabel *owner_label;
-	GtkLabel *owner_perm_label;
-	GtkLabel *group_perm_label;
-	GtkLabel *other_perm_label;
-	GtkComboBox *group_combo_box;
-	GtkComboBox *owner_combo_box;
-	GList *file_list;
-
-	vbox = create_page_with_vbox (window->details->notebook,
-				      _("Permissions"));
-
-	file_list = window->details->original_files;
-
-	window->details->initial_permissions = NULL;
-	
-	if (all_can_get_permissions (file_list) && all_can_get_permissions (window->details->target_files)) {
-		window->details->initial_permissions = get_initial_permissions (window->details->target_files);
-		
-		if (!all_can_set_permissions (file_list)) {
-			add_prompt_and_separator (
-				GTK_VBOX (vbox), 
-				_("You are not the owner, so you can't change these permissions."));
-		}
-
-		page_table = GTK_TABLE (gtk_table_new (1, COLUMN_COUNT, FALSE));
-		window->details->permissions_table = page_table;
-
-		apply_standard_table_padding (page_table);
-		last_row = 0;
-		gtk_widget_show (GTK_WIDGET (page_table));
-		gtk_box_pack_start (GTK_BOX (vbox), 
-				    GTK_WIDGET (page_table), 
-				    TRUE, TRUE, 0);
-
-		if (!is_multi_file_window (window) && nautilus_file_can_set_owner (get_target_file (window))) {
-			owner_label = attach_title_field (page_table, last_row, _("File _owner:"));
-			/* Combo box in this case. */
-			owner_combo_box = attach_owner_combo_box (page_table, last_row, get_target_file (window));
-			gtk_label_set_mnemonic_widget (owner_label,
-						       GTK_WIDGET (owner_combo_box));
-		} else {
-			attach_title_field (page_table, last_row, _("File owner:"));
-			/* Static text in this case. */
-			attach_value_field (window, 
-					    page_table, last_row, VALUE_COLUMN,
-					    "owner",
-					    _("--"),
-					    FALSE); 
-		}
+	GtkWidget *expander;
+	GtkComboBox *combo;
+	unsigned int last_row;
+	char *file_name;
+	char *prompt_text;
+
+	vbox = create_page_with_vbox (window->details->notebook, _("Access Rights"));
+
+	if (!is_multi_file_window (window) &&
+	    !all_can_get_permissions (window->details->original_files)) {
+		file_name = nautilus_file_get_display_name (get_target_file (window));
 
-		if (!is_multi_file_window (window) && nautilus_file_can_set_group (get_target_file (window))) {
-			last_row = append_title_field (page_table,
-						       _("_File group:"),
-						       &group_label);
-			/* Combo box in this case. */
-			group_combo_box = attach_group_combo_box (page_table, last_row,
-								 get_target_file (window));
-			gtk_label_set_mnemonic_widget (group_label,
-						       GTK_WIDGET (group_combo_box));
-		} else {
-			last_row = append_title_field (page_table,
-						       _("File group:"),
-						       NULL);
-			/* Static text in this case. */
-			attach_value_field (window, page_table, last_row, 
-					    VALUE_COLUMN, 
-					    "group",
-					    _("--"),
-					    FALSE); 
-		}
+		prompt_text = g_strdup_printf (_("The access rights of \"%s\" could not be determined."), file_name);
+  		add_prompt (GTK_VBOX (vbox), prompt_text, TRUE);
 
-		append_separator (page_table);
-		
-		checkbox_titles_row = append_title_field (page_table, _("Owner:"), &owner_perm_label);
-		append_title_field (page_table, _("Group:"), &group_perm_label);
-		append_title_field (page_table, _("Others:"), &other_perm_label);
-		
-		check_button_table = GTK_TABLE (gtk_table_new 
-						   (PERMISSIONS_CHECKBOXES_ROW_COUNT, 
-						    PERMISSIONS_CHECKBOXES_COLUMN_COUNT, 
-						    FALSE));
-		apply_standard_table_padding (check_button_table);
-		gtk_widget_show (GTK_WIDGET (check_button_table));
-		gtk_table_attach (page_table, GTK_WIDGET (check_button_table),
-				  VALUE_COLUMN, VALUE_COLUMN + 1, 
-				  checkbox_titles_row, checkbox_titles_row + PERMISSIONS_CHECKBOXES_ROW_COUNT,
-				  0, 0,
-				  0, 0);
+  		g_free (prompt_text);
+		g_free (file_name);
 
-		add_permissions_checkbox (window,
-					  check_button_table, 
-					  PERMISSIONS_CHECKBOXES_OWNER_ROW,
-					  PERMISSIONS_CHECKBOXES_READ_COLUMN,
-					  GNOME_VFS_PERM_USER_READ,
-					  owner_perm_label);
-
-		add_permissions_checkbox (window,
-					  check_button_table, 
-					  PERMISSIONS_CHECKBOXES_OWNER_ROW,
-					  PERMISSIONS_CHECKBOXES_WRITE_COLUMN,
-					  GNOME_VFS_PERM_USER_WRITE,
-					  owner_perm_label);
-
-		add_permissions_checkbox (window,
-					  check_button_table, 
-					  PERMISSIONS_CHECKBOXES_OWNER_ROW,
-					  PERMISSIONS_CHECKBOXES_EXECUTE_COLUMN,
-					  GNOME_VFS_PERM_USER_EXEC,
-					  owner_perm_label);
-
-		add_permissions_checkbox (window,
-					  check_button_table, 
-					  PERMISSIONS_CHECKBOXES_GROUP_ROW,
-					  PERMISSIONS_CHECKBOXES_READ_COLUMN,
-					  GNOME_VFS_PERM_GROUP_READ,
-					  group_perm_label);
-
-		add_permissions_checkbox (window,
-					  check_button_table, 
-					  PERMISSIONS_CHECKBOXES_GROUP_ROW,
-					  PERMISSIONS_CHECKBOXES_WRITE_COLUMN,
-					  GNOME_VFS_PERM_GROUP_WRITE,
-					  group_perm_label);
-
-		add_permissions_checkbox (window,
-					  check_button_table, 
-					  PERMISSIONS_CHECKBOXES_GROUP_ROW,
-					  PERMISSIONS_CHECKBOXES_EXECUTE_COLUMN,
-					  GNOME_VFS_PERM_GROUP_EXEC,
-					  group_perm_label);
-
-		add_permissions_checkbox (window,
-					  check_button_table, 
-					  PERMISSIONS_CHECKBOXES_OTHERS_ROW,
-					  PERMISSIONS_CHECKBOXES_READ_COLUMN,
-					  GNOME_VFS_PERM_OTHER_READ,
-					  other_perm_label);
-
-		add_permissions_checkbox (window,
-					  check_button_table, 
-					  PERMISSIONS_CHECKBOXES_OTHERS_ROW,
-					  PERMISSIONS_CHECKBOXES_WRITE_COLUMN,
-					  GNOME_VFS_PERM_OTHER_WRITE,
-					  other_perm_label);
-
-		add_permissions_checkbox (window,
-					  check_button_table, 
-					  PERMISSIONS_CHECKBOXES_OTHERS_ROW,
-					  PERMISSIONS_CHECKBOXES_EXECUTE_COLUMN,
-					  GNOME_VFS_PERM_OTHER_EXEC,
-					  other_perm_label);
+		return;
+	} else if (!all_can_get_permissions (window->details->original_files)) {
+		add_prompt (GTK_VBOX (vbox),
+			    ngettext ("The access rights of the selected file could not be determined.",
+				      "The access rights of the selected files could not be determined.",
+				      g_list_length (window->details->original_files)),
+			    TRUE);
 
-		append_separator (page_table);
+		return;
+	} else if (!is_multi_file_window (window) &&
+		   !all_can_set_permissions(window->details->original_files)) {
+		file_name = nautilus_file_get_display_name (get_target_file (window));
 
-		append_special_execution_flags (window, page_table);
-		
-		append_title_value_pair
-			(window, page_table, _("Text view:"), 
-			 "permissions", _("--"),
-			 FALSE);
-		append_title_value_pair 
-			(window, page_table, _("Number view:"), 
-			 "octal_permissions", _("--"),
-			 FALSE);
-		append_title_value_pair
-			(window, page_table, _("Last changed:"), 
-			 "date_permissions", _("--"),
-			 FALSE);
-	} else {
-		if (!is_multi_file_window (window)) {
-			file_name = nautilus_file_get_display_name (get_target_file (window));
-			prompt_text = g_strdup_printf (_("The permissions of \"%s\" could not be determined."), file_name);
-			g_free (file_name);
-		} else {
-			prompt_text = g_strdup (_("The permissions of the selected file could not be determined."));
-		}
-		
+		prompt_text = g_strdup_printf (_("You are not the owner of \"%s\", so you can not change its access rights."), file_name);
 		add_prompt (GTK_VBOX (vbox), prompt_text, TRUE);
+
 		g_free (prompt_text);
+		g_free (file_name);
+
+		return;
+	} else if (!all_can_set_permissions (window->details->original_files)) {
+		add_prompt (GTK_VBOX (vbox),
+			    ngettext ("You are not the owner of the selected file, so you can not change its access rights.",
+				      "You are not the owner of the selected files, so you can not change their access rights.",
+				      g_list_length (window->details->original_files)),
+			    TRUE);
+
+		return;
 	}
+
+	window->details->permissions_table = table = GTK_TABLE (create_attribute_value_table (GTK_VBOX (vbox), 0));
+
+	if (!is_multi_file_window (window) &&
+	    nautilus_file_can_set_owner (get_target_file (window))) {
+		last_row = append_title_field (table, _("_Owner:"), &label);
+		/* Combo box in this case. */
+		combo = attach_owner_combo_box (table, last_row, get_target_file (window));
+		gtk_label_set_mnemonic_widget (label, GTK_WIDGET (combo));
+	} else {
+		last_row = append_title_field (table, _("Owner:"), &label);
+		/* Static text in this case. */
+		attach_value_field (window, 
+				    table, last_row, VALUE_COLUMN,
+				    "owner",
+				    _("--"),
+				    FALSE,
+				    FALSE); 
+	}
+
+	append_permission_combo_box (window, table,
+				     SIMPLE_PERMISSION_SCOPE_USER,
+				     _("_Access:"));
+
+	append_executable_button (window, table, SIMPLE_PERMISSION_SCOPE_USER);
+
+	last_row = append_row (table);
+
+	/* FIXME use proper spacing */
+	last_row = append_title_field (table, NULL, NULL);
+
+	/* Group */
+	if (!is_multi_file_window (window) &&
+	    nautilus_file_can_set_group (get_target_file (window))) {
+		last_row = append_title_field (table,
+					       _("_Group:"),
+					       &label);
+		/* Combo box in this case. */
+		combo = attach_group_combo_box (table, last_row, get_target_file (window));
+		gtk_label_set_mnemonic_widget (label, GTK_WIDGET (combo));
+	} else {
+		last_row = append_title_field (table,
+					       _("Group:"),
+					       NULL);
+		/* Static text in this case. */
+		attach_value_field (window, table, last_row, 
+				    VALUE_COLUMN, 
+				    "group",
+				    _("--"),
+				    FALSE,
+				    FALSE); 
+	}
+
+	append_permission_combo_box (window, table,
+				     SIMPLE_PERMISSION_SCOPE_GROUP,
+				     _("Acc_ess:"));
+
+	append_executable_button (window, table, SIMPLE_PERMISSION_SCOPE_GROUP);
+
+	/* FIXME use proper spacing */
+	last_row = append_title_field (table, NULL, NULL);
+
+	/* Others */
+	append_permission_combo_box (window, table,
+				     SIMPLE_PERMISSION_SCOPE_OTHER,
+				     _("_Others:"));
+
+	append_executable_button (window, table, SIMPLE_PERMISSION_SCOPE_OTHER);
+
+	/* FIXME use proper spacing */
+	last_row = append_row (table);
+
+	g_assert (window->details->details_expander == NULL);
+
+	window->details->details_expander = expander = gtk_expander_new_with_mnemonic ("_Details");
+	gtk_widget_show (expander);
+	gtk_table_attach (table, expander,
+			  TITLE_COLUMN, VALUE_COLUMN + 1,
+			  last_row, last_row + 1,
+			  GTK_FILL, 0,
+			  0, 0);
+
+	g_signal_connect (expander, "notify::expanded",
+			  G_CALLBACK (permission_details_expander_expanded),
+			  window);
 }
 
 static void
@@ -3675,23 +4350,12 @@ real_destroy (GtkObject *object)
  
 	window->details->name_field = NULL;
 
-	g_list_free (window->details->special_flags_widgets);
-	window->details->special_flags_widgets = NULL;
-
 	g_list_free (window->details->emblem_buttons);
 	window->details->emblem_buttons = NULL;
 
 	if (window->details->initial_emblems) {
 		g_hash_table_destroy (window->details->initial_emblems);
 		window->details->initial_emblems = NULL;
-	}
-
-	g_list_free (window->details->permission_buttons);
-	window->details->permission_buttons = NULL;
-
-	if (window->details->initial_permissions) {
-		g_hash_table_destroy (window->details->initial_permissions);
-		window->details->initial_permissions = NULL;
 	}
 
 	g_list_free (window->details->value_fields);
