UI: Windows File Attributes and Hidden Items
authorHarley Acheson <harley.acheson@gmail.com>
Fri, 21 Feb 2020 16:18:29 +0000 (08:18 -0800)
committerHarley Acheson <harley.acheson@gmail.com>
Fri, 21 Feb 2020 16:20:58 +0000 (08:20 -0800)
File Browser using Windows file attributes for decorating and hiding items.

Differential Revision: https://developer.blender.org/D6816

Reviewed by Campbell Barton

source/blender/blenlib/BLI_fileops.h
source/blender/blenlib/intern/storage.c
source/blender/editors/include/UI_icons.h
source/blender/editors/space_file/file_draw.c
source/blender/editors/space_file/filelist.c
source/blender/makesdna/DNA_space_types.h

index 5c20e57181edc0de2a0dcd7e2aa14e91f566a7a7..3ee22e4ad0a75ee9040fc1e47d4013a260c2dd42 100644 (file)
@@ -73,6 +73,29 @@ int BLI_stat(const char *path, BLI_stat_t *buffer) ATTR_WARN_UNUSED_RESULT ATTR_
 int BLI_wstat(const wchar_t *path, BLI_stat_t *buffer);
 #endif
 
+typedef enum eFileAttributes {
+  FILE_ATTR_READONLY = 1 << 0,        /* Read-only or Immutable. */
+  FILE_ATTR_HIDDEN = 1 << 1,          /* Hidden or invisible. */
+  FILE_ATTR_SYSTEM = 1 << 2,          /* Used by the Operating System. */
+  FILE_ATTR_ARCHIVE = 1 << 3,         /* Marked as archived. */
+  FILE_ATTR_COMPRESSED = 1 << 4,      /* Compressed. */
+  FILE_ATTR_ENCRYPTED = 1 << 5,       /* Encrypted. */
+  FILE_ATTR_RESTRICTED = 1 << 6,      /* Protected by OS. */
+  FILE_ATTR_TEMPORARY = 1 << 7,       /* Used for temporary storage. */
+  FILE_ATTR_SPARSE_FILE = 1 << 8,     /* Sparse File. */
+  FILE_ATTR_OFFLINE = 1 << 9,         /* Data is not immediately available. */
+  FILE_ATTR_ALIAS = 1 << 10,          /* Mac Alias or Windows Lnk. File-based redirection. */
+  FILE_ATTR_REPARSE_POINT = 1 << 11,  /* File has associated reparse point. */
+  FILE_ATTR_SYMLINK = 1 << 12,        /* Reference to another file. */
+  FILE_ATTR_JUNCTION_POINT = 1 << 13, /* Folder Symlink. */
+  FILE_ATTR_MOUNT_POINT = 1 << 14,    /* Volume mounted as a folder. */
+  FILE_ATTR_HARDLINK = 1 << 15,       /* Duplicated directory entry. */
+} eFileAttributes;
+
+#define FILE_ATTR_ANY_LINK \
+  (FILE_ATTR_ALIAS | FILE_ATTR_REPARSE_POINT | FILE_ATTR_SYMLINK | FILE_ATTR_JUNCTION_POINT | \
+   FILE_ATTR_MOUNT_POINT | FILE_ATTR_HARDLINK)
+
 /* Directories */
 
 struct direntry;
@@ -83,6 +106,7 @@ bool BLI_dir_create_recursive(const char *dir) ATTR_NONNULL();
 double BLI_dir_free_space(const char *dir) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
 char *BLI_current_working_dir(char *dir, const size_t maxlen) ATTR_WARN_UNUSED_RESULT
     ATTR_NONNULL();
+eFileAttributes BLI_file_attributes(const char *path);
 
 /* Filelist */
 
index 7c481868d649fe4bb48a671a92fd28b76c71a6e8..04b3e8abca2995a118fda1ecf1457e9bb8913df6 100644 (file)
@@ -199,6 +199,69 @@ size_t BLI_file_size(const char *path)
   return stats.st_size;
 }
 
