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.