This is an automated email from the git hooks/post-receive script.

git pushed a commit to branch master
in repository efm2.

View the commit online.

commit bc20a90fd8e931c90f78411db378565c646724c2
Author: Carsten Haitzler (Rasterman) <ras...@rasterman.com>
AuthorDate: Thu Jul 6 07:48:37 2023 +0100

    metadta - add start of metadata system for files
    
    compiles, in theory should work... with some XXX gotchas (like race
    condition for 2 writers - no locks), but untested as of now. just
    built the infra and it compiles...
---
 src/backends/default/meson.build |   3 +-
 src/backends/default/meta.c      | 347 +++++++++++++++++++++++++++++++++++++++
 src/backends/default/meta.h      |   4 +
 src/backends/default/open.c      |  52 ++++--
 src/efm/efm.c                    |  60 +++++++
 5 files changed, 452 insertions(+), 14 deletions(-)

diff --git a/src/backends/default/meson.build b/src/backends/default/meson.build
index b807fa0..34973a1 100644
--- a/src/backends/default/meson.build
+++ b/src/backends/default/meson.build
@@ -11,7 +11,8 @@ executable('open', [
     '../../shared/common/cmd.c',
     '../../shared/common/sha.c',
     '../../backends/common/fs_backend_core.c',
-    'open.c'
+    'open.c',
+    'meta.c'
   ],
   include_directories: inc,
   dependencies: deps,
diff --git a/src/backends/default/meta.c b/src/backends/default/meta.c
new file mode 100644
index 0000000..bb2fcb1
--- /dev/null
+++ b/src/backends/default/meta.c
@@ -0,0 +1,347 @@
+#include <Eina.h>
+#include <Eet.h>
+#include <Efreet.h>
+#include <Ecore.h>
+#include <Ecore_File.h>
+#include "sha.h"
+#include "meta.h"
+
+// This is a metadata store to store added metadata for any given file path.
+// For example store the x,y location of a file, added comments, flags a user
+// may set on a file.
+// 
+// "primary" location for medata for a /path/to/dir/ location is
+// /path/to/dir/.efm/filename.efm where that file is a desktop style
+// ini format with X-Efm-XXX=YYY fields where XXX is the meta key and YYY is
+// the meta data. Secondary location is ~/.e/e/efm-meta/7a/172c003a7fb98e.efm
+// i.e. the sha1 of the full target file path for metadata with first 2 hex
+// chars being a toplevel dir to store all sub-hashes in that dir from the
+// first 2 chars of the hex string of the sha1.
+// 
+// if user cannot write to the primary location, metadata will be written to
+// the secondary personal store location instead.
+// 
+// this is not the most efficient in disk space or speed etc. but it
+// "accessible" as text files on disk able to be edited and as this is
+// the filemanager and is now doing things in a way to improve accessability
+// to allow scripts, tools etc. to work with and generate data etc. then
+// this is the compromise choice taken.
+
+typedef struct _Meta_File
+{
+   const char  *path; // path of original file this metadata is for
+   Eina_List   *list; // for a small mount of meta - list of Meta
+   Eina_Bool    changed : 1; // has changes to write out
+} Meta_File;
+
+typedef struct _Meta
+{
+   const char *meta; // meta key
+   const char *data; // data in meta
+} Meta;
+
+static const char  *_config_dir = NULL;
+static Eina_Hash   *_meta_hash = NULL;
+static Eina_List   *_meta_writes = NULL;
+static Ecore_Timer *_meta_flush_timer = NULL;
+
+///////////////////////////////////////////////////////////////////////////////
+
+static char *
+_meta_personal_overlay_file_get(Meta_File *mf)
+{ // get secondary personal path to meta file for target path
+   unsigned char dst[20];
+   char sha1[41], buf[PATH_MAX];
+
+   eina_sha1((unsigned char *)mf->path, strlen(mf->path), dst);
+   sha1_str(dst, sha1);
+   snprintf(buf, sizeof(buf), "%s/efm-meta/%c%c/%s.efm",
+            _config_dir, sha1[0], sha1[1], sha1 + 2);
+   return strdup(buf);
+}
+
+static char *
+_meta_file_get(Meta_File *mf)
+{ // get primy meta file for the target path
+   char *dir = ecore_file_dir_get(mf->path);
+   char buf[PATH_MAX];
+
+   if (!dir) return NULL;
+   snprintf(buf, sizeof(buf), "%s/.efm/%s.efm",
+            dir, ecore_file_file_get(mf->path));
+   free(dir);
+   return strdup(buf);
+}
+
+static void
+_meta_free(Meta *m)
+{
+   if (m->meta) eina_stringshare_del(m->meta);
+   if (m->data) eina_stringshare_del(m->data);
+   free(m);
+}
+
+static void
+_meta_file_free(Meta_File *mf)
+{
+   Meta *m;
+
+   if (mf->path) eina_stringshare_del(mf->path);
+   EINA_LIST_FREE(mf->list, m) _meta_free(m);
+   free(mf);
+}
+
+static void
+_meta_file_write(Meta_File *mf)
+{ // write out all in memory metadata to target meta file
+   Eina_List *l;
+   Meta *m;
+   FILE *f;
+   char *meta_path = NULL, *dir = NULL;
+
+   if (!mf->changed) return;
+   mf->changed = EINA_FALSE;
+
+   meta_path = _meta_file_get(mf);
+   if (!meta_path) goto err;
+   dir = ecore_file_dir_get(meta_path);
+   if (!dir) goto err;
+   ecore_file_mkpath(dir);
+   free(dir);
+   dir = NULL;
+
+   if (mf->list)
+     {
+        // XXX: should gain a lock!
+        f = fopen(meta_path, "w");
+        if (!f)
+          { // can't write to dir - write to personal meta instead
+             free(meta_path);
+             meta_path = _meta_personal_overlay_file_get(mf);
+             if (!meta_path) return;
+             dir = ecore_file_dir_get(meta_path);
+             if (!dir) return;
+             ecore_file_mkpath(dir);
+             free(dir);
+             dir = NULL;
+             f = fopen(meta_path, "w");
+             if (!f) goto err;
+          }
+        fprintf(f, "[Desktop Entry]\n");
+        EINA_LIST_FOREACH(mf->list, l, m)
+          fprintf(f, "X-Efm-%s=%s\n", m->meta, m->data);
+        fclose(f);
+     }
+   else // no meta keys - delete it
+     ecore_file_unlink(meta_path);
+err:
+   free(meta_path);
+   free(dir);
+}
+
+static void
+_meta_writes_flush(void)
+{ // flush all pending writes
+   Meta_File *mf;
+
+   // XXX: time this and don't spend more than X time writing out meta files
+   EINA_LIST_FREE(_meta_writes, mf) _meta_file_write(mf);
+}
+
+static Eina_Bool
+_cb_meta_flush_timer(void *data EINA_UNUSED)
+{
+   _meta_flush_timer = NULL;
+   _meta_writes_flush();
+   // XXX: if flush took too long - queue another timer and try flush again
+   // in future. repeate until nothing left to flush
+   return EINA_FALSE;
+}
+
+static void
+_meta_file_write_queue(Meta_File *mf)
+{ // tag the meta file as changed and queue some writes
+   if (_meta_flush_timer) ecore_timer_reset(_meta_flush_timer);
+   else _meta_flush_timer = ecore_timer_add(0.2, _cb_meta_flush_timer, NULL);
+   if (!mf->changed)
+     {
+        _meta_writes = eina_list_append(_meta_writes, mf);
+        mf->changed = EINA_TRUE;
+     }
+}
+
+static void
+_meta_hash_entry_free(void *data)
+{
+   _meta_file_free(data);
+}
+
+static Eina_Bool
+_cb_meta_desktop_x_foreach(const Eina_Hash *hash EINA_UNUSED, const void *key, void *data, void *fdata)
+{
+   Meta_File *mf = fdata;
+   Meta *m;
+
+   if (!strncmp(key, "X-Efm-", 6))
+     { // only care about X-Efm-something entries
+        m = calloc(1, sizeof(Meta));
+        if (m)
+          {
+             m->meta = eina_stringshare_add(key + 6);
+             m->data = ""
+             mf->list = eina_list_append(mf->list, m);
+          }
+     }
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+_cb_meta_desktop_x_foreach_over(const Eina_Hash *hash EINA_UNUSED, const void *key, void *data, void *fdata)
+{
+   Meta_File *mf = fdata;
+   Meta *m;
+   Eina_List *l;
+
+   if (!strncmp(key, "X-Efm-", 6))
+     { // only care about X-Efm-something entries
+        EINA_LIST_FOREACH(mf->list, l, m)
+          { // find existing meta key and replace
+             if (!strcmp(m->meta, key + 6))
+               {
+                  eina_stringshare_replace(&(m->data), data);
+                  goto done;
+               }
+          }
+        // not found - append meta
+        m = calloc(1, sizeof(Meta));
+        if (m)
+          {
+             m->meta = eina_stringshare_add(key + 6);
+             m->data = ""
+             mf->list = eina_list_append(mf->list, m);
+          }
+     }
+done:
+   return EINA_TRUE;
+}
+
+static Meta_File *
+_meta_file_find(const char *path)
+{
+   Meta_File *mf;
+   Efreet_Desktop *desktop;
+   char *meta_path;
+
+   // find existing in memory meta file data and return that
+   mf = eina_hash_find(_meta_hash, path);
+   if (mf) return mf;
+
+   // not found - alloc and load in from disk
+   mf = calloc(1, sizeof(Meta_File));
+   if (!mf) return NULL;
+
+   mf->path = eina_stringshare_add(path);
+
+   // load meta file data in dir itself first
+   meta_path = _meta_file_get(mf);
+   if (!meta_path) goto err;
+
+   desktop = efreet_desktop_get(meta_path);
+   if (desktop)
+     {
+        eina_hash_foreach(desktop->x, _cb_meta_desktop_x_foreach, mf);
+        efreet_desktop_free(desktop);
+     }
+   free(meta_path);
+
+   // load overlayed user metdata and modify meta file content based on it
+   meta_path = _meta_personal_overlay_file_get(mf);
+   if (meta_path)
+     {
+        desktop = efreet_desktop_get(meta_path);
+        if (desktop)
+          {
+             eina_hash_foreach(desktop->x, _cb_meta_desktop_x_foreach_over, mf);
+             efreet_desktop_free(desktop);
+          }
+        free(meta_path);
+     }
+
+   // add to our hash db of meta files
+   eina_hash_add(_meta_hash, mf->path, mf);
+
+   return mf;
+err:
+   if (mf->path) eina_stringshare_del(mf->path);
+   free(mf);
+   return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+meta_init(const char *config_dir)
+{
+   _config_dir = eina_stringshare_add(config_dir);
+   _meta_hash = eina_hash_string_superfast_new(_meta_hash_entry_free);
+}
+
+void
+meta_shutdown(void)
+{
+   if (_meta_flush_timer)
+     {
+        ecore_timer_del(_meta_flush_timer);
+        _meta_writes_flush();
+     }
+   eina_hash_free(_meta_hash);
+   eina_stringshare_del(_config_dir);
+}
+
+void
+meta_set(const char *path, const char *meta, const char *data)
+{ // data = "" -> delete meta
+   Meta_File *mf = _meta_file_find(path);
+   Eina_List *l;
+   Meta *m;
+
+   if (!mf) return;
+   EINA_LIST_FOREACH(mf->list, l, m)
+     {
+        if (!strcmp(m->meta, meta))
+          { // found matching meta key -> modify or del it
+             if (!strcmp(m->data, data)) return;
+             if (data) // modify it if data != NULL
+               eina_stringshare_replace(&m->data, data);
+             else
+               { // NULL data - delete it
+                  mf->list = eina_list_remove_list(mf->list, l);
+                  _meta_free(m);
+               }
+             _meta_file_write_queue(mf); // queue writes for later
+             return;
+          }
+     }
+   if (!data) return;
+   m = calloc(1, sizeof(Meta));
+   if (!m) return;
+   m->meta = eina_stringshare_add(meta);
+   m->data = ""
+   mf->list = eina_list_append(mf->list, m);
+}
+
+Eina_Stringshare *
+meta_get(const char *path, const char *meta)
+{
+   Meta_File *mf = _meta_file_find(path);
+   Eina_List *l;
+   Meta *m;
+
+   if (!mf) return NULL;
+   EINA_LIST_FOREACH(mf->list, l, m)
+     {
+        if (!strcmp(m->meta, meta))
+          return eina_stringshare_ref(m->data);
+     }
+   return NULL;
+}
diff --git a/src/backends/default/meta.h b/src/backends/default/meta.h
new file mode 100644
index 0000000..a2ef310
--- /dev/null
+++ b/src/backends/default/meta.h
@@ -0,0 +1,4 @@
+void              meta_init(const char *config_dir);
+void              meta_shutdown(void);
+void              meta_set(const char *path, const char *meta, const char *data);
+Eina_Stringshare *meta_get(const char *path, const char *meta);
diff --git a/src/backends/default/open.c b/src/backends/default/open.c
index 60ec446..09c8e70 100644
--- a/src/backends/default/open.c
+++ b/src/backends/default/open.c
@@ -8,6 +8,7 @@
 // copy, import, export etc.
 #include "cmd.h"
 #include "sha.h"
+#include "meta.h"
 #include <Ecore_File.h>
 #include <Efreet.h>
 #include <Efreet_Mime.h>
@@ -771,14 +772,38 @@ err:
 void
 do_handle_cmd(Cmd *c)
 {
-   if (!strcmp(c->command, "dir-set"))
+#define KEY_WALK_BEGIN \
+   for (i = 0; c->dict[i]; i += 2) \
+     { \
+        key = c->dict[i]; \
+        data = "" + 1]; \
+        if (data)
+#define KEY_WALK_END \
+     }
+   const char *path = NULL, *key = NULL, *data = ""
+   int i = 0;
+
+   if (!strcmp(c->command, "meta-set"))
+     { // set x/y or other extra metadata
+        // meta-set path=/path/file.txt [xy=10,33] [comment=blah] ...
+        KEY_WALK_BEGIN {
+           if (!strcmp(key, "path")) path = data;
+           else meta_set(path, key, data);
+        } KEY_WALK_END
+     }
+   else if (!strcmp(c->command, "meta-del"))
+     { // delete a meta key
+        // meta-del path=/path/file.txt [meta=xy] [meta=comment] ...
+        KEY_WALK_BEGIN {
+           if (!strcmp(key, "path")) path = data;
+           else if (!strcmp(key, "meta")) meta_set(path, data, NULL);
+        } KEY_WALK_END
+     }
+   else if (!strcmp(c->command, "dir-set"))
      {
-        int i = 0;
-
-        for (i = 0; c->dict[i]; i += 2)
-          {
-             if (!strcmp(c->dict[i], "path")) _monitor(c->dict[i + 1]);
-          }
+        KEY_WALK_BEGIN {
+           if (!strcmp(key, "path")) _monitor(data);
+        } KEY_WALK_END
      }
 //   cmd_dump_sterr(c);
 }
@@ -795,13 +820,13 @@ do_init(int argc EINA_UNUSED, const char **argv EINA_UNUSED)
    icon_theme = getenv("E_ICON_THEME");
    config_dir = getenv("E_HOME_DIR");
    home_dir = getenv("HOME");
-   if (!home_dir) return 0;
+   if (!home_dir) return 77; // no $HOME? definitely an error!
    if (!config_dir)
      {
         char buf[PATH_MAX];
 
         snprintf(buf, sizeof(buf), "%s/.e/e", home_dir);
-        home_dir = eina_stringshare_add(buf);
+        config_dir = eina_stringshare_add(buf);
      }
 
    // maximum number of back-end thumbnailer slaves is num cores - 2
@@ -810,13 +835,12 @@ do_init(int argc EINA_UNUSED, const char **argv EINA_UNUSED)
    // core will run 14, 32 core will run 30 etc. - this can get overidden
    // by env var of course
    s = getenv("E_THUMB_MAX");
-   if (s)
-     {
-        thumb_busy_max = atoi(s);
-     }
+   if (s) thumb_busy_max = atoi(s);
    else thumb_busy_max = eina_cpu_count() - 2;
    if (thumb_busy_max < 1) thumb_busy_max = 1;
 
+   meta_init(config_dir);
+
    // we want to listen for when thumbnails slaves finish
    thumb_exe_del_handler = ecore_event_handler_add(ECORE_EXE_EVENT_DEL,
                                                    _cb_thumb_exe_del, NULL);
@@ -832,6 +856,8 @@ do_shutdown(void)
    if (mon) ecore_file_monitor_del(mon);
    mon = NULL;
 
+   meta_shutdown();
+
    efreet_mime_shutdown();
    efreet_shutdown();
    ecore_file_shutdown();
diff --git a/src/efm/efm.c b/src/efm/efm.c
index ed1be02..1a68cb6 100644
--- a/src/efm/efm.c
+++ b/src/efm/efm.c
@@ -980,6 +980,64 @@ _relayout_icons(Smart_Data *sd)
    evas_object_size_hint_min_set(sd->o_smart, sd->icon_min_w, minh);
 }
 
+static void
+_relayout_icons_custom(Smart_Data *sd)
+{ // wall all blocks and icons in blocks and assign them geometry
+   Eina_List *bl, *il;
+   Block *block;
+   Icon *icon;
+   Evas_Coord x, y, minw, minh;
+
+   // this is a row by row top-left to bottom-right layout
+   x = y = 0;
+   minw = minh = 0;
+   EINA_LIST_FOREACH(sd->blocks, bl, block)
+     {
+        EINA_LIST_FOREACH(block->icons, il, icon)
+          { // icon is at urrent x,y
+             icon->geom.x = rand() % 1000;
+             icon->geom.y = rand() % 1000;
+             icon->geom.w = sd->icon_min_w;
+             icon->geom.h = sd->icon_min_h;
+             x += icon->geom.w;
+             if ((x + icon->geom.w) > sd->geom.w)
+               { // if next icon over end of row, start a new row
+                  x = 0;
+                  y += icon->geom.h;
+               }
+             if (block->icons == il)
+               { // first icon in block - block bounds == icon geom
+                  block->bounds.x = icon->geom.x;
+                  block->bounds.y = icon->geom.y;
+                  block->bounds.w = icon->geom.w;
+                  block->bounds.h = icon->geom.h;
+               }
+             else
+               { // expand block bounds based on icon geom if it needs to
+                  if (icon->geom.x < block->bounds.x)
+                    block->bounds.x = icon->geom.x;
+                  if (icon->geom.y < block->bounds.y)
+                    block->bounds.y = icon->geom.y;
+                  if ((icon->geom.x + icon->geom.w) >
+                      (block->bounds.x + block->bounds.w))
+                    block->bounds.w = icon->geom.x +
+                    icon->geom.w - block->bounds.x;
+                  if ((icon->geom.y + icon->geom.h) >
+                      (block->bounds.y + block->bounds.h))
+                    block->bounds.h = icon->geom.y +
+                    icon->geom.h - block->bounds.y;
+               }
+          }
+        // adjust view minw/h if block expands it
+        if ((block->bounds.x + block->bounds.w) > minw)
+          minw = block->bounds.x + block->bounds.w;
+        if ((block->bounds.y + block->bounds.h) > minh)
+          minh = block->bounds.y + block->bounds.h;
+     }
+   // set min size for scroller to know content size
+   evas_object_size_hint_min_set(sd->o_smart, minw, minh);
+}
+
 static void
 _relayout_list(Smart_Data *sd)
 { // wall all blocks and icons in blocks and assign them geometry
@@ -1054,6 +1112,8 @@ _relayout(Smart_Data *sd)
    // XXX: list layout
    if (sd->config.view_mode == EFM_VIEW_MODE_ICONS)
      _relayout_icons(sd);
+   else if (sd->config.view_mode == EFM_VIEW_MODE_ICONS_CUSTOM)
+     _relayout_icons_custom(sd);
    else if (sd->config.view_mode == EFM_VIEW_MODE_LIST)
      _relayout_list(sd);
    else if (sd->config.view_mode == EFM_VIEW_MODE_LIST_DETAILED)

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.

Reply via email to