+eFileAttributes BLI_file_attributes(const char *path)
+{
+  int ret = 0;
+
+#ifdef WIN32
+  wchar_t wline[FILE_MAXDIR];
+  BLI_strncpy_wchar_from_utf8(wline, path, ARRAY_SIZE(wline));
+  DWORD attr = GetFileAttributesW(wline);
+  if (attr & FILE_ATTRIBUTE_READONLY) {
+    ret |= FILE_ATTR_READONLY;
+  }
+  if (attr & FILE_ATTRIBUTE_HIDDEN) {
+    ret |= FILE_ATTR_HIDDEN;
+  }
+  if (attr & FILE_ATTRIBUTE_SYSTEM) {
+    ret |= FILE_ATTR_SYSTEM;
+  }
+  if (attr & FILE_ATTRIBUTE_ARCHIVE) {
+    ret |= FILE_ATTR_ARCHIVE;
+  }
+  if (attr & FILE_ATTRIBUTE_COMPRESSED) {
+    ret |= FILE_ATTR_COMPRESSED;
+  }
+  if (attr & FILE_ATTRIBUTE_ENCRYPTED) {
+    ret |= FILE_ATTR_ENCRYPTED;
+  }
+  if (attr & FILE_ATTRIBUTE_TEMPORARY) {
+    ret |= FILE_ATTR_TEMPORARY;
+  }
+  if (attr & FILE_ATTRIBUTE_SPARSE_FILE) {
+    ret |= FILE_ATTR_SPARSE_FILE;
+  }
+  if (attr & FILE_ATTRIBUTE_OFFLINE) {
+    ret |= FILE_ATTR_OFFLINE;
+  }
+  if (attr & FILE_ATTRIBUTE_REPARSE_POINT) {
+    ret |= FILE_ATTR_REPARSE_POINT;
+  }
+
+#endif
+
+#ifdef __APPLE__
+
+  /* TODO:
+   * If Hidden (Invisible) set FILE_ATTR_HIDDEN
+   * If Locked set FILE_ATTR_READONLY
+   * If Restricted set FILE_ATTR_RESTRICTED
+   */
+
+#endif
+
+#ifdef __linux__
+
+  /* TODO:
+   * If Immutable set FILE_ATTR_READONLY
+   * If Archived set FILE_ATTR_ARCHIVE
+   */
+
+#endif
+
+  return ret;
+}
+
 /**
  * Returns the st_mode from stat-ing the specified path name, or 0 if stat fails
  * (most likely doesn't exist or no access).
index fabf6baed2386e39f0a2a8ab27fa43f830a55963..6739f7cb12cbbca4a6c9af4c8623e606fadca4a0 100644 (file)
@@ -793,7 +793,7 @@ DEF_ICON(BOOKMARKS)
 DEF_ICON(FONTPREVIEW)
 DEF_ICON(FILTER)
 DEF_ICON(NEWFOLDER)
-DEF_ICON(FOLDER_REDIRECT)
+DEF_ICON_FOLDER(FOLDER_REDIRECT)
 DEF_ICON(FILE_PARENT)
 DEF_ICON(FILE_REFRESH)
 DEF_ICON_FOLDER(FILE_FOLDER)
index 0aec6d5e6a0a6b40e9bc5245cbf9754a1ec51dab..1739c15cbc6f68196b7a6759174296a1ed5ccbee 100644 (file)
@@ -133,8 +133,15 @@ static void draw_tile(int sx, int sy, int width, int height, int colorid, int sh
       true, (float)sx, (float)(sy - height), (float)(sx + width), (float)sy, 5.0f, color);
 }
 
-static void file_draw_icon(
-    uiBlock *block, const char *path, int sx, int sy, int icon, int width, int height, bool drag)
+static void file_draw_icon(uiBlock *block,
+                           const char *path,
+                           int sx,
+                           int sy,
+                           int icon,
+                           int width,
+                           int height,
+                           bool drag,
+                           bool dimmed)
 {
   uiBut *but;
   int x, y;
@@ -142,8 +149,11 @@ static void file_draw_icon(
   x = sx;
   y = sy - height;
 
+  /* For uiDefIconBut(), if a1==1.0 then a2 is alpha 0.0 - 1.0 */
+  const float a1 = dimmed ? 1.0f : 0.0f;
+  const float a2 = dimmed ? 0.3f : 0.0f;
   but = uiDefIconBut(
-      block, UI_BTYPE_LABEL, 0, icon, x, y, width, height, NULL, 0.0f, 0.0f, 0.0f, 0.0f, NULL);
+      block, UI_BTYPE_LABEL, 0, icon, x, y, width, height, NULL, 0.0f, 0.0f, a1, a2, NULL);
   UI_but_func_tooltip_set(but, file_draw_tooltip_func, BLI_strdup(path));
 
   if (drag) {
@@ -210,7 +220,8 @@ static void file_draw_preview(uiBlock *block,
                               FileLayout *layout,
                               const bool is_icon,
                               const int typeflags,
-                              const bool drag)
+                              const bool drag,
+                              const bool dimmed)
 {
   uiBut *but;
   float fx, fy;
@@ -273,6 +284,10 @@ static void file_draw_preview(uiBlock *block,
     UI_GetThemeColor4fv(TH_TEXT, col);
   }
 
+  if (dimmed) {
+    col[3] *= 0.3f;
+  }
+
   if (!is_icon && typeflags & FILE_TYPE_BLENDERLIB) {
     /* Datablock preview images use premultiplied alpha. */
     GPU_blend_set_func_separate(
@@ -775,6 +790,7 @@ void file_draw_list(const bContext *C, ARegion *ar)
 
     /* don't drag parent or refresh items */
     do_drag = !(FILENAME_IS_CURRPAR(file->relpath));
+    const bool is_hidden = (file->attributes & FILE_ATTR_HIDDEN);
 
     if (FILE_IMGDISPLAY == params->display) {
       const int icon = filelist_geticon(files, i, false);
@@ -795,7 +811,8 @@ void file_draw_list(const bContext *C, ARegion *ar)
                         layout,
                         is_icon,
                         file->typeflag,
-                        do_drag);
+                        do_drag,
+                        is_hidden);
     }
     else {
       file_draw_icon(block,
@@ -805,7 +822,8 @@ void file_draw_list(const bContext *C, ARegion *ar)
                      filelist_geticon(files, i, true),
                      ICON_DEFAULT_WIDTH_SCALE,
                      ICON_DEFAULT_HEIGHT_SCALE,
-                     do_drag);
+                     do_drag,
+                     is_hidden);
       icon_ofs += ICON_DEFAULT_WIDTH_SCALE + 0.2f * UI_UNIT_X;
     }
 
index b328b32263c5125dce6dfa72e02c04d66a0d06c1..28e6d95beb33adc4823123349732d7bfa63f4a5d 100644 (file)
@@ -213,6 +213,9 @@ typedef struct FileListInternEntry {
   /** not strictly needed, but used during sorting, avoids to have to recompute it there... */
   char *name;
 
+  /** Defined in BLI_fileops.h */
+  eFileAttributes attributes;
+
   BLI_stat_t st;
 } FileListInternEntry;
 
@@ -609,54 +612,76 @@ void filelist_setsorting(struct FileList *filelist, const short sort, bool inver
 
 /* ********** Filter helpers ********** */
 
-static bool is_hidden_file(const char *filename, FileListFilter *filter)
+/* True if filename is meant to be hidden, eg. starting with period. */
+static bool is_hidden_dot_filename(const char *filename, FileListInternEntry *file)
 {
-  char *sep = (char *)BLI_last_slash(filename);
-  bool is_hidden = false;
-
-  if (filter->flags & FLF_HIDE_DOT) {
-    if (filename[0] == '.' && filename[1] != '.' && filename[1] != '\0') {
-      is_hidden = true; /* ignore .file */
-    }
-    else {
-      int len = strlen(filename);
-      if ((len > 0) && (filename[len - 1] == '~')) {
-        is_hidden = true; /* ignore file~ */
-      }
-    }
+  if (filename[0] == '.' && !ELEM(filename[1], '.', '\0')) {
+    return true; /* ignore .file */
   }
-  if (!is_hidden && (filter->flags & FLF_HIDE_PARENT)) {
-    if (filename[0] == '.' && filename[1] == '.' && filename[2] == '\0') {
-      is_hidden = true; /* ignore .. */
+  else {
+    int len = strlen(filename);
+    if ((len > 0) && (filename[len - 1] == '~')) {
+      return true; /* ignore file~ */
     }
   }
-  if (!is_hidden && ((filename[0] == '.') && (filename[1] == '\0'))) {
-    is_hidden = true; /* ignore . */
-  }
+
   /* filename might actually be a piece of path, in which case we have to check all its parts. */
-  if (!is_hidden && sep) {
+
+  bool hidden = false;
+  char *sep = (char *)BLI_last_slash(filename);
+
+  if (!hidden && sep) {
     char tmp_filename[FILE_MAX_LIBEXTRA];
 
     BLI_strncpy(tmp_filename, filename, sizeof(tmp_filename));
     sep = tmp_filename + (sep - filename);
     while (sep) {
       BLI_assert(sep[1] != '\0');
-      if (is_hidden_file(sep + 1, filter)) {
-        is_hidden = true;
+      if (is_hidden_dot_filename(sep + 1, file)) {
+        hidden = true;
         break;
       }
       *sep = '\0';
       sep = (char *)BLI_last_slash(tmp_filename);
     }
   }
-  return is_hidden;
+  return hidden;
+}
+
+/* True if should be hidden, based on current filtering. */
+static bool is_filtered_hidden(const char *filename,
+                               FileListFilter *filter,
+                               FileListInternEntry *file)
+{
+  if ((filename[0] == '.') && (filename[1] == '\0')) {
+    return true; /* Ignore . */
+  }
+
+  if (filter->flags & FLF_HIDE_PARENT) {
+    if (filename[0] == '.' && filename[1] == '.' && filename[2] == '\0') {
+      return true; /* Ignore .. */
+    }
+  }
+
+  if ((filter->flags & FLF_HIDE_DOT) && (file->attributes & FILE_ATTR_HIDDEN)) {
+    return true; /* Ignore files with Hidden attribute. */
+  }
+
+#ifndef WIN32
+  /* Check for unix-style names starting with period. */
+  if ((filter->flags & FLF_HIDE_DOT) && is_hidden_dot_filename(filename, file)) {
+    return true;
+  }
+#endif
+
+  return false;
 }
 
 static bool is_filtered_file(FileListInternEntry *file,
                              const char *UNUSED(root),
                              FileListFilter *filter)
 {
-  bool is_filtered = !is_hidden_file(file->relpath, filter);
+  bool is_filtered = !is_filtered_hidden(file->relpath, filter, file);
 
   if (is_filtered && !FILENAME_IS_CURRPAR(file->relpath)) {
     /* We only check for types if some type are enabled in filtering. */
@@ -699,7 +724,7 @@ static bool is_filtered_lib(FileListInternEntry *file, const char *root, FileLis
   BLI_join_dirfile(path, sizeof(path), root, file->relpath);
 
   if (BLO_library_path_explode(path, dir, &group, &name)) {
-    is_filtered = !is_hidden_file(file->relpath, filter);
+    is_filtered = !is_filtered_hidden(file->relpath, filter, file);
     if (is_filtered && !FILENAME_IS_CURRPAR(file->relpath)) {
       /* We only check for types if some type are enabled in filtering. */
       if ((filter->filter || filter->filter_id) && (filter->flags & FLF_DO_FILTER)) {
@@ -747,7 +772,7 @@ static bool is_filtered_main(FileListInternEntry *file,
                              const char *UNUSED(dir),
                              FileListFilter *filter)
 {
-  return !is_hidden_file(file->relpath, filter);
+  return !is_filtered_hidden(file->relpath, filter, file);
 }
 
 static void filelist_filter_clear(FileList *filelist)
@@ -968,7 +993,7 @@ static int filelist_geticon_ex(FileDirEntry *file,
     else if (is_main) {
       /* Do not return icon for folders if icons are not 'main' draw type
        * (e.g. when used over previews). */
-      return ICON_FILE_FOLDER;
+      return (file->attributes & FILE_ATTR_ANY_LINK) ? ICON_FOLDER_REDIRECT : ICON_FILE_FOLDER;
     }
     else {
       /* If this path is in System list then use that icon. */
@@ -984,6 +1009,19 @@ static int filelist_geticon_ex(FileDirEntry *file,
         }
       }
     }
+
+    if (file->attributes & FILE_ATTR_ANY_LINK) {
+      return ICON_LOOP_FORWARDS;
+    }
+    else if (file->attributes & FILE_ATTR_OFFLINE) {
+      return ICON_ERROR;
+    }
+    else if (file->attributes & FILE_ATTR_TEMPORARY) {
+      return ICON_FILE_CACHE;
+    }
+    else if (file->attributes & FILE_ATTR_SYSTEM) {
+      return ICON_SYSTEM;
+    }
   }
 
   if (typeflag & FILE_TYPE_BLENDER) {
@@ -1621,7 +1659,7 @@ static FileDirEntry *filelist_file_create_entry(FileList *filelist, const int in
   memcpy(ret->uuid, entry->uuid, sizeof(ret->uuid));
   ret->blentype = entry->blentype;
   ret->typeflag = entry->typeflag;
-
+  ret->attributes = entry->attributes;
   BLI_addtail(&cache->cached_entries, ret);
   return ret;
 }
@@ -2401,6 +2439,7 @@ static int filelist_readjob_list_dir(const char *root,
 {
   struct direntry *files;
   int nbr_files, nbr_entries = 0;
+  char path[FILE_MAX];
 
   nbr_files = BLI_filelist_dir_contents(root, &files);
   if (files) {
@@ -2416,20 +2455,17 @@ static int filelist_readjob_list_dir(const char *root,
       entry->relpath = MEM_dupallocN(files[i].relname);
       entry->st = files[i].s;
 
+      BLI_join_dirfile(path, sizeof(path), root, entry->relpath);
+
       /* Set file type. */
       if (S_ISDIR(files[i].s.st_mode)) {
         entry->typeflag = FILE_TYPE_DIR;
       }
       else if (do_lib && BLO_has_bfile_extension(entry->relpath)) {
         /* If we are considering .blend files as libs, promote them to directory status. */
-        char name[FILE_MAX];
-
         entry->typeflag = FILE_TYPE_BLENDER;
-
-        BLI_join_dirfile(name, sizeof(name), root, entry->relpath);
-
         /* prevent current file being used as acceptable dir */
-        if (BLI_path_cmp(main_name, name) != 0) {
+        if (BLI_path_cmp(main_name, path) != 0) {
           entry->typeflag |= FILE_TYPE_DIR;
         }
       }
@@ -2441,6 +2477,16 @@ static int filelist_readjob_list_dir(const char *root,
         }
       }
 
+      /* Set file attributes. */
+      entry->attributes = BLI_file_attributes(path);
+
+#ifndef WIN32
+      /* Set linux-style dot files hidden too. */
+      if (is_hidden_dot_filename(entry->relpath, entry)) {
+        entry->attributes |= FILE_ATTR_HIDDEN;
+      }
+#endif
+
       BLI_addtail(entries, entry);
       nbr_entries++;
     }
index 159c0424ee8a8196051ce59569ddc56c7e3d3ed4..bdfe5040794e91d5685c8e8dbfa26489af824541 100644 (file)
@@ -965,6 +965,8 @@ typedef struct FileDirEntry {
 
   short status;
   short flags;
+  /* eFileAttributes defined in BLI_fileops.h */
+  int attributes;
 
   ListBase variants;
   int nbr_variants;