Import src

This commit is contained in:
Carsten Haitzler 2023-06-23 09:02:36 +01:00
parent c6124b2180
commit 2958defd9e
43 changed files with 9104 additions and 0 deletions

25
COPYING Normal file
View File

@ -0,0 +1,25 @@
Copyright notice for Enlightenment:
Copyright (C) 2022-2023 Carsten Haitzler and various contributors (see AUTHORS)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

9
README.md Normal file
View File

@ -0,0 +1,9 @@
# EFM
## Remake of Enlightenment's file manager
This redose EFM so it can be used in and out-of-process too in E. This
moves the FS layer entirely to a child process that EFM talks to over
stdio. The only direct file access is via local "cached" files in
$HOME that are intended to always be fast access (the location could
move somewhere - but this is the only sane way).

81
TODO.md Normal file
View File

@ -0,0 +1,81 @@
# Things To Do
## Now
* View
* List detailed mode
* Free x/y position
* Vertical icon view
* Free x/y cleanup/grid align
* Icon with no labels
* Icons with flush view (like rage video browser view)
* .dir.desktop support for changing view type and looks etc. etc.
* DND auto-open dir on hover-over
* File properties dialog
* File Ops
* mv
* cp
* rm
* "Job" tracking + management (cancel pending ones)
* Trashcan impl
* Progress feedback from file ops
* Display dir usage (# files, size)
* Display dir + subdir usage
* Each file/dir needs a busy status (eg while running du -s in a dir tree)
* In-fm
* Status - icon overlay label, icon, progress, busy spinner
* Status - progress/busy spinner + label + icon
* Dialog - label + icon + entry + buttons (ok/cancel etc.)
* Typebuf select/filter/cmd
* Choose fs abstraction dir setup etc.
* Live icons - desktop file runs binary with plug/socket efl app
* Filesystem info (df like with total available)
* List mode ".order" files
* Remember scroll pos, view size/pos
* Set window icon correctly for dir
* Save x,y per icon
* Set custom icon per file
* Special icons for special filenames/paths (~/Desktop, ~/Videos etc.)
* Favorites view move (manual .order changes, cb's for selecting single click)
* Single click/select mode
* Hide/show hidden files
* Immutable dirs (no moving files/changes - just browse/launch)
* Dir info pane
* Pane
* Table of label + icons
* List of items (label + 2 icons)
* Device monitoring/listing
* Device mount/unmount
* Custom target vfs dir (eg sshfs) controls
* Find filename in tree
* Show dir has no permission to view/go into (eg missing r/x on other/group)
* Right click context menu
* Open file with mime handler
* Open with ...
* File actions (separate to open with...)
## Medium term
* Edit desktop file support (dialog)
* Network fs monitoring/listing (smb/nfs/etc.)
* View background / overlay images/edj files
* Typebuf commands (ls, rm, cd, mv, ...)
* Special drop handling per dir (eg favorites adds links)
* Support running back-end cmds as another user
* Chmod support
* Thumbnail multiple pages from pdf, ps etc.
* Thumbnails multiple timepoints from video files
* Tooltip previews
* Multi-page show multiple pages
* Video files show multiple timepoints
## Long term
* Encrypted drives/volumes
* Partitioning and formatting tool (chnage volume labels etc)
* Encryption added to partitioning/formatitng tool
* Lvm support in partitioning/formatting tool
* Fstrim support
* Smart support for devices
* Badblocks support
* Lsattr/chattr support

0
data/checkme Normal file
View File

3
data/meson.build Normal file
View File

@ -0,0 +1,3 @@
dir = join_paths(dir_data, 'efm')
install_data('checkme',
install_dir: dir)

37
meson.build Normal file
View File

@ -0,0 +1,37 @@
project('efm', 'c', version: '0.0.0', license: 'GPL2',
default_options: [ 'buildtype=plain', 'c_std=gnu99' ],
meson_version: '>= 0.47.0')
proj = meson.project_name()
ver = meson.project_version()
base_url = 'https://www.enlightenment.org/about-'
cc = meson.get_compiler('c')
m_dep = cc.find_library('m', required : false)
deps = [
dependency('elementary', version: '>= 1.26.0'),
dependency('emotion', version: '>= 1.26.0'),
m_dep
]
dir_prefix = get_option('prefix')
dir_bin = join_paths(dir_prefix, get_option('bindir'))
dir_lib = join_paths(dir_prefix, get_option('libdir'))
dir_data = join_paths(dir_prefix, get_option('datadir'))
dir_locale = join_paths(dir_prefix, get_option('localedir'))
cfg = configuration_data()
cfg.set_quoted('PACKAGE' , proj)
cfg.set_quoted('PACKAGE_NAME' , proj)
cfg.set_quoted('PACKAGE_VERSION' , ver)
cfg.set_quoted('PACKAGE_STRING' , proj + ' ' + ver)
cfg.set_quoted('PACKAGE_URL' , base_url + proj)
cfg.set_quoted('PACKAGE_BIN_DIR' , dir_bin)
cfg.set_quoted('PACKAGE_LIB_DIR' , dir_lib)
cfg.set_quoted('PACKAGE_DATA_DIR' , join_paths(dir_data, proj))
cfg.set_quoted('LOCALEDIR' , dir_locale)
cfg.set ('_GNU_SOURCE' , 1)
cfg.set ('__EXTENSIONS__' , 1)
cfg.set ('_POSIX_PTHREAD_SEMANTICS', 1)
cfg.set ('_ALL_SOURCE' , 1)
cfg.set ('_POSIX_SOURCE' , 1)
cfg.set ('_POSIX_1_SOURCE' , 1)
configure_file(output: 'efm_config.h', configuration: cfg)
subdir('src')
subdir('data')

65
sample-open-stdio.txt Normal file
View File

@ -0,0 +1,65 @@
CMD dir-set path=/home/raster/C/efm2
CMD file-add path=/home/raster/C/efm2/DSC00943.JPG type=file mime=image/jpeg mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/image-jpeg.svg thumb=/home/raster/.e/e/thumbs/53/f957ab4d5460f00628a587556e35629caa0e53.eet label=DSC00943.JPG user=raster group=raster mode=1ed inode=18749257 nlink=1 gid=1000 size=26574848 blksize=4096 blocks=51904 atime=1646005748 mtime=1645991014 ctime=1646005748
CMD file-add path=/home/raster/C/efm2/efm type=file label=efm user=raster group=raster mode=1ed inode=23724617 nlink=1 gid=1000 size=537192 blksize=4096 blocks=1056 atime=1648769404 mtime=1648769404 ctime=1648769404
CMD file-add path=/home/raster/C/efm2/DSC00932.JPG type=file mime=image/jpeg mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/image-jpeg.svg thumb=/home/raster/.e/e/thumbs/6e/cb788e0e6e03c5bcc98dcfbde5ea7449ced476.eet label=DSC00932.JPG user=raster group=raster mode=1ed inode=18749251 nlink=1 gid=1000 size=18448384 blksize=4096 blocks=36032 atime=1646005748 mtime=1645991014 ctime=1646005748
CMD file-add path=/home/raster/C/efm2/cmd.c\x7e type=file mime=application/x-trash mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/application-x-trash.svg label=cmd.c\x7e user=raster group=raster mode=1a4 inode=23729908 nlink=1 gid=1000 size=6155 blksize=4096 blocks=16 atime=1648747518 mtime=1648747518 ctime=1648747544
CMD file-add path=/home/raster/C/efm2/dir1 type=dir label=dir1 user=raster group=raster mode=1ed inode=23732294 nlink=2 gid=1000 size=4096 blksize=4096 blocks=8 atime=1644011527 mtime=1644011527 ctime=1644011527
CMD file-add path=/home/raster/C/efm2/cmd.h type=file mime=text/x-chdr mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/text-x-chdr.svg label=cmd.h user=raster group=raster mode=1a4 inode=23729901 nlink=1 gid=1000 size=980 blksize=4096 blocks=8 atime=1648747535 mtime=1648747535 ctime=1648747535
CMD file-add path=/home/raster/C/efm2/build.sh type=file mime=application/x-shellscript mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/application-x-shellscript.svg label=build.sh user=raster group=raster mode=1ed inode=23724920 nlink=1 gid=1000 size=323 blksize=4096 blocks=8 atime=1646442369 mtime=1646442369 ctime=1646442369
CMD file-add path=/home/raster/C/efm2/mplayer.desktop type=file mime=application/x-desktop mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/application-x-desktop.svg label=MPlayer desktop-generic-name=true desktop-comment=Watch\x20movies\x20and\x20videos desktop-icon=video_player desktop-try-exec= desktop-exec=mplayer\x20\x25U desktop-url= desktop-no-display=false desktop-hidden=false desktop-terminal=false desktop-startup-notify=false desktop-icon.lookup=/usr/local/share/enlightenment/data/icons/video_player.png user=raster group=raster mode=1a4 inode=23724916 nlink=1 gid=1000 size=2404 blksize=4096 blocks=8 atime=1645890199 mtime=1645890199 ctime=1645890199
CMD file-add path=/home/raster/C/efm2/DSC00945.JPG type=file mime=image/jpeg mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/image-jpeg.svg thumb=/home/raster/.e/e/thumbs/14/c9fbb4214ab4b8a622025509fdd38c8fa276b8.eet label=DSC00945.JPG user=raster group=raster mode=1ed inode=18749262 nlink=1 gid=1000 size=26116096 blksize=4096 blocks=51008 atime=1646005748 mtime=1645991014 ctime=1646005748
CMD file-add path=/home/raster/C/efm2/sha.c type=file mime=text/x-csrc mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/text-x-csrc.svg label=sha.c user=raster group=raster mode=1a4 inode=23727830 nlink=1 gid=1000 size=3663 blksize=4096 blocks=8 atime=1645965283 mtime=1645965283 ctime=1645965283
CMD file-add path=/home/raster/C/efm2/dir2 type=dir label=dir2 user=raster group=raster mode=1ed inode=23732493 nlink=2 gid=1000 size=4096 blksize=4096 blocks=8 atime=1644011530 mtime=1644011530 ctime=1644011530
CMD file-add path=/home/raster/C/efm2/main.c type=file mime=text/x-csrc mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/text-x-csrc.svg label=main.c user=raster group=raster mode=1a4 inode=23729913 nlink=1 gid=1000 size=3752 blksize=4096 blocks=8 atime=1648762748 mtime=1648762748 ctime=1648762748
CMD file-add path=/home/raster/C/efm2/DSC00946.JPG type=file mime=image/jpeg mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/image-jpeg.svg thumb=/home/raster/.e/e/thumbs/99/641dd8d01cb0f212f4295cea418fe4637835b5.eet label=DSC00946.JPG user=raster group=raster mode=1ed inode=18749264 nlink=1 gid=1000 size=23396352 blksize=4096 blocks=45696 atime=1646005748 mtime=1645991014 ctime=1646005748
CMD file-add path=/home/raster/C/efm2/blah type=link link=/home/raster/ link-type=dir link-label=blah link-user=raster link-group=raster link-mode=1ed link-inode=18219010 link-nlink=116 link-gid=1000 link-size=12288 link-blksize=4096 link-blocks=24 link-atime=1586903708 link-mtime=1648890971 link-ctime=1648890971 user=raster group=raster mode=1ff inode=23724046 nlink=1 gid=1000 size=13 blksize=4096 blocks=0 atime=1643917174 mtime=1643906454 ctime=1643917174
CMD file-add path=/home/raster/C/efm2/yyb type=dir label=yyb user=raster group=raster mode=1ed inode=23732491 nlink=2 gid=1000 size=4096 blksize=4096 blocks=8 atime=1643917174 mtime=1643909696 ctime=1643917174
CMD file-add path=/home/raster/C/efm2/efm_structs.h type=file mime=text/x-chdr mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/text-x-chdr.svg label=efm_structs.h user=raster group=raster mode=1a4 inode=23726366 nlink=1 gid=1000 size=3546 blksize=4096 blocks=8 atime=1648764496 mtime=1648764496 ctime=1648764496
CMD file-add path=/home/raster/C/efm2/xxa type=file label=xxa user=raster group=raster mode=1a4 inode=18770119 nlink=1 gid=1000 size=0 blksize=4096 blocks=0 atime=1643917175 mtime=1643908180 ctime=1645954000
CMD file-add path=/home/raster/C/efm2/xxb type=file label=xxb user=raster group=raster mode=1a4 inode=18770120 nlink=1 gid=1000 size=0 blksize=4096 blocks=0 atime=1643917175 mtime=1643908180 ctime=1645954000
CMD file-add path=/home/raster/C/efm2/yyc type=dir label=yyc user=raster group=raster mode=1ed inode=23732492 nlink=2 gid=1000 size=4096 blksize=4096 blocks=8 atime=1643917174 mtime=1643909696 ctime=1643917174
CMD file-add path=/home/raster/C/efm2/Sam_Perry_-_Prince_-_When_doves_cry.mkv type=file mime=video/x-matroska mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/video-x-matroska.svg thumb=/home/raster/.e/e/thumbs/c5/bd5935e1f3f395d53a576e48aec7e39110a2ec.eet label=Sam_Perry_-_Prince_-_When_doves_cry.mkv user=raster group=raster mode=1a4 inode=23724895 nlink=1 gid=1000 size=46915391 blksize=4096 blocks=91640 atime=1646086494 mtime=1646086494 ctime=1647031857
CMD file-add path=/home/raster/C/efm2/xxc type=file label=xxc user=raster group=raster mode=1a4 inode=18770121 nlink=1 gid=1000 size=0 blksize=4096 blocks=0 atime=1643917175 mtime=1643908180 ctime=1645954000
CMD file-add path=/home/raster/C/efm2/efm_private.h\x7e type=file mime=application/x-trash mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/application-x-trash.svg label=efm_private.h\x7e user=raster group=raster mode=1a4 inode=23729902 nlink=1 gid=1000 size=1193 blksize=4096 blocks=8 atime=1648752425 mtime=1648752425 ctime=1648762883
CMD file-add path=/home/raster/C/efm2/efm_util.c type=file mime=text/x-csrc mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/text-x-csrc.svg label=efm_util.c user=raster group=raster mode=1a4 inode=23729811 nlink=1 gid=1000 size=31886 blksize=4096 blocks=64 atime=1648769288 mtime=1648769288 ctime=1648769288
CMD file-add path=/home/raster/C/efm2/efm_icon.c type=file mime=text/x-csrc mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/text-x-csrc.svg label=efm_icon.c user=raster group=raster mode=1a4 inode=23730949 nlink=1 gid=1000 size=11078 blksize=4096 blocks=24 atime=1648209986 mtime=1648209986 ctime=1648209986
CMD file-add path=/home/raster/C/efm2/DSC00929.JPG type=file mime=image/jpeg mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/image-jpeg.svg thumb=/home/raster/.e/e/thumbs/3f/4a8b9b2c79314c676db1ea0d29136058d5aaab.eet label=DSC00929.JPG user=raster group=raster mode=1ed inode=18749248 nlink=1 gid=1000 size=14516224 blksize=4096 blocks=28352 atime=1646005748 mtime=1645991014 ctime=1646005748
CMD file-add path=/home/raster/C/efm2/efm_back_end.c type=file mime=text/x-csrc mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/text-x-csrc.svg label=efm_back_end.c user=raster group=raster mode=1a4 inode=18746021 nlink=1 gid=1000 size=15235 blksize=4096 blocks=32 atime=1647210250 mtime=1647206422 ctime=1647210250
CMD file-add path=/home/raster/C/efm2/sort.h type=file mime=text/x-chdr mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/text-x-chdr.svg label=sort.h user=raster group=raster mode=1a4 inode=23730766 nlink=1 gid=1000 size=538 blksize=4096 blocks=8 atime=1643992534 mtime=1643992534 ctime=1643992534
CMD file-add path=/home/raster/C/efm2/thumb.c type=file mime=text/x-csrc mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/text-x-csrc.svg label=thumb.c user=raster group=raster mode=1a4 inode=23724123 nlink=1 gid=1000 size=7007 blksize=4096 blocks=16 atime=1646085700 mtime=1646085700 ctime=1646085700
CMD file-add path=/home/raster/C/efm2/efm_private.h type=file mime=text/x-chdr mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/text-x-chdr.svg label=efm_private.h user=raster group=raster mode=1a4 inode=23728355 nlink=1 gid=1000 size=1213 blksize=4096 blocks=8 atime=1648762883 mtime=1648762883 ctime=1648762883
CMD file-add path=/home/raster/C/efm2/cmd.h\x7e type=file mime=application/x-trash mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/application-x-trash.svg label=cmd.h\x7e user=raster group=raster mode=1a4 inode=23724625 nlink=1 gid=1000 size=974 blksize=4096 blocks=8 atime=1648747530 mtime=1648747530 ctime=1648747535
CMD file-add path=/home/raster/C/efm2/fs_backend_core.c type=file mime=text/x-csrc mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/text-x-csrc.svg label=fs_backend_core.c user=raster group=raster mode=1a4 inode=23724929 nlink=1 gid=1000 size=2629 blksize=4096 blocks=8 atime=1646248242 mtime=1646248242 ctime=1646248242
CMD file-add path=/home/raster/C/efm2/.hidden1 type=dir label=.hidden1 user=raster group=raster mode=1ed inode=24512350 nlink=2 gid=1000 size=4096 blksize=4096 blocks=8 atime=1646596578 mtime=1646596578 ctime=1646596578
CMD file-add path=/home/raster/C/efm2/efm.h type=file mime=text/x-chdr mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/text-x-chdr.svg label=efm.h user=raster group=raster mode=1a4 inode=23730930 nlink=1 gid=1000 size=1079 blksize=4096 blocks=8 atime=1647632307 mtime=1647632307 ctime=1647632307
CMD file-add path=/home/raster/C/efm2/ark.desktop type=file mime=application/x-desktop mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/application-x-desktop.svg label=ARK:\x20Normal desktop-generic-name=false desktop-comment=Play\x20this\x20game\x20on\x20Steam desktop-icon=/home/raster/.icons/ark.png desktop-try-exec= desktop-exec=steam\x20steam://rungameid/346110 desktop-url= desktop-no-display=false desktop-hidden=false desktop-terminal=false desktop-startup-notify=false desktop-icon.lookup= label-clicked=Ark:\x20Clicked label-selected=Ark:\x20Selected desktop-icon-clicked=/home/raster/.icons/wechat.png desktop-icon-selected=/home/raster/.icons/sweethome3d.png user=raster group=raster mode=1a4 inode=23724926 nlink=1 gid=1000 size=319 blksize=4096 blocks=8 atime=1647026562 mtime=1647026562 ctime=1647026562
CMD file-add path=/home/raster/C/efm2/tig.desktop type=file mime=application/x-desktop mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/application-x-desktop.svg label=Tiggy\x20SVG desktop-generic-name=false desktop-comment=Play\x20this\x20game\x20on\x20Steam desktop-icon=/home/raster/C/efm2/tiger.svg desktop-try-exec= desktop-exec=steam\x20steam://rungameid/346110 desktop-url= desktop-no-display=false desktop-hidden=false desktop-terminal=false desktop-startup-notify=false desktop-icon.lookup= user=raster group=raster mode=1a4 inode=23727828 nlink=1 gid=1000 size=166 blksize=4096 blocks=8 atime=1646596358 mtime=1646596358 ctime=1646596358
CMD file-add path=/home/raster/C/efm2/DSC00931.JPG type=file mime=image/jpeg mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/image-jpeg.svg thumb=/home/raster/.e/e/thumbs/cd/fb485ec9e20bad55b5b6f76d3e8976eb438399.eet label=DSC00931.JPG user=raster group=raster mode=1ed inode=18749250 nlink=1 gid=1000 size=14024704 blksize=4096 blocks=27392 atime=1646005748 mtime=1645991014 ctime=1646005748
CMD file-add path=/home/raster/C/efm2/dir3 type=dir label=dir3 user=raster group=raster mode=1ed inode=23732494 nlink=2 gid=1000 size=4096 blksize=4096 blocks=8 atime=1644011532 mtime=1644011532 ctime=1644011532
CMD file-add path=/home/raster/C/efm2/xxd type=file label=xxd user=raster group=raster mode=1a4 inode=18770149 nlink=1 gid=1000 size=0 blksize=4096 blocks=0 atime=1643917175 mtime=1643908180 ctime=1645954000
CMD file-add path=/home/raster/C/efm2/efm.c type=file mime=text/x-csrc mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/text-x-csrc.svg label=efm.c user=raster group=raster mode=1a4 inode=23729823 nlink=1 gid=1000 size=28335 blksize=4096 blocks=56 atime=1648769134 mtime=1648769134 ctime=1648769134
CMD file-add path=/home/raster/C/efm2/DSC00928.JPG type=file mime=image/jpeg mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/image-jpeg.svg thumb=/home/raster/.e/e/thumbs/aa/bfc32b248723656c48a31d446999fd3f8913f9.eet label=DSC00928.JPG user=raster group=raster mode=1ed inode=18745989 nlink=1 gid=1000 size=22773760 blksize=4096 blocks=44480 atime=1646005748 mtime=1645991014 ctime=1646005748
CMD file-add path=/home/raster/C/efm2/cmd.c type=file mime=text/x-csrc mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/text-x-csrc.svg label=cmd.c user=raster group=raster mode=1a4 inode=23729897 nlink=1 gid=1000 size=6161 blksize=4096 blocks=16 atime=1648747544 mtime=1648747544 ctime=1648747544
CMD file-add path=/home/raster/C/efm2/efm.c\x7e type=file mime=application/x-trash mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/application-x-trash.svg label=efm.c\x7e user=raster group=raster mode=1a4 inode=23724064 nlink=1 gid=1000 size=28357 blksize=4096 blocks=56 atime=1648768644 mtime=1648768644 ctime=1648769134
CMD file-add path=/home/raster/C/efm2/DSC00941.JPG type=file mime=image/jpeg mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/image-jpeg.svg thumb=/home/raster/.e/e/thumbs/14/020bf44c8ddce48364d5ede7e9ee74e378f70a.eet label=DSC00941.JPG user=raster group=raster mode=1ed inode=18749252 nlink=1 gid=1000 size=14647296 blksize=4096 blocks=28608 atime=1646005748 mtime=1645991014 ctime=1646005748
CMD file-add path=/home/raster/C/efm2/beast.desktop type=file mime=application/x-desktop mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/application-x-desktop.svg label=BEEAAST desktop-generic-name=false desktop-comment=Da\x20Biiieest desktop-icon=/home/raster/C/efm2/beast-art.jpg desktop-try-exec= desktop-exec=xterm desktop-url= desktop-no-display=false desktop-hidden=false desktop-terminal=false desktop-startup-notify=false desktop-icon.lookup= user=raster group=raster mode=1a4 inode=23724921 nlink=1 gid=1000 size=122 blksize=4096 blocks=8 atime=1645890552 mtime=1645890552 ctime=1645890552
CMD file-add path=/home/raster/C/efm2/sort.c type=file mime=text/x-csrc mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/text-x-csrc.svg label=sort.c user=raster group=raster mode=1a4 inode=23729895 nlink=1 gid=1000 size=2789 blksize=4096 blocks=8 atime=1644017056 mtime=1644017056 ctime=1644017056
CMD file-add path=/home/raster/C/efm2/home.desktop type=file mime=application/x-desktop mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/application-x-desktop.svg label=Home desktop-generic-name=false desktop-comment=The\x20Directory\x20containing\x20all\x20your\x20personal\x20files desktop-icon=user-home desktop-try-exec= desktop-exec= desktop-url=file:\x24HOME desktop-no-display=false desktop-hidden=false desktop-terminal=false desktop-startup-notify=false desktop-icon.lookup=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/places/scalable/user-home.svg user=raster group=raster mode=1a4 inode=18770054 nlink=1 gid=1000 size=765 blksize=4096 blocks=8 atime=1643917175 mtime=1643906431 ctime=1643917175
CMD file-add path=/home/raster/C/efm2/beast-art.jpg type=file mime=image/jpeg mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/image-jpeg.svg thumb=/home/raster/.e/e/thumbs/9f/0fdd2433a4c6914e3685f6f203b0cab4342702.eet label=beast-art.jpg user=raster group=raster mode=1a4 inode=23724917 nlink=1 gid=1000 size=557108 blksize=4096 blocks=1096 atime=1645960418 mtime=1645960418 ctime=1645960418
CMD file-add path=/home/raster/C/efm2/main.c\x7e type=file mime=application/x-trash mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/application-x-trash.svg label=main.c\x7e user=raster group=raster mode=1a4 inode=18746023 nlink=1 gid=1000 size=3230 blksize=4096 blocks=8 atime=1647133165 mtime=1647120355 ctime=1648762748
CMD file-add path=/home/raster/C/efm2/DSC00930.JPG type=file mime=image/jpeg mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/image-jpeg.svg thumb=/home/raster/.e/e/thumbs/2a/e2b0646970fa380c2641db83d0bb64e10973db.eet label=DSC00930.JPG user=raster group=raster mode=1ed inode=18749249 nlink=1 gid=1000 size=19038208 blksize=4096 blocks=37184 atime=1646005748 mtime=1645991014 ctime=1646005748
CMD file-add path=/home/raster/C/efm2/cities.desktop type=file mime=application/x-desktop mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/application-x-desktop.svg label=City4 desktop-generic-name=false desktop-comment=Play\x20this\x20game\x20on\x20Steam desktop-icon=steam_icon_255710 desktop-try-exec= desktop-exec=steam\x20steam://rungameid/255710 desktop-url= desktop-no-display=false desktop-hidden=false desktop-terminal=false desktop-startup-notify=false desktop-icon.lookup=/home/raster/.local/share/icons/hicolor/128x128/apps/steam_icon_255710.png user=raster group=raster mode=1a4 inode=23727046 nlink=1 gid=1000 size=167 blksize=4096 blocks=8 atime=1645911516 mtime=1645911516 ctime=1646086457
CMD file-add path=/home/raster/C/efm2/efm_icon.h type=file mime=text/x-chdr mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/text-x-chdr.svg label=efm_icon.h user=raster group=raster mode=1a4 inode=23724919 nlink=1 gid=1000 size=414 blksize=4096 blocks=8 atime=1646596453 mtime=1646596453 ctime=1646596453
CMD file-add path=/home/raster/C/efm2/sh-open type=file label=sh-open user=raster group=raster mode=1ed inode=18770068 nlink=1 gid=1000 size=296 blksize=4096 blocks=8 atime=1643917175 mtime=1643906431 ctime=1643917175
CMD file-add path=/home/raster/C/efm2/efm_util.c\x7e type=file mime=application/x-trash mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/application-x-trash.svg label=efm_util.c\x7e user=raster group=raster mode=1a4 inode=23724141 nlink=1 gid=1000 size=31835 blksize=4096 blocks=64 atime=1648768645 mtime=1648768645 ctime=1648769288
CMD file-add path=/home/raster/C/efm2/beast2.desktop type=file mime=application/x-desktop mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/application-x-desktop.svg label=BEEAAST2 desktop-generic-name=false desktop-comment=Da\x20Biiieest desktop-icon=/home/raster/C/efm2/beast-shot.gif desktop-try-exec= desktop-exec=xterm desktop-url= desktop-no-display=false desktop-hidden=false desktop-terminal=false desktop-startup-notify=false desktop-icon.lookup= user=raster group=raster mode=1a4 inode=23724922 nlink=1 gid=1000 size=117 blksize=4096 blocks=8 atime=1645890953 mtime=1645890953 ctime=1645890953
CMD file-add path=/home/raster/C/efm2/open.c type=file mime=text/x-csrc mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/text-x-csrc.svg label=open.c user=raster group=raster mode=1a4 inode=23726403 nlink=1 gid=1000 size=27651 blksize=4096 blocks=56 atime=1647027930 mtime=1647027930 ctime=1647027930
CMD file-add path=/home/raster/C/efm2/broken type=link broken-link=true user=raster group=raster mode=1ff inode=23724048 nlink=1 gid=1000 size=7 blksize=4096 blocks=0 atime=1643917174 mtime=1643906443 ctime=1643917174
CMD file-add path=/home/raster/C/efm2/beast-shot.gif type=file mime=image/gif mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/image-gif.svg thumb=/home/raster/.e/e/thumbs/b2/9787354aa1c33816b2a53d5a85542d751f81d4.eet label=beast-shot.gif user=raster group=raster mode=1a4 inode=23724918 nlink=1 gid=1000 size=18854 blksize=4096 blocks=40 atime=1645890492 mtime=1645890492 ctime=1645954000
CMD file-add path=/home/raster/C/efm2/order.pdf type=file mime=application/pdf mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/categories/scalable/application-pdf.svg thumb=/home/raster/.e/e/thumbs/37/bd6ec0ae6bfaa4035c40efaf73fcfbfcf57045.eet label=order.pdf user=raster group=raster mode=1a4 inode=23724126 nlink=1 gid=1000 size=184544 blksize=4096 blocks=368 atime=1646596700 mtime=1646596700 ctime=1647031857
CMD file-add path=/home/raster/C/efm2/efm_structs.h\x7e type=file mime=application/x-trash mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/application-x-trash.svg label=efm_structs.h\x7e user=raster group=raster mode=1a4 inode=23730938 nlink=1 gid=1000 size=3510 blksize=4096 blocks=8 atime=1648489751 mtime=1648489751 ctime=1648764496
CMD file-add path=/home/raster/C/efm2/open type=file label=open user=raster group=raster mode=1ed inode=23724049 nlink=1 gid=1000 size=283680 blksize=4096 blocks=560 atime=1648769403 mtime=1648769403 ctime=1648769403
CMD file-add path=/home/raster/C/efm2/yya type=dir label=yya user=raster group=raster mode=1ed inode=23732295 nlink=2 gid=1000 size=4096 blksize=4096 blocks=8 atime=1643917174 mtime=1643909696 ctime=1643917174
CMD file-add path=/home/raster/C/efm2/ark.png type=file mime=image/png mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/image-png.svg thumb=/home/raster/.e/e/thumbs/d9/0f5094c9a5019a065dbc42ca9db28f56441836.eet label=ark.png user=raster group=raster mode=1a4 inode=23724913 nlink=1 gid=1000 size=65829 blksize=4096 blocks=136 atime=1646596853 mtime=1646596895 ctime=1647031857
CMD file-add path=/home/raster/C/efm2/thumb type=file label=thumb user=raster group=raster mode=1ed inode=23724056 nlink=1 gid=1000 size=399696 blksize=4096 blocks=784 atime=1648769403 mtime=1648769404 ctime=1648769404
CMD file-add path=/home/raster/C/efm2/tiger.svg type=file mime=image/svg+xml mime-icon=/home/raster/.local/share/icons/Flat-Remix-Yellow-Dark/mimetypes/scalable/image-svg+xml.svg thumb=/home/raster/.e/e/thumbs/28/d53be4c4a7bc03dde866cc2a189ebc40b62648.eet label=tiger.svg user=raster group=raster mode=1a4 inode=18219382 nlink=1 gid=1000 size=86486 blksize=4096 blocks=176 atime=1646577034 mtime=1646577034 ctime=1648769263

View File

@ -0,0 +1,130 @@
// this is a template file to drive any fs backend that sets up stdin/out
// and the core parsers and init/shutdown funcs. the expectation is you build
// this along with an implementation file that implements that fs
#include <Eina.h>
#include <Ecore.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include "cmd.h"
void do_handle_cmd(Cmd *c);
int do_init(int argc, const char **argv);
void do_shutdown(void);
static Ecore_Fd_Handler *fdh = NULL;
static Eina_Strbuf *strbuf = NULL;
static Eina_Bool
_cb_stdio_in_read(void *data EINA_UNUSED, Ecore_Fd_Handler *fd_handler EINA_UNUSED)
{
ssize_t ret;
char buf[4096 + 1];
errno = 0;
ret = read(0, buf, sizeof(buf) - 1);
if (ret < 1)
{
int e = errno;
if ((e == EIO) || (e == EBADF) || (e == EPIPE) || (e == EINVAL) ||
(e == ENOSPC) || (!((e == EAGAIN) || (e == EINTR))))
{
ecore_main_loop_quit();
goto done;
}
goto done;
}
else
{
const char *nl, *str;
buf[ret] = '\0';
eina_strbuf_append(strbuf, buf);
for (;;)
{
char *s;
Cmd *c;
str = eina_strbuf_string_get(strbuf);
nl = strchr(str, '\n');
if (!nl) break;
s = strndup(str, nl - str);
if (!s) break;
c = cmd_parse(s);
if (c)
{
do_handle_cmd(c);
cmd_free(c);
}
free(s);
eina_strbuf_remove(strbuf, 0, nl - str + 1);
}
}
done:
return EINA_TRUE;
}
static void
_init(void)
{
strbuf = eina_strbuf_new();
if (!strbuf)
{
fprintf(stderr, "ERR: Can't allocate strbuf\n");
goto err;
}
if (fcntl(0, F_SETFL, O_NONBLOCK) != 0)
{
fprintf(stderr, "ERR: Can't set stdin to O_NONBLOCK\n");
goto err;
}
fdh = ecore_main_fd_handler_add(0, ECORE_FD_READ, _cb_stdio_in_read,
NULL, NULL, NULL);
if (!fdh) goto err;
return;
err:
exit(-1);
}
static void
_shutdown(void)
{
ecore_main_fd_handler_del(fdh);
fdh = NULL;
eina_strbuf_free(strbuf);
strbuf = NULL;
}
int
main(int argc, const char **argv)
{
int ret;
eina_init();
ecore_init();
_init();
ret = do_init(argc, argv);
if (ret == 0)
{
ecore_main_loop_begin();
do_shutdown();
}
_shutdown();
ecore_shutdown();
eina_shutdown();
return ret;
}

View File

@ -0,0 +1,36 @@
dir = join_paths(dir_lib, 'efm', 'backends', 'default')
inc = include_directories(
'.',
'../../..',
'../../efm',
'../../shared/commands',
'../../shared/common',
'../../backends/common'
)
executable('open', [
'../../shared/common/cmd.c',
'../../shared/common/sha.c',
'../../backends/common/fs_backend_core.c',
'open.c'
],
include_directories: inc,
dependencies: deps,
install: true,
install_dir: dir)
executable('thumb', [
'../../shared/common/sha.c',
'thumb.c',
'thumb_util_img.c',
'thumb_util_search.c',
'thumb_util_url.c',
'thumb_image.c',
'thumb_font.c',
'thumb_paged.c',
'thumb_music.c',
'thumb_video.c',
'thumb_edje.c'
],
include_directories: inc,
dependencies: deps,
install: true,
install_dir: dir)

838
src/backends/default/open.c Normal file
View File

@ -0,0 +1,838 @@
// fs backend for regular fs files - opens a dir, lists, monitors and
// sends all files it finds nd all metadata and any changes that happen
// to that dir. it only handles a single dir. in theory you can write complex
// fs handlers that could do this over a network protocol or merge multiple
// real fs locations into a single apparent dir to the user. the idea is
// the front end that consumes the output of this should do NO file access
// itself at all and do everything via fs handlers for open, delete, rename
// copy, import, export etc.
#include "cmd.h"
#include "sha.h"
#include <Ecore_File.h>
#include <Efreet.h>
#include <Efreet_Mime.h>
#include <Eet.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#include "thumb_check.h"
static const char *icon_theme = NULL;
static const char *config_dir = NULL;
static const char *home_dir = NULL;
static Ecore_File_Monitor *mon = NULL;
typedef struct
{
const char *path;
const char *mime;
const char *thumb;
Ecore_Exe *exe;
Ecore_Timer *busy_delay_timer;
} Thumb;
static Ecore_Event_Handler *thumb_exe_del_handler = NULL;
static Eina_List *thumb_queue = NULL;
static Eina_List *thumb_busy_queue = NULL;
static unsigned int thumb_busy_max = 8;
static double thumb_update_delay = 0.25;
static Eina_Bool _file_add_mod_info(Eina_Strbuf *strbuf, const char *path, Eina_Bool delay);
static void
_strbuf_append_file_escaped(Eina_Strbuf *strbuf, const char *path)
{ // append a filename and escape special chars for cmdline args
const char *s;
for (s = path; *s; s++)
{
// if it's a special char - escale it
if ((*s == ' ') || (*s == '\t') || (*s == '\n') || (*s == '\\') ||
(*s == '\'') || (*s == '\"') || (*s == ';') || (*s == '!') ||
(*s == '#') || (*s == '$') || (*s == '%') || (*s == '&') ||
(*s == '*') || (*s == '(') || (*s == ')') || (*s == '[') ||
(*s == ']') || (*s == '{') || (*s == '}') || (*s == '|') ||
(*s == '<') || (*s == '>') || (*s == '?'))
eina_strbuf_append_char(strbuf, '\\');
eina_strbuf_append_char(strbuf, *s);
}
}
static void
_file_thumb_flush(void)
{ // while count of busy list < max, pick from queue and launch ecore exe
Thumb *th;
Eina_List *l;
Eina_Strbuf *strbuf;
const char *dir = getenv("EFM_BACKEND_DIR");
if (!dir)
{
fprintf(stderr, "EFM_BACKEND_DIR not set to path to backend dirs\n");
abort();
}
strbuf = eina_strbuf_new();
if (!strbuf) return;
// run up to thumb_busy_max thumbnailers in the background. this is
// set in the init func.
while (eina_list_count(thumb_busy_queue) < thumb_busy_max)
{
if (!thumb_queue) break;
// find first item in out queue that is noy delaying until it is busy
EINA_LIST_FOREACH(thumb_queue, l, th)
{
if (!th->busy_delay_timer) break;
th = NULL;
}
if (!th) break; // we didn't find anything that is not busy sleeping
// remove from queue and put on busys queue
thumb_queue = eina_list_remove_list(thumb_queue, l);
thumb_busy_queue = eina_list_append(thumb_busy_queue, th);
// build thumb command:
// thumb FILE_PATH MIME THUMBNAIL_PATH
// e.g.
// thumb /path/to/f.jpg image/jpeg /home/u/.e/e/thumbs/f8/2a18.eet
eina_strbuf_reset(strbuf);
eina_strbuf_append(strbuf, getenv("EFM_BACKEND_DIR"));
eina_strbuf_append(strbuf, "/thumb ");
_strbuf_append_file_escaped(strbuf, th->path);
eina_strbuf_append_char(strbuf, ' ');
_strbuf_append_file_escaped(strbuf, th->mime);
eina_strbuf_append_char(strbuf, ' ');
_strbuf_append_file_escaped(strbuf, th->thumb);
th->exe = ecore_exe_run(eina_strbuf_string_get(strbuf), th);
}
eina_strbuf_free(strbuf);
}
static Eina_Bool
_file_thumb(const char *path EINA_UNUSED, const char *mime)
{ // return true if we should have/generate a thumb for this
return check_thumb_any(path, mime);
}
static char *
_file_thumb_find(const char *path, const char *mime EINA_UNUSED)
{ // find the thumb file
unsigned char sha[20];
char buf[PATH_MAX], shastr[41];
eina_sha1((const unsigned char *)path, strlen(path), sha);
sha1_str(sha, shastr);
snprintf(buf, sizeof(buf), "%s/thumbs/%c%c/%s.eet", config_dir,
shastr[0], shastr[1], shastr + 2);
return strdup(buf);
}
static Eina_Bool
_cb_thumb_delay(void *data)
{ // when a delay timer expires - flush the queue to the busy queue
Thumb *th = data;
th->busy_delay_timer = NULL;
_file_thumb_flush();
return EINA_FALSE;
}
static void
_file_thumb_queue(const char *path, const char *mime, Eina_Bool delay)
{ // append a thumb generation item to the queue with a delay timer
Thumb *th = calloc(1, sizeof(Thumb));
char *thumb;
if (!th) return;
thumb = _file_thumb_find(path, mime);
if (!thumb)
{
free(th);
return;
}
th->path = eina_stringshare_add(path);
th->mime = eina_stringshare_add(mime);
th->thumb = eina_stringshare_add(thumb);
if (delay)
th->busy_delay_timer = ecore_timer_add(thumb_update_delay,
_cb_thumb_delay, th);
thumb_queue = eina_list_append(thumb_queue, th);
free(thumb);
}
static void
_file_thumb_gen(const char *path, const char *mime, Eina_Bool delay)
{ // queue path + mime and kick off queue porcessor if idle
Eina_List *l;
Thumb *th;
Eina_Bool found = EINA_FALSE;
// if already on queue - just moved to back
EINA_LIST_FOREACH(thumb_queue, l, th)
{
if (!strcmp(th->path, path))
{
// move to end of queue
thumb_queue = eina_list_demote_list(thumb_queue, l);
// reset timer to start again
ecore_timer_reset(th->busy_delay_timer);
found = EINA_TRUE;
break;
}
}
if (!found) _file_thumb_queue(path, mime, delay);
_file_thumb_flush();
}
static Eina_Bool
_cb_thumb_exe_del(void *data EINA_UNUSED, int ev_type EINA_UNUSED, void *event)
{ // thumb slave exited - exit code 0 == all ok
Ecore_Exe_Event_Del *ev = event;
Thumb *th;
Eina_List *l;
// find exe in our busy queue - if it is there
EINA_LIST_FOREACH(thumb_busy_queue, l, th)
{
if (th->exe != ev->exe) continue; // it's not our desired exe - next
// remove this thumb from the busy list
thumb_busy_queue = eina_list_remove_list(thumb_busy_queue, l);
if (ev->exit_code == 0)
{ // thumb generation was all ok so send a file update
Eina_Strbuf *strbuf = cmd_strbuf_new("file-mod");
cmd_strbuf_append(strbuf, "path", th->path);
if (!_file_add_mod_info(strbuf, th->path, EINA_FALSE))
eina_strbuf_free(strbuf);
else
cmd_strbuf_print_consume(strbuf);
}
// free up this thumb in our busy queue as we're done with it
eina_stringshare_del(th->path);
eina_stringshare_del(th->mime);
eina_stringshare_del(th->thumb);
free(th);
// we may have spare spots on our busy queue so flush to it
_file_thumb_flush();
break;
}
return ECORE_CALLBACK_PASS_ON; // let others handle this exe if they want
}
static void
_file_thumb_handle(Eina_Strbuf *strbuf, const char *path, const char *mime, struct stat *st, Eina_Bool delay)
{ // handle finding the thumb of file and checking it's valid etc.
char *thumb;
// does this file type do/need thumbnails
if (!_file_thumb(path, mime)) return;
// get what the path to the target thumb should be
thumb = _file_thumb_find(path, mime);
if (thumb)
{ // open the thumb and let's see if the stat info is up to date
Eet_File *ef = eet_open(thumb, EET_FILE_MODE_READ);
if (ef)
{ // thumb file exists - check meta data
int size = 0;
unsigned char statsha1[20];
unsigned char *origstatsha1;
Eina_Bool ok = EINA_FALSE;
// sha1 the relevant stat data at this point
sha1_stat(st, statsha1);
// load the stored sha1 of stat data from the thumb file
origstatsha1 = eet_read(ef, "orig/stat/sha1", &size);
if ((origstatsha1) && (size == 20))
{
if (!memcmp(statsha1, origstatsha1, 20))
ok = EINA_TRUE; // sha1 of stat data matches - ok
}
eet_close(ef);
if (!ok)
{ // thumb stat data doesn'yt match file state data
free(thumb);
thumb = NULL;
}
}
else
{ // doesn't exist so no valid thumb
free(thumb);
thumb = NULL;
}
}
if (!thumb)
{ // no valid thumb - generate one
_file_thumb_gen(path, mime, delay);
}
else
{ // add the thumb property with full path to thumb
cmd_strbuf_append(strbuf, "thumb", thumb);
// XXX: add if tumb has alpha or not...
free(thumb);
}
}
static char *
_icon_resolve(const char *path, const char *icon, struct stat *st)
{
struct passwd *pw;
char buf[PATH_MAX], *dir, *p, *user;
if (!icon) return NULL;
// /path/to/file.png
if (icon[0] == '/') return strdup(icon);
if ((icon[0] == '.') && (icon[1] == '/'))
{ // ./path/file.png
if ((st->st_mode & S_IFMT) == S_IFDIR)
dir = strdup(path);
else
dir = ecore_file_dir_get(path);
if (dir)
{
snprintf(buf, sizeof(buf), "%s/%s", dir, icon + 2);
return strdup(buf);
}
else return NULL;
}
if ((icon[0] == '~') && (icon[1] == '/'))
{ // ~/path/file.png
pw = getpwuid(st->st_uid);
if ((pw) && (pw->pw_dir))
{
snprintf(buf, sizeof(buf), "%s/%s", pw->pw_dir, icon + 2);
return strdup(buf);
}
else return NULL;
}
if (icon[0] == '~')
{ // ~user/path/file.png
p = strchr(icon, '/');
if (p)
{
user = malloc(p - icon);
if (user)
{
strncpy(user, icon + 1, p - icon - 1);
user[p - icon - 1] = 0;
pw = getpwnam(user);
if ((pw) && (pw->pw_dir))
{
snprintf(buf, sizeof(buf), "%s/%s", pw->pw_dir, p + 1);
return strdup(buf);
}
else return NULL;
}
else return NULL;
}
else return NULL;
}
return NULL;
}
static const char *
_desktop_x_field(Efreet_Desktop *desktop, const char *key)
{
if (!desktop->x) return NULL;
return eina_hash_find(desktop->x, key);
}
static void
_cmd_desktop_x_field_append(Eina_Strbuf *strbuf, Efreet_Desktop *desktop, const char *cmd_key, const char *key)
{
const char *s = _desktop_x_field(desktop, key);
if (s) cmd_strbuf_append(strbuf, cmd_key, s);
}
static void
_cmd_desktop_x_field_icon_resolve_append(Eina_Strbuf *strbuf, Efreet_Desktop *desktop, const char *cmd_key, const char *key, const char *path, struct stat *st)
{
const char *s = _desktop_x_field(desktop, key);
char *icf;
if (!s) return;
icf = _icon_resolve(path, s, st);
if (!icf) return;
cmd_strbuf_append(strbuf, cmd_key, icf);
free(icf);
}
const char *
_mime_get(const char *file)
{
if (eina_fnmatch("*.edj", file, EINA_FNMATCH_CASEFOLD))
return "application/x-edje";
return efreet_mime_type_get(file);
}
static Eina_Bool
_file_add_mod_info(Eina_Strbuf *strbuf, const char *path, Eina_Bool delay)
{ // add file metadata info on file add or modfiy
char dst[PATH_MAX], buf[256], buf2[PATH_MAX], *icf;
struct stat st;
int mode;
struct passwd *pw;
struct group *gr;
const char *mime, *ext, *icon;
Efreet_Desktop *desktop;
Eina_Bool have_label = EINA_FALSE;
// XXX: x & y pos @ wxh view
if (lstat(path, &st) != 0) return EINA_FALSE;
if ((st.st_mode & S_IFMT) == S_IFLNK)
{
struct stat stdst;
cmd_strbuf_append(strbuf, "type", "link");
if (stat(path, &stdst) == 0)
{
ssize_t sz;
sz = readlink(path, dst, sizeof(dst) - 1);
if (sz > 0)
{
dst[sz] = 0;
cmd_strbuf_append(strbuf, "link", dst);
if ((stdst.st_mode & S_IFMT) == S_IFLNK)
cmd_strbuf_append(strbuf, "link-type", "link");
else if ((stdst.st_mode & S_IFMT) == S_IFBLK)
cmd_strbuf_append(strbuf, "link-type", "block");
else if ((stdst.st_mode & S_IFMT) == S_IFCHR)
cmd_strbuf_append(strbuf, "link-type", "char");
else if ((stdst.st_mode & S_IFMT) == S_IFIFO)
cmd_strbuf_append(strbuf, "link-type", "fifo");
else if ((stdst.st_mode & S_IFMT) == S_IFSOCK)
cmd_strbuf_append(strbuf, "link-type", "socket");
else if ((stdst.st_mode & S_IFMT) == S_IFDIR)
{
cmd_strbuf_append(strbuf, "link-type", "dir");
snprintf(buf2, sizeof(buf2), "%s/.dir.desktop", path);
desktop = efreet_desktop_get(buf2);
if (desktop)
{
cmd_strbuf_append(strbuf, "link-label", desktop->name ? desktop->name : ecore_file_file_get(path));
cmd_strbuf_append(strbuf, "link-desktop-generic-name", desktop->generic_name ? "true" : "false");
cmd_strbuf_append(strbuf, "link-desktop-comment", desktop->comment ? desktop->comment : "");
icf = _icon_resolve(path, desktop->icon, &stdst);
if (icf)
{
cmd_strbuf_append(strbuf, "link-desktop-icon", icf);
free(icf);
}
else
cmd_strbuf_append(strbuf, "link-desktop-icon", desktop->icon ? desktop->icon : "");
cmd_strbuf_append(strbuf, "link-desktop-try-exec", desktop->try_exec ? desktop->try_exec : "");
cmd_strbuf_append(strbuf, "link-desktop-exec", desktop->exec ? desktop->exec : "");
cmd_strbuf_append(strbuf, "link-desktop-url", desktop->url ? desktop->url : "");
cmd_strbuf_append(strbuf, "link-desktop-no-display", desktop->no_display ? "true" : "false");
cmd_strbuf_append(strbuf, "link-desktop-hidden", desktop->hidden ? "true" : "false");
cmd_strbuf_append(strbuf, "link-desktop-terminal", desktop->terminal ? "true" : "false");
cmd_strbuf_append(strbuf, "link-desktop-startup-notify", desktop->startup_notify ? "true" : "false");
if ((desktop->icon) && (desktop->icon[0] != '/'))
{
icon = efreet_mime_type_icon_get(desktop->icon, icon_theme, 128);
cmd_strbuf_append(strbuf, "link-desktop-icon.lookup", icon ? icon : "");
}
_cmd_desktop_x_field_append(strbuf, desktop, "link-label-clicked", "X-NameClicked");
_cmd_desktop_x_field_append(strbuf, desktop, "link-label-selected", "X-NameSelected");
_cmd_desktop_x_field_icon_resolve_append(strbuf, desktop, "link-desktop-icon-clicked", "X-IconClicked", path, &stdst);
_cmd_desktop_x_field_icon_resolve_append(strbuf, desktop, "link-desktop-icon-selected", "X-IconSelected", path, &stdst);
efreet_desktop_free(desktop);
have_label = EINA_TRUE;
}
}
else
cmd_strbuf_append(strbuf, "link-type", "file");
mime = _mime_get(dst);
if (mime)
{
cmd_strbuf_append(strbuf, "mime", mime);
icon = efreet_mime_type_icon_get(mime, icon_theme, 128);
if (icon) cmd_strbuf_append(strbuf, "mime-icon", icon);
_file_thumb_handle(strbuf, dst, mime, &stdst, delay);
}
ext = strrchr(dst, '.');
if ((ext) && (!strcasecmp(ext, ".desktop")) && (S_ISREG(stdst.st_mode)))
{
desktop = efreet_desktop_get(path);
if (desktop)
{
cmd_strbuf_append(strbuf, "link-label", desktop->name ? desktop->name : ecore_file_file_get(path));
cmd_strbuf_append(strbuf, "link-desktop-generic-name", desktop->generic_name ? "true" : "false");
cmd_strbuf_append(strbuf, "link-desktop-comment", desktop->comment ? desktop->comment : "");
icf = _icon_resolve(path, desktop->icon, &stdst);
if (icf)
{
cmd_strbuf_append(strbuf, "link-desktop-icon", icf);
free(icf);
}
else
cmd_strbuf_append(strbuf, "link-desktop-icon", desktop->icon ? desktop->icon : "");
cmd_strbuf_append(strbuf, "link-desktop-try-exec", desktop->try_exec ? desktop->try_exec : "");
cmd_strbuf_append(strbuf, "link-desktop-exec", desktop->exec ? desktop->exec : "");
cmd_strbuf_append(strbuf, "link-desktop-url", desktop->url ? desktop->url : "");
cmd_strbuf_append(strbuf, "link-desktop-no-display", desktop->no_display ? "true" : "false");
cmd_strbuf_append(strbuf, "link-desktop-hidden", desktop->hidden ? "true" : "false");
cmd_strbuf_append(strbuf, "link-desktop-terminal", desktop->terminal ? "true" : "false");
cmd_strbuf_append(strbuf, "link-desktop-startup-notify", desktop->startup_notify ? "true" : "false");
if ((desktop->icon) && (desktop->icon[0] != '/'))
{
icon = efreet_mime_type_icon_get(desktop->icon, icon_theme, 128);
cmd_strbuf_append(strbuf, "link-desktop-icon.lookup", icon ? icon : "");
}
_cmd_desktop_x_field_append(strbuf, desktop, "link-label-clicked", "X-NameClicked");
_cmd_desktop_x_field_append(strbuf, desktop, "link-label-selected", "X-NameSelected");
_cmd_desktop_x_field_icon_resolve_append(strbuf, desktop, "link-desktop-icon-clicked", "X-IconClicked", path, &stdst);
_cmd_desktop_x_field_icon_resolve_append(strbuf, desktop, "link-desktop-icon-selected", "X-IconSelected", path, &stdst);
efreet_desktop_free(desktop);
}
else
cmd_strbuf_append(strbuf, "link-label", ecore_file_file_get(path));
}
else
cmd_strbuf_append(strbuf, "link-label", ecore_file_file_get(path));
pw = getpwuid(stdst.st_uid);
if (pw) cmd_strbuf_append(strbuf, "link-user", pw->pw_name);
gr = getgrgid(stdst.st_gid);
if (gr) cmd_strbuf_append(strbuf, "link-group", gr->gr_name);
mode = stdst.st_mode &
(S_ISUID | S_ISGID | S_ISVTX | S_IRUSR | S_IWUSR | S_IXUSR |
S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH);
snprintf(buf, sizeof(buf), "%x", mode);
cmd_strbuf_append(strbuf, "link-mode", buf);
snprintf(buf, sizeof(buf), "%llu", (unsigned long long)stdst.st_ino);
cmd_strbuf_append(strbuf, "link-inode", buf);
snprintf(buf, sizeof(buf), "%llu", (unsigned long long)stdst.st_nlink);
cmd_strbuf_append(strbuf, "link-nlink", buf);
snprintf(buf, sizeof(buf), "%llu", (unsigned long long)stdst.st_gid);
cmd_strbuf_append(strbuf, "link-gid", buf);
snprintf(buf, sizeof(buf), "%llu", (unsigned long long)stdst.st_size);
cmd_strbuf_append(strbuf, "link-size", buf);
snprintf(buf, sizeof(buf), "%llu", (unsigned long long)stdst.st_blksize);
cmd_strbuf_append(strbuf, "link-blksize", buf);
snprintf(buf, sizeof(buf), "%llu", (unsigned long long)stdst.st_blocks);
cmd_strbuf_append(strbuf, "link-blocks", buf);
snprintf(buf, sizeof(buf), "%llu", (unsigned long long)stdst.st_atime);
cmd_strbuf_append(strbuf, "link-atime", buf);
snprintf(buf, sizeof(buf), "%llu", (unsigned long long)stdst.st_mtime);
cmd_strbuf_append(strbuf, "link-mtime", buf);
snprintf(buf, sizeof(buf), "%llu", (unsigned long long)stdst.st_ctime);
cmd_strbuf_append(strbuf, "link-ctime", buf);
}
}
else // stat of original failed - what do we do?
{
cmd_strbuf_append(strbuf, "broken-link", "true");
mime = _mime_get(path);
if (mime)
{
cmd_strbuf_append(strbuf, "mime", mime);
icon = efreet_mime_type_icon_get(mime, icon_theme, 128);
if (icon) cmd_strbuf_append(strbuf, "mime-icon", icon);
}
}
}
else
{
if ((st.st_mode & S_IFMT) == S_IFBLK)
{
cmd_strbuf_append(strbuf, "type", "block");
snprintf(buf, sizeof(buf), "%i", (int)st.st_dev);
cmd_strbuf_append(strbuf, "dev", buf);
snprintf(buf, sizeof(buf), "%i", (int)st.st_rdev);
cmd_strbuf_append(strbuf, "rdev", buf);
}
else if ((st.st_mode & S_IFMT) == S_IFCHR)
{
cmd_strbuf_append(strbuf, "type", "char");
snprintf(buf, sizeof(buf), "%i", (int)st.st_dev);
cmd_strbuf_append(strbuf, "dev", buf);
snprintf(buf, sizeof(buf), "%i", (int)st.st_rdev);
cmd_strbuf_append(strbuf, "rdev", buf);
}
else if ((st.st_mode & S_IFMT) == S_IFIFO)
{
cmd_strbuf_append(strbuf, "type", "fifo");
}
else if ((st.st_mode & S_IFMT) == S_IFSOCK)
{
cmd_strbuf_append(strbuf, "type", "socket");
}
else if ((st.st_mode & S_IFMT) == S_IFDIR)
{
cmd_strbuf_append(strbuf, "type", "dir");
snprintf(buf2, sizeof(buf2), "%s/.dir.desktop", path);
desktop = efreet_desktop_get(buf2);
if (desktop)
{
cmd_strbuf_append(strbuf, "label", desktop->name ? desktop->name : ecore_file_file_get(path));
cmd_strbuf_append(strbuf, "desktop-generic-name", desktop->generic_name ? "true" : "false");
cmd_strbuf_append(strbuf, "desktop-comment", desktop->comment ? desktop->comment : "");
icf = _icon_resolve(path, desktop->icon, &st);
if (icf)
{
cmd_strbuf_append(strbuf, "desktop-icon", icf);
free(icf);
}
else
cmd_strbuf_append(strbuf, "desktop-icon", desktop->icon ? desktop->icon : "");
cmd_strbuf_append(strbuf, "desktop-try-exec", desktop->try_exec ? desktop->try_exec : "");
cmd_strbuf_append(strbuf, "desktop-exec", desktop->exec ? desktop->exec : "");
cmd_strbuf_append(strbuf, "desktop-url", desktop->url ? desktop->url : "");
cmd_strbuf_append(strbuf, "desktop-no-display", desktop->no_display ? "true" : "false");
cmd_strbuf_append(strbuf, "desktop-hidden", desktop->hidden ? "true" : "false");
cmd_strbuf_append(strbuf, "desktop-terminal", desktop->terminal ? "true" : "false");
cmd_strbuf_append(strbuf, "desktop-startup-notify", desktop->startup_notify ? "true" : "false");
if ((desktop->icon) && (desktop->icon[0] != '/'))
{
icon = efreet_mime_type_icon_get(desktop->icon, icon_theme, 128);
cmd_strbuf_append(strbuf, "desktop-icon.lookup", icon ? icon : "");
}
_cmd_desktop_x_field_append(strbuf, desktop, "label-clicked", "X-NameClicked");
_cmd_desktop_x_field_append(strbuf, desktop, "label-selected", "X-NameSelected");
_cmd_desktop_x_field_icon_resolve_append(strbuf, desktop, "desktop-icon-clicked", "X-IconClicked", path, &st);
_cmd_desktop_x_field_icon_resolve_append(strbuf, desktop, "desktop-icon-selected", "X-IconSelected", path, &st);
efreet_desktop_free(desktop);
have_label = EINA_TRUE;
}
}
else
cmd_strbuf_append(strbuf, "type", "file");
mime = _mime_get(path);
if (mime)
{
cmd_strbuf_append(strbuf, "mime", mime);
icon = efreet_mime_type_icon_get(mime, icon_theme, 128);
if (icon) cmd_strbuf_append(strbuf, "mime-icon", icon);
_file_thumb_handle(strbuf, path, mime, &st, delay);
}
ext = strrchr(path, '.');
if ((ext) && (!strcasecmp(ext, ".desktop")) && (S_ISREG(st.st_mode)))
{
desktop = efreet_desktop_get(path);
if (desktop)
{
cmd_strbuf_append(strbuf, "label", desktop->name ? desktop->name : ecore_file_file_get(path));
cmd_strbuf_append(strbuf, "desktop-generic-name", desktop->generic_name ? "true" : "false");
cmd_strbuf_append(strbuf, "desktop-comment", desktop->comment ? desktop->comment : "");
icf = _icon_resolve(path, desktop->icon, &st);
if (icf)
{
cmd_strbuf_append(strbuf, "desktop-icon", icf);
free(icf);
}
else
cmd_strbuf_append(strbuf, "desktop-icon", desktop->icon ? desktop->icon : "");
cmd_strbuf_append(strbuf, "desktop-try-exec", desktop->try_exec ? desktop->try_exec : "");
cmd_strbuf_append(strbuf, "desktop-exec", desktop->exec ? desktop->exec : "");
cmd_strbuf_append(strbuf, "desktop-url", desktop->url ? desktop->url : "");
cmd_strbuf_append(strbuf, "desktop-no-display", desktop->no_display ? "true" : "false");
cmd_strbuf_append(strbuf, "desktop-hidden", desktop->hidden ? "true" : "false");
cmd_strbuf_append(strbuf, "desktop-terminal", desktop->terminal ? "true" : "false");
cmd_strbuf_append(strbuf, "desktop-startup-notify", desktop->startup_notify ? "true" : "false");
if ((desktop->icon) && (desktop->icon[0] != '/'))
{
icon = efreet_mime_type_icon_get(desktop->icon, icon_theme, 128);
cmd_strbuf_append(strbuf, "desktop-icon.lookup", icon ? icon : "");
}
_cmd_desktop_x_field_append(strbuf, desktop, "label-clicked", "X-NameClicked");
_cmd_desktop_x_field_append(strbuf, desktop, "label-selected", "X-NameSelected");
_cmd_desktop_x_field_icon_resolve_append(strbuf, desktop, "desktop-icon-clicked", "X-IconClicked", path, &st);
_cmd_desktop_x_field_icon_resolve_append(strbuf, desktop, "desktop-icon-selected", "X-IconSelected", path, &st);
efreet_desktop_free(desktop);
}
else if (!have_label)
cmd_strbuf_append(strbuf, "label", ecore_file_file_get(path));
}
else if (!have_label)
cmd_strbuf_append(strbuf, "label", ecore_file_file_get(path));
}
pw = getpwuid(st.st_uid);
if (pw) cmd_strbuf_append(strbuf, "user", pw->pw_name);
gr = getgrgid(st.st_gid);
if (gr) cmd_strbuf_append(strbuf, "group", gr->gr_name);
mode = st.st_mode /*&
(S_ISUID | S_ISGID | S_ISVTX | S_IRUSR | S_IWUSR | S_IXUSR |
S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH)*/;
snprintf(buf, sizeof(buf), "%x", mode);
cmd_strbuf_append(strbuf, "mode", buf);
snprintf(buf, sizeof(buf), "%llu", (unsigned long long)st.st_ino);
cmd_strbuf_append(strbuf, "inode", buf);
snprintf(buf, sizeof(buf), "%llu", (unsigned long long)st.st_nlink);
cmd_strbuf_append(strbuf, "nlink", buf);
snprintf(buf, sizeof(buf), "%llu", (unsigned long long)st.st_gid);
cmd_strbuf_append(strbuf, "gid", buf);
snprintf(buf, sizeof(buf), "%llu", (unsigned long long)st.st_size);
cmd_strbuf_append(strbuf, "size", buf);
snprintf(buf, sizeof(buf), "%llu", (unsigned long long)st.st_blksize);
cmd_strbuf_append(strbuf, "blksize", buf);
snprintf(buf, sizeof(buf), "%llu", (unsigned long long)st.st_blocks);
cmd_strbuf_append(strbuf, "blocks", buf);
snprintf(buf, sizeof(buf), "%llu", (unsigned long long)st.st_atime);
cmd_strbuf_append(strbuf, "atime", buf);
snprintf(buf, sizeof(buf), "%llu", (unsigned long long)st.st_mtime);
cmd_strbuf_append(strbuf, "mtime", buf);
snprintf(buf, sizeof(buf), "%llu", (unsigned long long)st.st_ctime);
cmd_strbuf_append(strbuf, "ctime", buf);
return EINA_TRUE;
}
static void
_file_add(const char *path)
{
Eina_Strbuf *strbuf;
strbuf = cmd_strbuf_new("file-add");
cmd_strbuf_append(strbuf, "path", path);
if (!_file_add_mod_info(strbuf, path, EINA_FALSE))
eina_strbuf_free(strbuf);
else
cmd_strbuf_print_consume(strbuf);
}
static void
_file_del(const char *path)
{
Eina_Strbuf *strbuf;
strbuf = cmd_strbuf_new("file-del");
cmd_strbuf_append(strbuf, "path", path);
cmd_strbuf_print_consume(strbuf);
}
static void
_file_mod(const char *path)
{
Eina_Strbuf *strbuf;
strbuf = cmd_strbuf_new("file-mod");
cmd_strbuf_append(strbuf, "path", path);
if (!_file_add_mod_info(strbuf, path, EINA_TRUE))
eina_strbuf_free(strbuf);
else
cmd_strbuf_print_consume(strbuf);
}
static void
_dir_del(const char *path)
{
Eina_Strbuf *strbuf;
strbuf = cmd_strbuf_new("dir-del");
cmd_strbuf_append(strbuf, "path", path);
cmd_strbuf_print_consume(strbuf);
}
static void
_cb_mon(void *data EINA_UNUSED, Ecore_File_Monitor *em EINA_UNUSED, Ecore_File_Event event, const char *path)
{
if ((event == ECORE_FILE_EVENT_CREATED_FILE) ||
(event == ECORE_FILE_EVENT_CREATED_DIRECTORY))
_file_add(path);
else if ((event == ECORE_FILE_EVENT_DELETED_FILE) ||
(event == ECORE_FILE_EVENT_DELETED_DIRECTORY))
_file_del(path);
else if (event == ECORE_FILE_EVENT_MODIFIED)
_file_mod(path);
else if (event == ECORE_FILE_EVENT_DELETED_SELF)
_dir_del(path);
}
static void
_monitor(const char *path)
{
Eina_Iterator *it;
Eina_File_Direct_Info *info;
Eina_Strbuf *strbuf;
if (mon) return;
// tell the front end out listing is beginning
strbuf = cmd_strbuf_new("list-begin");
cmd_strbuf_print_consume(strbuf);
mon = ecore_file_monitor_add(path, _cb_mon, NULL);
it = eina_file_direct_ls(path);
if (!it)
{
// XXX: error output
goto err;
}
EINA_ITERATOR_FOREACH(it, info) _file_add(info->path);
eina_iterator_free(it);
err:
// tell the front end out listing is done
strbuf = cmd_strbuf_new("list-end");
cmd_strbuf_print_consume(strbuf);
}
void
do_handle_cmd(Cmd *c)
{
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]);
}
}
// cmd_dump_sterr(c);
}
int
do_init(int argc EINA_UNUSED, const char **argv EINA_UNUSED)
{
const char *s;
ecore_file_init();
efreet_init();
efreet_mime_init();
icon_theme = getenv("E_ICON_THEME");
config_dir = getenv("E_HOME_DIR");
home_dir = getenv("HOME");
if (!home_dir) return 0;
if (!config_dir)
{
char buf[PATH_MAX];
snprintf(buf, sizeof(buf), "%s/.e/e", home_dir);
home_dir = eina_stringshare_add(buf);
}
// maximum number of back-end thumbnailer slaves is num cores - 2
// with a minimum of 1 (so dual core systems will be limited to 1
// thumbnailer at once. quad core will run 2. 8 core will run 6, 16
// 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);
}
else thumb_busy_max = eina_cpu_count() - 2;
if (thumb_busy_max < 1) thumb_busy_max = 1;
// 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);
return 0;
}
void
do_shutdown(void)
{
ecore_event_handler_del(thumb_exe_del_handler);
thumb_exe_del_handler = NULL;
if (mon) ecore_file_monitor_del(mon);
mon = NULL;
efreet_mime_shutdown();
efreet_shutdown();
ecore_file_shutdown();
}

10
src/backends/default/sh-open Executable file
View File

@ -0,0 +1,10 @@
#!/bin/bash
read -r CMD
sleep 1;
echo -e "CMD add type=file file=hello.txt icon=plain path=/xx/yy/hello.txt"
sleep 1
echo -e "CMD add type=file file=blah.txt icon=folder path=/xx/yy/blah.txt"
echo -e "CMD add type=file file=boo.txt icon=blank path=/xx/yy/boo.txt"
while [ 1 ]; do sleep 1; done

View File

@ -0,0 +1,108 @@
#include "thumb.h"
Evas_Object *win = NULL;
Evas_Object *subwin = NULL;
Evas_Object *image = NULL;
static void
_thumb_dir_make(const char *thumb)
{ // create dir for the thumb to live in
char *dir = ecore_file_dir_get(thumb);
if (!dir) return;
ecore_file_mkpath(dir);
free(dir);
}
static Eet_File *
_thumb_output_open(const char *thumb)
{ // open target thumb tmp file we will later atomically replace target with
char buf[PATH_MAX];
snprintf(buf, sizeof(buf), "%s.tmp", thumb);
return eet_open(buf, EET_FILE_MODE_WRITE);
}
static void
_thumb_output_close(Eet_File *ef, const char *thumb)
{ // close thumnb file and atomically rename tmp file on top of target
char buf[PATH_MAX];
eet_close(ef);
snprintf(buf, sizeof(buf), "%s.tmp", thumb);
ecore_file_mv(buf, thumb);
}
EAPI_MAIN int
elm_main(int argc, char **argv)
{
Eet_File *ef;
const char *path, *mime, *thumb;
int ret;
struct stat st;
unsigned char statsha1[20];
if (argc < 4) return 100;
path = argv[1];
mime = argv[2];
thumb = argv[3];
// set up buffer win/canvas with sub win rendered inline as image
elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED);
elm_config_preferred_engine_set("buffer");
win = elm_win_add(NULL, "Efm-Thumb", ELM_WIN_BASIC);
subwin = elm_win_add(win, "inlined", ELM_WIN_INLINED_IMAGE);
elm_win_alpha_set(subwin, EINA_TRUE);
image = elm_win_inlined_image_object_get(subwin);
evas_object_show(subwin);
evas_object_show(win);
// manual rendering as we won't run the loop - only render then get results
elm_win_norender_push(subwin);
elm_win_norender_push(win);
// stat orig file and store the state info we care about as a sha1 hash
if (stat(path, &st) != 0) exit(1);
sha1_stat(&st, statsha1);
// ensure dest dir for thumb exists
_thumb_dir_make(thumb);
// open our thubm file ti write thnigs into it
ef = _thumb_output_open(thumb);
if (!ef) exit(3);
// an edj file (theme, background, icon .... groups inside matter
if (check_thumb_edje(path, mime))
ret = thumb_edje(ef, path, mime, thumb);
// if it's a font - load it and render some text as thumb/preview
else if (check_thumb_font(path, mime))
ret = thumb_font(ef, path, mime, thumb);
// a paged document - load multiple pages into thumb
else if (check_thumb_paged(path, mime))
ret = thumb_paged(ef, path, mime, thumb);
// a music track/file
else if (check_thumb_music(path, mime))
ret = thumb_music(ef, path, mime, thumb);
// a video file of a moive, series episode or something else
else if (check_thumb_video(path, mime))
ret = thumb_video(ef, path, mime, thumb);
// otherwise handle as an image
else
ret = thumb_image(ef, path, mime, thumb);
// write out the original file path so we could walk through all thumbs
// and find which thumbs no longer have an original file left
eet_write(ef, "orig/path", path, strlen(path),
EET_COMPRESSION_LOW);
// write out our sha1 of the file stat info - quick and mostly right way
// to heck if the thumb is up to date with file
eet_write(ef, "orig/stat/sha1", statsha1, 20,
EET_COMPRESSION_NONE);
// done - finish file write and atomic rename
_thumb_output_close(ef, thumb);
// if we failed to generate the thumb - delete what we were building
if (ret != 0) unlink(thumb);
return ret;
}
ELM_MAIN()

View File

@ -0,0 +1,71 @@
#include <Elementary.h>
#include "efm_config.h"
#include "sha.h"
#include "thumb_check.h"
extern Evas_Object *win;
extern Evas_Object *subwin;
extern Evas_Object *image;
void thumb_image_write(Eet_File *ef, const char *key, Evas_Object *img, Eina_Bool a, Eina_Bool lossy);
void thumb_url_str_get(const char *url, size_t max, void (*cb) (void *data, const char *result), const void *data);
void thumb_url_bin_get(const char *url, size_t max, void (*cb) (void *data, const void *result, size_t size), const void *data);
typedef struct
{
char *url;
int w, h;
} Search_Result;
void thumb_search_image(const char *str, void (*cb) (void *data, Eina_List *results_orig, Eina_List *results_cached), void *data);
int thumb_image (Eet_File *ef, const char *path, const char *mime, const char *thumb);
int thumb_font (Eet_File *ef, const char *path, const char *mime, const char *thumb);
int thumb_paged (Eet_File *ef, const char *path, const char *mime, const char *thumb);
int thumb_music (Eet_File *ef, const char *path, const char *mime, const char *thumb);
int thumb_video (Eet_File *ef, const char *path, const char *mime, const char *thumb);
int thumb_edje (Eet_File *ef, const char *path, const char *mime, const char *thumb);
// utility
static inline void
scale(int *w, int *h, int maxw, int maxh, Eina_Bool no_scale_up)
{ // write a big 1024x1024 preview (but no larger than the original image)
int ww, hh;
ww = maxw;
hh = (*h * maxw) / *w;
if (hh > maxh)
{ // too tall = so limit height and scale down keeping aspect
hh = maxh;
ww = (*w * maxh) / *h;
}
if ((no_scale_up) && ((ww > *h) || (hh > *h)))
{
ww = *w;
hh = *h;
}
*w = ww;
*h = hh;
}
static inline void
scale_out(int *w, int *h, int maxw, int maxh, Eina_Bool no_scale_up)
{ // write a big 1024x1024 preview (but no larger than the original image)
int ww, hh;
ww = maxw;
hh = (*h * maxw) / *w;
if (hh < maxh)
{ // not tall enough = so limit height and scale down keeping aspect
hh = maxh;
ww = (*w * maxh) / *h;
}
if ((no_scale_up) && ((ww > *h) || (hh > *h)))
{
ww = *w;
hh = *h;
}
*w = ww;
*h = hh;
}

View File

@ -0,0 +1,378 @@
// generate thumbnail for images
#include "thumb.h"
// XXX: can do progressive resize down ie scale to 512 then take 512 and
// halve to 256 then halve it to 128 etc. rather than render from orig to
// target size....
static Evas_Object *edj = NULL;
static Evas_Object *subsubwin = NULL;
static Evas_Object *subimage = NULL;
static int iw = 0, ih = 0;
static char *grp_first = NULL;
typedef enum
{
EDJ_NONE, // does not appear to have groups
EDJ_THEME, // what looks like a full theme
EDJ_BACKGROUND, // a wallpaper background file
EDJ_ICON, // an icon edje file
EDJ_FIRST // just use the first group found
} Edj_Type;
static void
_thumb_image_setup(void)
{ // create and show image
subsubwin = elm_win_add(subwin, "inlined2", ELM_WIN_INLINED_IMAGE);
subimage = elm_win_inlined_image_object_get(subsubwin);
elm_win_alpha_set(subsubwin, EINA_TRUE);
evas_object_show(subsubwin);
edj = edje_object_add(evas_object_evas_get(subsubwin));
evas_object_show(edj);
}
static void
_thumb_image_file_background_set(const char *file)
{
edje_object_file_set(edj, file, "e/desktop/background");
iw = 1920;
ih = 1080;
}
static void
_thumb_image_file_icon_set(const char *file)
{
edje_object_file_set(edj, file, "icon");
edje_object_size_min_get(edj, &iw, &ih);
if ((iw <= 0) || (ih < 0))
{
iw = 256;
ih = 256;
}
}
static void
_thumb_image_file_first_group_set(const char *file, const char *grp)
{
edje_object_file_set(edj, file, grp);
edje_object_size_min_get(edj, &iw, &ih);
if (iw <= 0)
{
edje_object_size_min_restricted_calc(edj, &iw, &ih, 256, 256);
}
}
static Eina_Bool
_cb_quit(void *data EINA_UNUSED)
{
elm_exit();
return EINA_FALSE;
}
static void
_thumb_image_file_theme_set(const char *file)
{
const char *s;
Evas_Object *o, *o_comp, *o_border, *o_win_table, *o_win_bg;
Evas_Object *o_toolbar, *o_list, *o_frame, *o_box;
Evas_Object *o_check1, *o_check2, *o_check3, *o_sep;
Evas_Object *o_radio1, *o_radio2, *o_radio3;
Evas_Object *o_slider, *o_progress;
Evas_Object *o_button1, *o_button2;
Elm_Object_Item *it;
elm_theme_overlay_add(NULL, file);
s = elm_theme_group_path_find(NULL, "e/desktop/background");
edje_object_file_set(edj, s, "e/desktop/background");
iw = 512;
ih = 512;
o = o_comp = edje_object_add(evas_object_evas_get(subsubwin));
s = elm_theme_group_path_find(NULL, "e/comp/frame/default");
edje_object_file_set(o, s, "e/comp/frame/default");
edje_object_signal_emit(o, "e,state,visible", "e");
o = o_border = edje_object_add(evas_object_evas_get(subsubwin));
s = elm_theme_group_path_find(NULL, "e/widgets/border/default/border");
edje_object_file_set(o, s, "e/widgets/border/default/border");
edje_object_part_text_set(o, "e.text.title", "Title");
edje_object_signal_emit(o, "e,state,focused", "e");
evas_object_show(o);
edje_object_part_swallow(o_comp, "e.swallow.content", o);
o = o_win_table = elm_table_add(subsubwin);
edje_object_part_swallow(o_border, "e.swallow.client", o);
evas_object_show(o);
o = o_win_bg = elm_bg_add(o_win_table);
evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_table_pack(o_win_table, o, 0, 0, 10, 10);
evas_object_show(o);
o = o_toolbar = elm_toolbar_add(o_win_table);
evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, 0.0);
evas_object_size_hint_align_set(o, EVAS_HINT_FILL, 0.0);
elm_toolbar_homogeneous_set(o, EINA_TRUE);
elm_toolbar_align_set(o, 0.5);
elm_table_pack(o_win_table, o, 0, 0, 10, 1);
evas_object_show(o);
it = elm_toolbar_item_append(o_toolbar, "folder", "Folder", NULL, NULL);
elm_toolbar_item_selected_set(it, EINA_TRUE);
it = elm_toolbar_item_append(o_toolbar, "computer", "Computer", NULL, NULL);
it = elm_toolbar_item_append(o_toolbar, "audio-volume", "Volume", NULL, NULL);
it = elm_toolbar_item_append(o_toolbar, "user-available", "User", NULL, NULL);
it = elm_toolbar_item_append(o_toolbar, "mail-send", "Mail", NULL, NULL);
o = o_list = elm_list_add(o_win_table);
evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_table_pack(o_win_table, o, 0, 1, 5, 6);
evas_object_show(o);
it = elm_list_item_append(o_list, "This", NULL, NULL, NULL, NULL);
it = elm_list_item_append(o_list, "List", NULL, NULL, NULL, NULL);
it = elm_list_item_append(o_list, "Contains", NULL, NULL, NULL, NULL);
it = elm_list_item_append(o_list, "Items", NULL, NULL, NULL, NULL);
it = elm_list_item_append(o_list, "Long list item", NULL, NULL, NULL, NULL);
it = elm_list_item_append(o_list, "Short item", NULL, NULL, NULL, NULL);
elm_list_item_selected_set(it, EINA_TRUE);
it = elm_list_item_append(o_list, "More text here", NULL, NULL, NULL, NULL);
it = elm_list_item_append(o_list, "Less text", NULL, NULL, NULL, NULL);
it = elm_list_item_append(o_list, "Text", NULL, NULL, NULL, NULL);
it = elm_list_item_append(o_list, "Yet more items", NULL, NULL, NULL, NULL);
it = elm_list_item_append(o_list, "And even longer items", NULL, NULL, NULL, NULL);
it = elm_list_item_append(o_list, "Shorter items", NULL, NULL, NULL, NULL);
it = elm_list_item_append(o_list, "Short items", NULL, NULL, NULL, NULL);
it = elm_list_item_append(o_list, "Shortest item", NULL, NULL, NULL, NULL);
it = elm_list_item_append(o_list, "Second last item", NULL, NULL, NULL, NULL);
it = elm_list_item_append(o_list, "Last item", NULL, NULL, NULL, NULL);
elm_list_go(o_list);
o = o_frame = elm_frame_add(o_win_table);
evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_object_text_set(o, "Frame");
elm_table_pack(o_win_table, o, 5, 1, 5, 6);
evas_object_show(o);
o = o_box = elm_box_add(o_win_table);
evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_box_horizontal_set(o, EINA_FALSE);
elm_box_align_set(o, 0.5, 0.0);
elm_object_content_set(o_frame, o);
evas_object_show(o);
o = o_check1 = elm_check_add(o_win_table);
evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, 0.0);
evas_object_size_hint_align_set(o, EVAS_HINT_FILL, 0.5);
elm_object_text_set(o, "This option");
elm_box_pack_end(o_box, o);
evas_object_show(o);
o = o_check2 = elm_check_add(o_win_table);
evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, 0.0);
evas_object_size_hint_align_set(o, EVAS_HINT_FILL, 0.5);
elm_object_text_set(o, "Something");
elm_check_state_set(o, EINA_TRUE);
elm_box_pack_end(o_box, o);
evas_object_show(o);
o = o_check3 = elm_check_add(o_win_table);
evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, 0.0);
evas_object_size_hint_align_set(o, EVAS_HINT_FILL, 0.5);
elm_object_text_set(o, "And another thing");
elm_check_state_set(o, EINA_TRUE);
elm_box_pack_end(o_box, o);
evas_object_show(o);
o = o_sep = elm_separator_add(o_win_table);
evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, 0.0);
evas_object_size_hint_align_set(o, EVAS_HINT_FILL, 0.5);
elm_separator_horizontal_set(o, EINA_TRUE);
elm_box_pack_end(o_box, o);
evas_object_show(o);
o = o_radio1 = elm_radio_add(o_win_table);
evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, 0.0);
evas_object_size_hint_align_set(o, EVAS_HINT_FILL, 0.5);
elm_radio_state_value_set(o, 1);
elm_object_text_set(o, "First");
elm_box_pack_end(o_box, o);
evas_object_show(o);
o = o_radio2 = elm_radio_add(o_win_table);
evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, 0.0);
evas_object_size_hint_align_set(o, EVAS_HINT_FILL, 0.5);
elm_radio_state_value_set(o, 2);
elm_radio_group_add(o, o_radio1);
elm_object_text_set(o, "Second");
elm_box_pack_end(o_box, o);
evas_object_show(o);
o = o_radio3 = elm_radio_add(o_win_table);
evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, 0.0);
evas_object_size_hint_align_set(o, EVAS_HINT_FILL, 0.5);
elm_radio_state_value_set(o, 3);
elm_radio_group_add(o, o_radio1);
elm_object_text_set(o, "Third");
elm_box_pack_end(o_box, o);
evas_object_show(o);
elm_radio_value_set(o_radio1, 2);
o = o_slider = elm_slider_add(o_win_table);
evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, 0.0);
evas_object_size_hint_align_set(o, EVAS_HINT_FILL, 1.0);
elm_object_text_set(o, "Slider");
elm_slider_unit_format_set(o, "%1.1f");
elm_slider_min_max_set(o, 0, 10);
elm_slider_value_set(o, 3.7);
elm_table_pack(o_win_table, o, 0, 7, 10, 1);
evas_object_show(o);
o = o_progress = elm_progressbar_add(o_win_table);
evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, 0.0);
evas_object_size_hint_align_set(o, EVAS_HINT_FILL, 1.0);
elm_object_text_set(o, "Progress");
elm_progressbar_value_set(o, 0.7);
elm_progressbar_unit_format_set(o, "%1.1f");
elm_table_pack(o_win_table, o, 0, 8, 10, 1);
evas_object_show(o);
o = o_button1 = elm_button_add(o_win_table);
evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, 0.0);
evas_object_size_hint_align_set(o, EVAS_HINT_FILL, 1.0);
elm_object_text_set(o, "Select");
elm_table_pack(o_win_table, o, 0, 9, 5, 1);
evas_object_show(o);
o = o_button2 = elm_button_add(o_win_table);
evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, 0.0);
evas_object_size_hint_align_set(o, EVAS_HINT_FILL, 1.0);
elm_object_text_set(o, "Cancel");
elm_table_pack(o_win_table, o, 5, 9, 5, 1);
evas_object_show(o);
o = o_comp;
evas_object_move(o, 64, 64);
evas_object_resize(o, 384, 384);
evas_object_show(o);
ecore_timer_add(1.0, _cb_quit, NULL);
elm_run();
}
static Edj_Type
_thumb_edje_type_guess(const char *file)
{
Eina_List *l;
const char *s;
Eina_List *groups = edje_file_collection_list(file);
if (!groups) return EDJ_NONE;
Eina_Bool have_icon = EINA_FALSE;
Eina_Bool have_e_background = EINA_FALSE;
Eina_Bool have_e_border = EINA_FALSE;
Eina_Bool have_elm_button = EINA_FALSE;
Eina_Bool have_elm_check = EINA_FALSE;
Eina_Bool have_elm_genlist = EINA_FALSE;
Eina_Bool have_elm_frame = EINA_FALSE;
EINA_LIST_FOREACH(groups, l, s)
{
if (!grp_first) grp_first = strdup(s);
#define MATCH(__str, __val) (!strcmp(s, __str)) have_ ## __val = EINA_TRUE
if MATCH("icon", icon);
else if MATCH("e/desktop/background", e_background);
else if MATCH("e/widgets/border/default/border", e_border);
else if MATCH("elm/button/base/default", elm_button);
else if MATCH("elm/check/base/default", elm_check);
else if MATCH("elm/genlist/item/default/default", elm_genlist);
else if MATCH("elm/frame/base/default", elm_frame);
}
edje_file_collection_list_free(groups);
if (have_e_background && have_e_border && have_elm_button &&
have_elm_check && have_elm_genlist && have_elm_frame)
return EDJ_THEME;
else if (have_e_background)
return EDJ_BACKGROUND;
else if (have_icon)
return EDJ_ICON;
return EDJ_FIRST;
}
int
thumb_edje(Eet_File *ef, const char *path, const char *mime EINA_UNUSED, const char *thumb EINA_UNUSED)
{
const int sizes[] = { 512, 256, 128, 64, 32, 16, 0 };
int w, h, i;
char buf[128];
Edj_Type etype = _thumb_edje_type_guess(path);
if (etype == EDJ_NONE) return 2;
elm_config_scale_set(1.0);
_thumb_image_setup();
if (etype == EDJ_BACKGROUND)
{ // add filled background
_thumb_image_file_background_set(path);
}
else if (etype == EDJ_ICON)
{ // an edj icon file
_thumb_image_file_icon_set(path);
}
else if (etype == EDJ_THEME)
{ // it's a theme - let's fake up some gui setup
_thumb_image_file_theme_set(path);
}
else if (etype == EDJ_FIRST)
{ // just load first group we find
_thumb_image_file_first_group_set(path, grp_first);
}
// if size is bunk - we can't load it...
if ((iw <= 0) || (ih < 0)) return 2;
// write a big 1024x1024 preview (but no larger than the original image)
w = iw; h = ih; scale(&w, &h, 1024, 1024, EINA_TRUE);
// resize to target size
evas_object_resize(edj, iw, ih);
evas_object_resize(subsubwin, iw, ih);
// preview
evas_object_resize(subimage, w, h);
evas_object_resize(subwin, w, h);
// render our current state and pick up pixel results
elm_win_render(subsubwin);
elm_win_render(subwin);
// save out preview size
snprintf(buf, sizeof(buf), "image/preview");
thumb_image_write(ef, buf, image, EINA_TRUE, EINA_TRUE);
snprintf(buf, sizeof(buf), "%i %i", w, h);
eet_write(ef, "image/preview/size", buf, strlen(buf) + 1,
EET_COMPRESSION_NONE);
// multiple thumb sizes so can load/pick the best one at runtime
for (i = 0; sizes[i] != 0; i++)
{
// scale down and keep aspect
w = iw; h = ih; scale(&w, &h, sizes[i], sizes[i], EINA_FALSE);
// resize to target size
evas_object_resize(subimage, w, h);
evas_object_resize(subwin, w, h);
// render our current state and pick up pixel results
elm_win_render(subwin);
// save out thumb size
snprintf(buf, sizeof(buf), "image/thumb/%i", sizes[i]);
thumb_image_write(ef, buf, image, EINA_TRUE, EINA_TRUE);
}
return 0;
}

View File

@ -0,0 +1,102 @@
// generate thumbnail files for fonts
#include "thumb.h"
static Evas_Object *
_thumb_text_setup(const char *file, const char *str, int maxw, int maxh, int size, int *size_chosen)
{
Evas_Object *o;
Evas_Coord w, h;
int sizeup, sizedn, psize;
o = evas_object_text_add(evas_object_evas_get(subwin));
evas_object_color_set(o, 255, 255, 255, 255);
evas_object_text_text_set(o, str);
if (size == 0)
evas_object_text_font_set(o, file, maxh);
else
evas_object_text_font_set(o, file, size);
evas_object_show(o);
if (size == 0)
{ // auto find a size that fits in maxw/maxh
size = maxh;
sizeup = size * 2;
sizedn = 1;
psize = size + 100; // dummy psize for first loop
for (;;)
{
evas_object_geometry_get(o, NULL, NULL, &w, &h);
if ((w <= maxw) && (h <= maxh))
{ // it fits
if (abs(size - psize) <= 1) break; // we found the best size
psize = size;
size = (size + sizeup) / 2;
sizedn = psize;
}
else
{
if (abs(size - psize) <= 1) break; // we found the best size
psize = size;
size = (size + sizedn) / 2;
sizeup = psize;
}
evas_object_text_font_set(o, file, size);
}
}
*size_chosen = size;
return o;
}
static void
_thumb_font_2_line(const char *str1, const char *str2, Eet_File *ef, const char *path, int szw, int szh, const char *key)
{
Evas_Object *tx1, *tx2;
int sz1, sz2, w, h;
tx1 = _thumb_text_setup(path, str1, szw, szh / 2, 0, &sz1);
tx2 = _thumb_text_setup(path, str2, szw, szh / 2, 0, &sz2);
if (sz1 < sz2)
{
evas_object_del(tx2);
sz2 = sz1;
tx2 = _thumb_text_setup(path, str2, szw, szh / 2, sz2, &sz2);
}
else if (sz2 < sz1)
{
evas_object_del(tx1);
sz1 = sz2;
tx1 = _thumb_text_setup(path, str1, szw, szh / 2, sz1, &sz1);
}
evas_object_geometry_get(tx1, NULL, NULL, &w, &h);
evas_object_move(tx1, (szw - w) / 2, ((szh / 2) - h) / 2);
evas_object_geometry_get(tx2, NULL, NULL, &w, &h);
evas_object_move(tx2, (szw - w) / 2, (szh / 2) + (((szh / 2) - h) / 2));
evas_object_resize(subwin, szw, szh);
elm_win_render(subwin);
thumb_image_write(ef, key, image, EINA_TRUE, EINA_FALSE);
evas_object_del(tx1);
evas_object_del(tx2);
}
int
thumb_font(Eet_File *ef, const char *path, const char *mime EINA_UNUSED, const char *thumb EINA_UNUSED)
{
const int sizes[] = { 512, 256, 128, 64, 32, 16, 0 };
char buf[128];
int i;
char one = 1;
// XXX: we don't handle any font load errors - eg 0 sized
for (i = 0; sizes[i] != 0; i++)
{
snprintf(buf, sizeof(buf), "image/thumb/%i", sizes[i]);
_thumb_font_2_line("ABC",
"def",
ef, path, sizes[i], sizes[i], buf);
}
_thumb_font_2_line("Lorem ipsum dolor sit amet 012/789 #!?",
"日本語 にほんご ソフト 中文 華語 한국",
ef, path, 2048, 256, "image/preview");
eet_write(ef, "image/thumb/mono", &one, 1,
EET_COMPRESSION_NONE);
return 0;
}

View File

@ -0,0 +1,165 @@
// generate thumbnail for images
#include "thumb.h"
// XXX: can do progressive resize down ie scale to 512 then take 512 and
// halve to 256 then halve it to 128 etc. rather than render from orig to
// target size....
static Evas_Object *im = NULL;
static Eina_Bool alpha = EINA_FALSE;
static int iw = 0, ih = 0;
static void
_thumb_image_setup(void)
{ // create and show image
im = evas_object_image_filled_add(evas_object_evas_get(subwin));
evas_object_show(im);
}
static void
_thumb_image_file_set(const char *file)
{ // set file to image, get size & alpha
evas_object_image_file_set(im, file, NULL);
evas_object_image_size_get(im, &iw, &ih);
alpha = evas_object_image_alpha_get(im);
}
static void
_thumb_image_hsv_sort_render(unsigned int *dst, int w, int h)
{ // render image down to a specific size (small) and copy to dest
void *pixels;
evas_object_resize(im, w, h);
evas_object_resize(subwin, w, h);
elm_win_render(subwin);
pixels = evas_object_image_data_get(image, EINA_FALSE);
if (!pixels) return;
memcpy(dst, pixels, w * h * sizeof(int));
evas_object_image_data_set(image, pixels);
}
static void
_thumb_image_hsv_sort_key_write(Eet_File *ef, const char *key)
{ // get rendered pixels from image representing subwin and write out AHVS key
unsigned int *data4x4, *data2x2, *data1x1;
unsigned char id2[256];
int n, i, hi, si, vi;
float h, s, v;
const int pat2x2[4] = { 0, 3, 1, 2 };
const int pat4x4[16] = { 5, 10, 6, 9,
0, 15, 3, 12,
1, 14, 7, 8,
4, 11, 2, 13 };
data4x4 = malloc(4 * 4 * sizeof(int));
data2x2 = malloc(2 * 2 * sizeof(int));
data1x1 = malloc(1 * 1 * sizeof(int));
if ((!data4x4) || (!data2x2) || (!data1x1)) goto err;
_thumb_image_hsv_sort_render(data4x4, 4, 4);
_thumb_image_hsv_sort_render(data2x2, 2, 2);
_thumb_image_hsv_sort_render(data1x1, 1, 1);
n = 0;
#define A(v) (((v) >> 24) & 0xff)
#define R(v) (((v) >> 16) & 0xff)
#define G(v) (((v) >> 8 ) & 0xff)
#define B(v) (((v) ) & 0xff)
#define HSV(p) do { \
evas_color_rgb_to_hsv(R(p), G(p), B(p), &h, &s, &v); \
hi = 20 * (h / 360.0); \
si = 20 * s; \
vi = 20 * v; \
if (si < 2) hi = 25; \
} while (0)
#define SAVEX(x) id2[n++] = 'a' + (x)
SAVEX(A(data1x1[0]));
for (i = 0; i < 4 ; i++) SAVEX(A(data2x2[pat2x2[i]]));
for (i = 0; i < 16; i++) SAVEX(A(data4x4[pat4x4[i]]));
#define STORE(val) \
HSV(data1x1[0]); \
SAVEX(val); \
for (i = 0; i < 4; i++) { \
HSV(data2x2[pat2x2[i]]); \
SAVEX(val); \
} \
for (i = 0; i < 16; i++) { \
HSV(data4x4[pat4x4[i]]); \
SAVEX(val); \
}
STORE(hi);
STORE(vi);
STORE(si);
id2[n++] = 0;
eet_write(ef, key, id2, n,
EET_COMPRESSION_NONE);
err:
free(data4x4);
free(data2x2);
free(data1x1);
}
int
thumb_image(Eet_File *ef, const char *path, const char *mime EINA_UNUSED, const char *thumb EINA_UNUSED)
{
const int sizes[] = { 512, 256, 128, 64, 32, 16, 0 };
int w, h, i;
char buf[128];
unsigned char a;
_thumb_image_setup();
// add filled image to then size accordingly
_thumb_image_file_set(path);
// if size is bunk - we can't load it...
if ((iw <= 0) || (ih < 0)) return 2;
// write a big 1024x1024 preview (but no larger than the original image)
w = iw; h = ih; scale(&w, &h, 1024, 1024, EINA_TRUE);
// resize to target size
evas_object_resize(im, w, h);
evas_object_resize(subwin, w, h);
// render our current state and pick up pixel results
elm_win_render(subwin);
// save out preview size
snprintf(buf, sizeof(buf), "image/preview");
thumb_image_write(ef, buf, image, alpha, EINA_TRUE);
snprintf(buf, sizeof(buf), "%i %i", w, h);
eet_write(ef, "image/preview/size", buf, strlen(buf) + 1,
EET_COMPRESSION_NONE);
// multiple thumb sizes so can load/pick the best one at runtime
for (i = 0; sizes[i] != 0; i++)
{
// scale down and keep aspect
w = iw; h = ih; scale(&w, &h, sizes[i], sizes[i], EINA_FALSE);
// resize to target size
evas_object_resize(im, w, h);
evas_object_resize(subwin, w, h);
// render our current state and pick up pixel results
elm_win_render(subwin);
// save out thumb size
snprintf(buf, sizeof(buf), "image/thumb/%i", sizes[i]);
thumb_image_write(ef, buf, image, alpha, EINA_TRUE);
}
_thumb_image_hsv_sort_key_write(ef, "image/sort_key");
// write original alpha flag
a = alpha;
if (alpha)
eet_write(ef, "orig/image/alpha", &a, 1,
EET_COMPRESSION_NONE);
// write original size
snprintf(buf, sizeof(buf), "%i %i", iw, ih);
eet_write(ef, "orig/image/size", buf, strlen(buf) + 1,
EET_COMPRESSION_NONE);
return 0;
}

View File

@ -0,0 +1,400 @@
// generate thumbnail for music files - look for album art on google
#include "thumb.h"
#include <Emotion.h>
// XXX: can do progressive resize down ie scale to 512 then take 512 and
// halve to 256 then halve it to 128 etc. rather than render from orig to
// target size....
static Evas_Object *im = NULL;
static Eina_Bool alpha = EINA_FALSE;
static int iw = 0, ih = 0;
static Eina_List *results = NULL;
static void *mem_data = NULL;
static int mem_size = 0;
static int query_pass = 0;
static char *title = NULL;
static char *artist = NULL;
static char *album = NULL;
typedef struct
{
char *url;
int w, h;
unsigned long long fitness;
} Result;
static void
_thumb_image_setup(void)
{ // create and show image
im = evas_object_image_filled_add(evas_object_evas_get(subwin));
evas_object_show(im);
}
static void
_thumb_image_mem_set(void *data, int size)
{ // set file to image, get size & alpha
evas_object_image_memfile_set(im, data, size, "jpg", NULL);
evas_object_image_size_get(im, &iw, &ih);
alpha = evas_object_image_alpha_get(im);
}
static void
_thumb_image_file_set(const char *file)
{ // set file to image, get size & alpha
evas_object_image_file_set(im, file, NULL);
evas_object_image_size_get(im, &iw, &ih);
alpha = evas_object_image_alpha_get(im);
}
static Eina_Bool
_cb_vid_open_done_timeout(void *data EINA_UNUSED)
{
elm_exit();
return EINA_FALSE;
}
static void
_cb_vid_open_done(void *data EINA_UNUSED, Evas_Object *obj, void *event EINA_UNUSED)
{ // we finished opening - get netadata
const char *s;
s = emotion_object_meta_info_get(obj, EMOTION_META_INFO_TRACK_TITLE);
if (s) title = strdup(s);
s = emotion_object_meta_info_get(obj, EMOTION_META_INFO_TRACK_ARTIST);
if (s) artist = strdup(s);
s = emotion_object_meta_info_get(obj, EMOTION_META_INFO_TRACK_ALBUM);
if (s) album = strdup(s);
// finish loop
elm_exit();
}
static void
_video_metadata_get(const char *path)
{
Ecore_Timer *t;
Evas_Object *o = emotion_object_add(evas_object_evas_get(subwin));
evas_object_smart_callback_add(o, "open_done", _cb_vid_open_done, NULL);
emotion_object_file_set(o, path);
emotion_object_audio_mute_set(o, EINA_TRUE);
emotion_object_audio_volume_set(o, 0.0);
// a timeout for the loop
t = ecore_timer_add(10.0, _cb_vid_open_done_timeout, NULL);
elm_run();
ecore_timer_del(t);
evas_object_del(o);
}
static void
_cb_results(void *data EINA_UNUSED, Eina_List *results_orig, Eina_List *results_cached EINA_UNUSED)
{
Eina_List *l;
Search_Result *res;
Result *r;
unsigned long long fit_size, fit_square, fit_jpg, fit_listpos, fit_pass;
// we need to re-score results first square better than not suqare
// next - higher est better than lower rest
// ends in .jpg, .jpeg better than not
fit_listpos = 100;
EINA_LIST_FOREACH(results_orig, l, res)
{
// skip results that are 0 sized or with no url
if ((res->w <= 0) || (res->h <= 0) || (!res->url)) continue;
// new result
r = calloc(1, sizeof(Result));
if (!r) continue;
r->w = res->w;
r->h = res->h;
r->url = strdup(res->url);
// jpegs preferred
if ((eina_fnmatch("*.jpg", res->url, EINA_FNMATCH_CASEFOLD)) ||
(eina_fnmatch("*.jpeg", res->url, EINA_FNMATCH_CASEFOLD)) ||
(eina_fnmatch("*.jpe", res->url, EINA_FNMATCH_CASEFOLD)))
fit_jpg = 100;
else
fit_jpg = 50;
// bigger is better
fit_size = (r->w / 10) * (r->h / 10);
// if it's bigger than 1000x1000 it's not really better
if (fit_size > 10000) fit_size = 10000;
// more square is better
fit_square = (100 * r->w) / r->h;
if (fit_square > 100) fit_square = (100 * r->h) / r->w;
// first pass gets a higher multiplier than latter passes
fit_pass = ((10 - query_pass) * 100) / 10;
// store fitness and result
r->fitness = fit_listpos * fit_size * fit_square * fit_jpg * fit_pass;
results = eina_list_append(results, r);
// list position fitness goes down by .9 of previous list pos fitness
fit_listpos = (90 * fit_listpos) / 100;
if (fit_listpos < 1) fit_listpos = 1;
}
}
static int
_cb_fitness_sort(const void *data1, const void *data2)
{
const Result *r1 = data1, *r2 = data2;
if (r1->fitness < r2->fitness) return 1;
else if (r1->fitness > r2->fitness) return -1;
return 0;
}
static void
_cb_url_bin(void *data EINA_UNUSED, const void *result, size_t size)
{ // handle in memory fetch of image
// too big - 64M
if (size > (64 * 1024 * 1024)) return;
if (mem_data) free(mem_data);
mem_data = malloc(size);
mem_size = size;
memcpy(mem_data, result, size);
elm_exit();
}
static void
_thumb_online_search(const char *path)
{
Eina_Strbuf *query_buf;
const char *file, *ext;
Eina_List *l;
Result *r;
int i;
// get file and where extension starts to be removed
file = ecore_file_file_get(path);
ext = strchr(file, '.');
_video_metadata_get(path);
if ((title) || (album) || (artist))
{
query_buf = eina_strbuf_new();
if (artist)
{
eina_strbuf_append(query_buf, artist);
eina_strbuf_append(query_buf, " ");
}
if (artist)
{
eina_strbuf_append(query_buf, artist);
eina_strbuf_append(query_buf, " ");
}
if (title)
{
eina_strbuf_append(query_buf, title);
eina_strbuf_append(query_buf, " ");
}
thumb_search_image(eina_strbuf_string_get(query_buf), _cb_results, NULL);
eina_strbuf_free(query_buf);
// we have real metasata - make filename searches lhave lower
// fitness by bumping query pass
query_pass += 4;
free(title);
free(album);
free(artist);
}
// search using filename as our search
// search for munged filename + "album art"
query_buf = eina_strbuf_new();
if (ext) // append all but extension and dot
eina_strbuf_append_n(query_buf, file, ext - file);
else // append the whole filename
eina_strbuf_append(query_buf, file);
eina_strbuf_append(query_buf, " album art");
thumb_search_image(eina_strbuf_string_get(query_buf), _cb_results, NULL);
eina_strbuf_free(query_buf);
query_pass++;
// now search for just the munged filename without any extra string
query_buf = eina_strbuf_new();
if (ext) // append all but extension and dot
eina_strbuf_append_n(query_buf, file, ext - file);
else // append the whole filename
eina_strbuf_append(query_buf, file);
thumb_search_image(eina_strbuf_string_get(query_buf), _cb_results, NULL);
eina_strbuf_free(query_buf);
// sort results by fitness
results = eina_list_sort(results, eina_list_count(results),
_cb_fitness_sort);
i = 0;
EINA_LIST_FOREACH(results, l, r)
{
// get image max 64M
thumb_url_bin_get(r->url, 64 * 1024 * 1024, _cb_url_bin, NULL);
elm_run();
if (mem_data)
{
// add filled image to then size accordingly
_thumb_image_mem_set(mem_data, mem_size);
free(mem_data);
mem_data = NULL;
if ((iw > 0) && (ih > 0)) break;
i++;
}
// tried 10 - give up
if (i >= 10) break;
}
if (mem_data) free(mem_data);
}
static char *
_thumb_explicit_find(const char *path)
{
char *tmp = alloca(strlen(path) + 1 + 100);
char *dir, *fraw, *s;
const char *fname, *e, *c;
const char *ext[] = {
"png", "PNG",
"jpg", "JPG",
"jpeg", "JPEG",
"jpe", "JPE",
NULL };
const char *cover[] = {
"cover", "Cover", "COVER",
"front", "Front", "FRONT",
"folder", "Folder", "FOLDER",
".cover", ".Cover", ".COVER",
".front", ".Front", ".FRONT",
".folder", ".Folder", ".FOLDER",
NULL };
int i, j;
// from here example comments assume /dir/file.mp3 as the path
for (i = 0; (e = ext[i]) && e; i++)
{ // /dir/file.mp3.png etc.
sprintf(tmp, "%s.%s", path, e);
if (ecore_file_exists(tmp)) return strdup(tmp);
}
dir = ecore_file_dir_get(path);
if (!dir)
{ // if no dir we are /file.mp3 thus "" works find for following code
dir = strdup("");
if (!dir) return NULL;
}
fname = ecore_file_file_get(path);
if (!fname)
{ // this shouldn't happen - but handle it anyway
free(dir);
return NULL;
}
fraw = strdup(fname); // fraw will be filename for e.g. filename.mp3
if (!fraw)
{
free(dir);
return NULL;
}
s = strrchr(fraw, '.');
if (s) *s = 0;
for (i = 0; (e = ext[i]) && e; i++)
{ // /dir/file.png etc.
sprintf(tmp, "%s/.%s.%s", dir, fraw, e);
if (ecore_file_exists(tmp)) goto found;
}
for (i = 0; (e = ext[i]) && e; i++)
{ // /dir/file.mp3.png etc.
sprintf(tmp, "%s/.%s.%s", dir, fname, e);
if (ecore_file_exists(tmp)) goto found;
}
for (i = 0; (e = ext[i]) && e; i++)
{ // /dir/.file.png etc.
sprintf(tmp, "%s/.%s.%s", dir, fraw, e);
if (ecore_file_exists(tmp)) goto found;
}
for (i = 0; (e = ext[i]) && e; i++)
{ // /dir/.file.mp3.png etc.
sprintf(tmp, "%s/.thumb/%s.%s", dir, fname, e);
if (ecore_file_exists(tmp)) goto found;
}
for (i = 0; (e = ext[i]) && e; i++)
{ // /dir/.thumb/file.png etc.
sprintf(tmp, "%s/.thumb/%s.%s", dir, fraw, e);
if (ecore_file_exists(tmp)) goto found;
}
// XXX: should we do this for every file in that dir? really?
for (j = 0; (c = cover[j]) && c; j++)
{ // /dir/cover.png etc. - single img for everything in the dir...
for (i = 0; (e = ext[i]) && e; i++)
{
sprintf(tmp, "%s/%s.%s", dir, c, e);
if (ecore_file_exists(tmp)) goto found;
}
}
free(dir);
free(fraw);
return NULL;
found:
free(dir);
free(fraw);
return strdup(tmp);
}
int
thumb_music(Eet_File *ef, const char *path, const char *mime EINA_UNUSED, const char *thumb EINA_UNUSED)
{
const int sizes[] = { 512, 256, 128, 64, 32, 16, 0 };
int w, h, i;
char buf[128];
char *thumb_file;
_thumb_image_setup();
// look for an explicitly "requested" file for the thumb path
thumb_file = _thumb_explicit_find(path);
// if we didn't find an explicit matching thumb path in dir or nearby...
if (thumb_file) _thumb_image_file_set(thumb_file);
// explicit thumb not found or the load failed as image size is not sane
if ((iw <= 0) || (ih < 0)) _thumb_online_search(path);
// if size is bunk - we can't load it...
if ((iw <= 0) || (ih < 0)) return 2;
// write a big 1024x1024 preview (but no larger than the original image)
w = iw; h = ih; scale_out(&w, &h, 1024, 1024, EINA_FALSE);
// resize to target size
evas_object_resize(im, w, h);
evas_object_move(im, (1024 - w) / 2, 0);
evas_object_resize(subwin, 1024, 1024);
// render our current state and pick up pixel results
elm_win_render(subwin);
// save out preview size
snprintf(buf, sizeof(buf), "image/preview");
thumb_image_write(ef, buf, image, alpha, EINA_TRUE);
snprintf(buf, sizeof(buf), "%i %i", w, h);
eet_write(ef, "image/preview/size", buf, strlen(buf) + 1,
EET_COMPRESSION_NONE);
// multiple thumb sizes so can load/pick the best one at runtime
for (i = 0; sizes[i] != 0; i++)
{
// scale down and keep aspect
w = iw; h = ih; scale_out(&w, &h, sizes[i], sizes[i], EINA_FALSE);
// resize to target size
evas_object_resize(im, w, h);
evas_object_move(im, (sizes[i] - w) / 2, 0);
evas_object_resize(subwin, sizes[i], sizes[i]);
// render our current state and pick up pixel results
elm_win_render(subwin);
// save out thumb size
snprintf(buf, sizeof(buf), "image/thumb/%i", sizes[i]);
thumb_image_write(ef, buf, image, alpha, EINA_TRUE);
}
return 0;
}

View File

@ -0,0 +1,107 @@
// generate thumbnail for images
#include "thumb.h"
// XXX: can do progressive resize down ie scale to 512 then take 512 and
// halve to 256 then halve it to 128 etc. rather than render from orig to
// target size....
static Evas_Object *im = NULL;
static Eina_Bool alpha = EINA_FALSE;
static int iw = 0, ih = 0;
static void
_thumb_page_setup(void)
{ // create and show image
im = evas_object_image_filled_add(evas_object_evas_get(subwin));
evas_object_show(im);
}
static Eina_Bool
_thumb_page_file_set(const char *file, int page)
{ // set file to image, get size & alpha
char buf[32];
snprintf(buf, sizeof(buf), "%i", page);
evas_object_image_file_set(im, file, buf);
evas_object_image_size_get(im, &iw, &ih);
if ((iw > 0) && (ih > 0)) return EINA_TRUE;
alpha = evas_object_image_alpha_get(im);
return EINA_FALSE;
}
int
thumb_paged(Eet_File *ef, const char *path, const char *mime EINA_UNUSED, const char *thumb EINA_UNUSED)
{
const int sizes[] = { 512, 256, 128, 64, 32, 16, 0 };
int w, h, i;
char buf[128];
unsigned char a;
_thumb_page_setup();
// add filled image to then size accordingly
_thumb_page_file_set(path, 0);
// if size is bunk - we can't load it...
if ((iw <= 0) || (ih < 0)) return 2;
// write a big 1024x1024 preview (but no larger than the original doc)
w = iw; h = ih; scale(&w, &h, 1024, 1024, EINA_TRUE);
// resize to target size
evas_object_resize(im, w, h);
evas_object_resize(subwin, w, h);
// render our current state and pick up pixel results
elm_win_render(subwin);
// save out preview size
snprintf(buf, sizeof(buf), "image/preview");
thumb_image_write(ef, buf, image, alpha, EINA_TRUE);
snprintf(buf, sizeof(buf), "%i %i", w, h);
eet_write(ef, "image/preview/size", buf, strlen(buf) + 1,
EET_COMPRESSION_NONE);
// multiple thumb sizes so can load/pick the best one at runtime
for (i = 0; sizes[i] != 0; i++)
{
// scale down and keep aspect
w = sizes[i];
h = (ih * sizes[i]) / iw;
if (h > sizes[i])
{ // too tall = so limit height and scale down keeping aspect
h = sizes[i];
w = (iw * sizes[i]) / ih;
}
// resize to target size
evas_object_resize(im, w, h);
evas_object_resize(subwin, w, h);
// render our current state and pick up pixel results
elm_win_render(subwin);
// save out thumb size
snprintf(buf, sizeof(buf), "image/thumb/%i", sizes[i]);
thumb_image_write(ef, buf, image, alpha, EINA_TRUE);
}
// write original alpha flag
a = alpha;
if (alpha)
eet_write(ef, "orig/image/alpha", &a, 1,
EET_COMPRESSION_NONE);
// write original size
snprintf(buf, sizeof(buf), "%i %i", iw, ih);
eet_write(ef, "orig/image/size", buf, strlen(buf) + 1,
EET_COMPRESSION_NONE);
// write page previews of the first 32 pages
for (i = 0; i < 32; i++)
{
if (!_thumb_page_file_set(path, i)) break;
w = iw; h = ih; scale(&w, &h, 512, 512, EINA_TRUE);
snprintf(buf, sizeof(buf), "image/page/%i", i);
evas_object_resize(im, w, h);
evas_object_resize(subwin, w, h);
// render our current state and pick up pixel results
elm_win_render(subwin);
// save out thumb size
thumb_image_write(ef, buf, image, alpha, EINA_TRUE);
}
return 0;
}

View File

@ -0,0 +1,22 @@
#include "thumb.h"
void
thumb_image_write(Eet_File *ef, const char *key, Evas_Object *img, Eina_Bool a, Eina_Bool lossy)
{ // get rendered pixels from image representing subwin
void *pixels = evas_object_image_data_get(img, EINA_FALSE);
int w, h;
if (!pixels) return;
evas_object_image_size_get(img, &w, &h);
if (lossy)
eet_data_image_write(ef, key, pixels, w, h, a,
0, /* compr */
80, /* qual */
EET_IMAGE_JPEG);
else
eet_data_image_write(ef, key, pixels, w, h, a,
EET_COMPRESSION_HI, /* compr */
0, /* qual */
EET_IMAGE_LOSSLESS);
evas_object_image_data_set(img, pixels); // put pixels back we borrowed
}

View File

@ -0,0 +1,212 @@
// search for
#include "thumb.h"
// look for these divs:
//
// <div class="xxxx"...
// data-ow="640"
// data-oh="640"
// data-ou="http://xxxxx"
// >
//
// data-xxx could be in any order so parse tag until end of tag and store
// each X=Y
//
// other option - low res but google served imaage in img tags
//
// <img ...
// data-src="http://xxxx"
// >
typedef struct
{
Eina_Bool in_div;
Eina_Bool in_img;
Eina_Hash *attr;
void (*cb) (void *data, Eina_List *results_orig, Eina_List *results_cached);
void *data;
Eina_List *results_orig;
Eina_List *results_cached;
int magic;
} State;
static Eina_Bool
_cb_attr(void *data, const char *key, const char *val)
{
State *state = data;
// only if in div or img - add tag
if (state->in_div || state->in_img)
eina_hash_add(state->attr, key, strdup(val));
return EINA_TRUE;
}
static Eina_Bool
_cb_tag(void *data, Eina_Simple_XML_Type type,
const char *content, unsigned offset EINA_UNUSED, unsigned int len)
{
State *state = data;
const char *tags, *ow, *oh, *img;
int w, h;
Search_Result *res;
if (type == EINA_SIMPLE_XML_OPEN)
{ // start sokme tag
state->in_div = EINA_FALSE;
state->in_img = EINA_FALSE;
if (state->attr)
{ // clean up previous hash from previous tag if there
eina_hash_free(state->attr);
state->attr = NULL;
}
if (!strncmp(content, "div ", 4))
{ // we have a div - we're interested in those
state->in_div = EINA_TRUE;
state->attr = eina_hash_string_superfast_new(free);
}
else if (!strncmp(content, "img ", 4))
{ // we have an img - we're also interested in those
state->in_img = EINA_TRUE;
state->attr = eina_hash_string_superfast_new(free);
}
if ((state->in_div) || (state->in_img))
{ // pars the tag attributes
tags = eina_simple_xml_tag_attributes_find(content, len);
if (tags)
{ // pars a tag - div or img
eina_simple_xml_attributes_parse(tags,
len - (tags - content),
_cb_attr, state);
img = eina_hash_find(state->attr, "data-ou");
if (img)
{ // this is an origial url entry with size
ow = eina_hash_find(state->attr, "data-ow");
oh = eina_hash_find(state->attr, "data-oh");
if ((ow) && (oh))
{ // we need a width and height string and have them
w = atoi(ow);
h = atoi(oh);
res = calloc(1, sizeof(Search_Result));
if (res)
{
res->w = w;
res->h = h;
res->url = strdup(img);
state->results_orig =
eina_list_append(state->results_orig, res);
}
}
}
else
{
img = eina_hash_find(state->attr, "data-src");
if (img)
{ // this is a cached/scaled down version withotu size
res = calloc(1, sizeof(Search_Result));
if (res)
{
res->url = strdup(img);
state->results_cached =
eina_list_append(state->results_cached, res);
}
}
}
}
}
}
return EINA_TRUE;
}
static void
_cb_query(void *data, const char *reply)
{ // parse the query result from searching
State *state = data;
Search_Result *res;
if (!reply) goto err;
eina_simple_xml_parse(reply, strlen(reply), EINA_TRUE, _cb_tag, state);
if (state->attr)
{ // clean up any left over attributes hash
eina_hash_free(state->attr);
state->attr = NULL;
}
state->cb(state->data, state->results_orig, state->results_cached);
// clean up lists of results we no longer need
EINA_LIST_FREE(state->results_orig, res)
{
free(res->url);
free(res);
}
EINA_LIST_FREE(state->results_cached, res)
{
free(res->url);
free(res);
}
err:
elm_exit();
}
static Eina_Bool
_search_append(Eina_Strbuf *sb, const char *str, Eina_Bool hadword)
{ // take input string "as typed" and convert to part of query with +'s
// like "this is a set of words" -> "this+is+a+set+of+words"
// hadwords indicates if we had words before this call so to add a +
// at the start to appeand to previous words
const char *s;
Eina_Bool word = EINA_FALSE;
for (s = str; *s; s++)
{
// only use a-z, A-Z and 0-9 chars as words
if (((*s >= 'a') && (*s <= 'z')) ||
((*s >= 'A') && (*s <= 'Z')) ||
((*s >= '0') && (*s <= '9')))
{
if (!word)
{ // we're not inside a word now
if (hadword)
{ // we had a word before so add + between words
eina_strbuf_append_char(sb, '+');
word = EINA_FALSE;
}
}
eina_strbuf_append_char(sb, *s);
word = EINA_TRUE;
hadword = EINA_TRUE;
}
else word = EINA_FALSE;
// stop when we hit a .
if (*s == '.') break;
}
return hadword;
}
void
thumb_search_image(const char *str, void (*cb) (void *data, Eina_List *results_orig, Eina_List *results_cached), void *data)
{ // search for images given the str string
Eina_Strbuf *query_buf;
const char *query;
State state = { 0 };
state.cb = cb;
state.data = data;
state.magic = 1234567;
query_buf = eina_strbuf_new();
// add beginning of query
eina_strbuf_append
(query_buf,
"http://www.google.com/search?as_st=y&tbm=isch&hl=en&as_q=");
// add a search string like typed in like "this is a search"
_search_append(query_buf, str, EINA_FALSE);
// add rest of query
eina_strbuf_append
(query_buf,
"&as_epq=&as_oq=&as_eq=&cr=&as_sitesearch=&safe=images&tbs=ift:jpg");
query = eina_strbuf_string_get(query_buf);
// send that query off and handle it in _cb_query - max 8M
thumb_url_str_get(query, 8 * 1024 * 1024, _cb_query, &state);
elm_run();
eina_strbuf_free(query_buf);
}

View File

@ -0,0 +1,126 @@
#include "thumb.h"
static Ecore_Con_Url *fetch = NULL;
static Ecore_Event_Handler *handle_data = NULL;
static Ecore_Event_Handler *handle_complete = NULL;
static Eina_Strbuf *buf_str = NULL;
static size_t buf_str_max = 0;
static void *cb_str_data = NULL;
static void (*cb_str) (void *data, const char *result) = NULL;
static Eina_Strbuf *buf_bin = NULL;
static size_t buf_bin_max = 0;
static void *cb_bin_data = NULL;
static void (*cb_bin) (void *data, const void *result, size_t size) = NULL;
static Eina_Bool
_cb_http_data(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
{
Ecore_Con_Event_Url_Data *ev = event;
if (ev->url_con != fetch) return EINA_TRUE;
if (buf_str)
{
eina_strbuf_append_length(buf_str, (char *)ev->data, (size_t)ev->size);
if (eina_strbuf_length_get(buf_str) > buf_str_max)
{ // too big - abort whole fetch entirely
ecore_con_url_free(fetch);
fetch = NULL;
cb_str(cb_str_data, NULL);
cb_str = NULL;
cb_str_data = NULL;
eina_strbuf_free(buf_str);
buf_str = NULL;
}
}
else if (buf_bin)
{
eina_binbuf_append_length(buf_bin, (unsigned char *)ev->data, (size_t)ev->size);
if (eina_binbuf_length_get(buf_bin) > buf_bin_max)
{ // too big - abort whole fetch entirely
ecore_con_url_free(fetch);
fetch = NULL;
cb_bin(cb_bin_data, NULL, 0);
cb_bin = NULL;
cb_bin_data = NULL;
eina_strbuf_free(buf_bin);
buf_bin = NULL;
}
}
return EINA_FALSE;
}
static Eina_Bool
_cb_http_complete(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
{
Ecore_Con_Event_Url_Complete *ev = event;
Eina_Strbuf *buf;
if (ev->url_con != fetch) return EINA_TRUE;
if (buf_str)
{
buf = buf_str;
buf_str = NULL;
ecore_con_url_free(fetch);
fetch = NULL;
cb_str(cb_str_data,
eina_strbuf_string_get(buf));
cb_str = NULL;
cb_str_data = NULL;
eina_strbuf_free(buf);
}
else if (buf_bin)
{
buf = buf_bin;
buf_bin = NULL;
ecore_con_url_free(fetch);
fetch = NULL;
cb_bin(cb_bin_data,
eina_binbuf_string_get(buf),
eina_binbuf_length_get(buf));
cb_bin = NULL;
cb_bin_data = NULL;
eina_binbuf_free(buf);
}
return EINA_FALSE;
}
static Ecore_Con_Url *
_url_init(const char *url)
{
Ecore_Con_Url *f;
if (!handle_data)
handle_data = ecore_event_handler_add(ECORE_CON_EVENT_URL_DATA,
_cb_http_data, NULL);
if (!handle_complete)
handle_complete = ecore_event_handler_add(ECORE_CON_EVENT_URL_COMPLETE,
_cb_http_complete, NULL);
f = ecore_con_url_new(url);
ecore_con_url_additional_header_add
(f, "user-agent",
"Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19");
ecore_con_url_get(f);
return f;
}
void
thumb_url_str_get(const char *url, size_t max, void (*cb) (void *data, const char *result), const void *data)
{
cb_str_data = (void *)data;
cb_str = cb;
buf_str = eina_strbuf_new();
buf_str_max = max;
fetch = _url_init(url);
}
void
thumb_url_bin_get(const char *url, size_t max, void (*cb) (void *data, const void *result, size_t size), const void *data)
{
cb_bin_data = (void *)data;
cb_bin = cb;
buf_bin = eina_binbuf_new();
buf_bin_max = max;
fetch = _url_init(url);
}

View File

@ -0,0 +1,444 @@
// generate thumbnail for video files - look for posters on google or frames
#include "thumb.h"
#include <Emotion.h>
// XXX: can do progressive resize down ie scale to 512 then take 512 and
// halve to 256 then halve it to 128 etc. rather than render from orig to
// target size....
static Evas_Object *im = NULL;
static Eina_Bool alpha = EINA_FALSE;
static int iw = 0, ih = 0;
static Eina_List *results = NULL;
static void *mem_data = NULL;
static int mem_size = 0;
static int query_pass = 0;
static char *title = NULL;
static char *artist = NULL;
static char *album = NULL;
static double len = 0.0;
static double aspect = 0.0;
typedef struct
{
char *url;
int w, h;
unsigned long long fitness;
} Result;
static void
_thumb_image_setup(void)
{ // create and show image
im = evas_object_image_filled_add(evas_object_evas_get(subwin));
evas_object_show(im);
}
static void
_thumb_image_mem_set(void *data, int size)
{ // set file to image, get size & alpha
evas_object_image_memfile_set(im, data, size, "jpg", NULL);
evas_object_image_size_get(im, &iw, &ih);
alpha = evas_object_image_alpha_get(im);
}
static void
_thumb_image_file_set(const char *file, double pos)
{ // set file to image, get size & alpha
char buf[32];
if (pos >= 0.0)
{
snprintf(buf, sizeof(buf), "%llu", (unsigned long long)(pos * 1000.0));
evas_object_image_file_set(im, file, buf);
}
else evas_object_image_file_set(im, file, NULL);
evas_object_image_size_get(im, &iw, &ih);
alpha = evas_object_image_alpha_get(im);
}
static Eina_Bool
_cb_vid_open_done_timeout(void *data EINA_UNUSED)
{
elm_exit();
return EINA_FALSE;
}
static void
_cb_vid_open_done(void *data EINA_UNUSED, Evas_Object *obj, void *event EINA_UNUSED)
{ // we finished opening - get netadata also size/aspect
const char *s;
int w = 0, h = 0;
s = emotion_object_meta_info_get(obj, EMOTION_META_INFO_TRACK_TITLE);
if (s) title = strdup(s);
s = emotion_object_meta_info_get(obj, EMOTION_META_INFO_TRACK_ARTIST);
if (s) artist = strdup(s);
s = emotion_object_meta_info_get(obj, EMOTION_META_INFO_TRACK_ALBUM);
if (s) album = strdup(s);
len = emotion_object_play_length_get(obj);
aspect = emotion_object_ratio_get(obj);
if (aspect <= 0.0)
{
emotion_object_size_get(obj, &w, &h);
if (h > 0) aspect = (double)w / (double)h;
}
// finish loop
elm_exit();
}
static void
_video_metadata_get(const char *path)
{
Ecore_Timer *t;
Evas_Object *o = emotion_object_add(evas_object_evas_get(subwin));
evas_object_smart_callback_add(o, "open_done", _cb_vid_open_done, NULL);
emotion_object_file_set(o, path);
emotion_object_audio_mute_set(o, EINA_TRUE);
emotion_object_audio_volume_set(o, 0.0);
// a timeout for the loop
t = ecore_timer_add(10.0, _cb_vid_open_done_timeout, NULL);
elm_run();
ecore_timer_del(t);
evas_object_del(o);
}
static void
_cb_results(void *data EINA_UNUSED, Eina_List *results_orig, Eina_List *results_cached EINA_UNUSED)
{
Eina_List *l;
Search_Result *res;
Result *r;
unsigned long long fit_size, fit_ratio2to3, fit_jpg, fit_listpos, fit_pass;
// we need to re-score results first ratio 2:3 better than not suqare
// next - higher est better than lower rest
// ends in .jpg, .jpeg better than not
fit_listpos = 100;
EINA_LIST_FOREACH(results_orig, l, res)
{
// skip results that are 0 sized or with no url
if ((res->w <= 0) || (res->h <= 0) || (!res->url)) continue;
// new result
r = calloc(1, sizeof(Result));
if (!r) continue;
r->w = res->w;
r->h = res->h;
r->url = strdup(res->url);
// jpegs preferred
if ((eina_fnmatch("*.jpg", res->url, EINA_FNMATCH_CASEFOLD)) ||
(eina_fnmatch("*.jpeg", res->url, EINA_FNMATCH_CASEFOLD)) ||
(eina_fnmatch("*.jpe", res->url, EINA_FNMATCH_CASEFOLD)))
fit_jpg = 5000;
else
fit_jpg = 100;
// bigger is better
fit_size = (r->w / 10) * (r->h / 10);
// if it's bigger than 1000x1000 it's not really better
if (fit_size > 10000) fit_size = 10000;
// 2:3 poster better
fit_ratio2to3 = (100 * r->w * 3) / (r->h * 2);
if (fit_ratio2to3 > 100) fit_ratio2to3 = (100 * r->h * 2) / (r->w * 3);
fit_ratio2to3 *= 10; // ratio is VERY important
// first pass gets a higher multiplier than latter passes
fit_pass = (10 - query_pass) * 1000;
// store fitness and result
r->fitness = fit_listpos * fit_size * fit_ratio2to3 * fit_jpg * fit_pass;
results = eina_list_append(results, r);
// list position fitness goes down by .9 of previous list pos fitness
fit_listpos = (90 * fit_listpos) / 100;
if (fit_listpos < 1) fit_listpos = 1;
}
}
static int
_cb_fitness_sort(const void *data1, const void *data2)
{
const Result *r1 = data1, *r2 = data2;
if (r1->fitness < r2->fitness) return 1;
else if (r1->fitness > r2->fitness) return -1;
return 0;
}
static void
_cb_url_bin(void *data EINA_UNUSED, const void *result, size_t size)
{ // handle in memory fetch of image
// too big - 64M
if (size > (64 * 1024 * 1024)) return;
if (mem_data) free(mem_data);
mem_data = malloc(size);
mem_size = size;
memcpy(mem_data, result, size);
elm_exit();
}
static void
_thumb_online_search(const char *path)
{
Eina_Strbuf *query_buf;
const char *file, *ext;
Eina_List *l;
Result *r;
int i;
// get file and where extension starts to be removed
file = ecore_file_file_get(path);
ext = strchr(file, '.');
if ((title) || (album) || (artist))
{
query_buf = eina_strbuf_new();
if (artist)
{
eina_strbuf_append(query_buf, artist);
eina_strbuf_append(query_buf, " ");
}
if (artist)
{
eina_strbuf_append(query_buf, artist);
eina_strbuf_append(query_buf, " ");
}
if (title)
{
eina_strbuf_append(query_buf, title);
eina_strbuf_append(query_buf, " ");
}
eina_strbuf_append(query_buf, " movie poster");
thumb_search_image(eina_strbuf_string_get(query_buf), _cb_results, NULL);
eina_strbuf_free(query_buf);
// we have real metasata - make filename searches lhave lower
// fitness by bumping query pass
query_pass += 4;
free(title);
free(album);
free(artist);
}
// search using filename as our search
// search for munged filename + "album art"
query_buf = eina_strbuf_new();
if (ext) // append all but extension and dot
eina_strbuf_append_n(query_buf, file, ext - file);
else // append the whole filename
eina_strbuf_append(query_buf, file);
eina_strbuf_append(query_buf, " movie poster");
thumb_search_image(eina_strbuf_string_get(query_buf), _cb_results, NULL);
eina_strbuf_free(query_buf);
query_pass++;
// now search for just the munged filename without any extra string
query_buf = eina_strbuf_new();
if (ext) // append all but extension and dot
eina_strbuf_append_n(query_buf, file, ext - file);
else // append the whole filename
eina_strbuf_append(query_buf, file);
thumb_search_image(eina_strbuf_string_get(query_buf), _cb_results, NULL);
eina_strbuf_free(query_buf);
// sort results by fitness
results = eina_list_sort(results, eina_list_count(results),
_cb_fitness_sort);
i = 0;
EINA_LIST_FOREACH(results, l, r)
{
// get image max 64M
thumb_url_bin_get(r->url, 64 * 1024 * 1024, _cb_url_bin, NULL);
elm_run();
if (mem_data)
{
// add filled image to then size accordingly
_thumb_image_mem_set(mem_data, mem_size);
free(mem_data);
mem_data = NULL;
if ((iw > 0) && (ih > 0)) break;
i++;
}
// tried 10 - give up
if (i >= 10) break;
}
if (mem_data) free(mem_data);
}
static char *
_thumb_explicit_find(const char *path)
{
char *tmp = alloca(strlen(path) + 1 + 100);
char *dir, *fraw, *s;
const char *fname, *e;
const char *ext[] = {
"png", "PNG",
"jpg", "JPG",
"jpeg", "JPEG",
"jpe", "JPE",
NULL };
int i;
// from here example comments assume /dir/file.mp3 as the path
for (i = 0; (e = ext[i]) && e; i++)
{ // /dir/file.mp3.png etc.
sprintf(tmp, "%s.%s", path, e);
if (ecore_file_exists(tmp)) return strdup(tmp);
}
dir = ecore_file_dir_get(path);
if (!dir)
{ // if no dir we are /file.mp3 thus "" works find for following code
dir = strdup("");
if (!dir) return NULL;
}
fname = ecore_file_file_get(path);
if (!fname)
{ // this shouldn't happen - but handle it anyway
free(dir);
return NULL;
}
fraw = strdup(fname); // fraw will be filename for e.g. filename.mp3
if (!fraw)
{
free(dir);
return NULL;
}
s = strrchr(fraw, '.');
if (s) *s = 0;
for (i = 0; (e = ext[i]) && e; i++)
{ // /dir/file.png etc.
sprintf(tmp, "%s/.%s.%s", dir, fraw, e);
if (ecore_file_exists(tmp)) goto found;
}
for (i = 0; (e = ext[i]) && e; i++)
{ // /dir/file.mp3.png etc.
sprintf(tmp, "%s/.%s.%s", dir, fname, e);
if (ecore_file_exists(tmp)) goto found;
}
for (i = 0; (e = ext[i]) && e; i++)
{ // /dir/.file.png etc.
sprintf(tmp, "%s/.%s.%s", dir, fraw, e);
if (ecore_file_exists(tmp)) goto found;
}
for (i = 0; (e = ext[i]) && e; i++)
{ // /dir/.file.mp3.png etc.
sprintf(tmp, "%s/.thumb/%s.%s", dir, fname, e);
if (ecore_file_exists(tmp)) goto found;
}
for (i = 0; (e = ext[i]) && e; i++)
{ // /dir/.thumb/file.png etc.
sprintf(tmp, "%s/.thumb/%s.%s", dir, fraw, e);
if (ecore_file_exists(tmp)) goto found;
}
free(dir);
free(fraw);
return NULL;
found:
free(dir);
free(fraw);
return strdup(tmp);
}
static void
_thumb_file_snap_pos_set(Eet_File *ef, const char *path, int snap, double pos)
{
char buf[128];
int w, h;
_thumb_image_file_set(path, pos);
snprintf(buf, sizeof(buf), "image/snap/%i", snap);
w = iw; h = ih; scale(&w, &h, 512, 512, EINA_TRUE);
evas_object_resize(im, w, h);
evas_object_resize(subwin, w, h);
// render our current state and pick up pixel results
elm_win_render(subwin);
// save out thumb size
thumb_image_write(ef, buf, image, alpha, EINA_TRUE);
}
int
thumb_video(Eet_File *ef, const char *path, const char *mime EINA_UNUSED, const char *thumb EINA_UNUSED)
{
const int sizes[] = { 512, 256, 128, 64, 32, 16, 0 };
int w, h, i;
char buf[128];
char *thumb_file;
double p;
_thumb_image_setup();
// look for an explicitly "requested" file for the thumb path
thumb_file = _thumb_explicit_find(path);
// if we didn't find an explicit matching thumb path in dir or nearby...
if (thumb_file) _thumb_image_file_set(thumb_file, -1);
// explicit thumb not found or the load failed as image size is not sane
if ((iw <= 0) || (ih < 0))
{
_video_metadata_get(path);
if ((len > (65.0 * 60.0)) && // more than 65 mins - like movies
(aspect > 1.6)) // is 16:9 or wider like a movie
_thumb_online_search(path);
}
// if size is bunk - we hven;'t found something yet
if ((iw <= 0) || (ih < 0)) _thumb_image_file_set(path, len / 2.0);
// if size is bunk - we can't load it...
if ((iw <= 0) || (ih < 0)) return 2;
// write a big 1024x1024 preview (but no larger than the original image)
w = iw; h = ih; scale(&w, &h, 1024, 1024, EINA_FALSE);
// resize to target size
evas_object_resize(im, w, h);
evas_object_resize(subwin, w, h);
// render our current state and pick up pixel results
elm_win_render(subwin);
// save out preview size
snprintf(buf, sizeof(buf), "image/preview");
thumb_image_write(ef, buf, image, alpha, EINA_TRUE);
snprintf(buf, sizeof(buf), "%i %i", w, h);
eet_write(ef, "image/preview/size", buf, strlen(buf) + 1,
EET_COMPRESSION_NONE);
// multiple thumb sizes so can load/pick the best one at runtime
for (i = 0; sizes[i] != 0; i++)
{
// scale down and keep aspect
w = iw; h = ih; scale(&w, &h, sizes[i], sizes[i], EINA_FALSE);
// resize to target size
evas_object_resize(im, w, h);
evas_object_resize(subwin, w, h);
// render our current state and pick up pixel results
elm_win_render(subwin);
// save out thumb size
snprintf(buf, sizeof(buf), "image/thumb/%i", sizes[i]);
thumb_image_write(ef, buf, image, alpha, EINA_TRUE);
}
evas_object_del(im);
im = NULL;
_thumb_image_setup();
if (len < (1 * 60.0)) // less than 1 min
{
for (i = 0, p = 0.25; i < 3; i++, p += 0.25)
_thumb_file_snap_pos_set(ef, path, i, p * len);
}
else if (len < (10 * 60.0)) // less than 10 min
{
for (i = 0, p = 0.10; i < 9; i++, p += 0.10)
_thumb_file_snap_pos_set(ef, path, i, p * len);
}
else
{
for (i = 0, p = 0.05; i < 19; i++, p += 0.05)
_thumb_file_snap_pos_set(ef, path, i, p * len);
}
return 0;
}

1
src/backends/meson.build Normal file
View File

@ -0,0 +1 @@
subdir('default')

1604
src/efm/efm.c Normal file

File diff suppressed because it is too large Load Diff

66
src/efm/efm.h Normal file
View File

@ -0,0 +1,66 @@
#ifndef EFM_H
# define EFM_H 1
# include <Elementary.h>
#include "efm_config.h"
typedef enum {
EFM_VIEW_MODE_ICONS,
EFM_VIEW_MODE_ICONS_CUSTOM,
EFM_VIEW_MODE_LIST,
EFM_VIEW_MODE_LIST_DETAILED
} Efm_View_Mode;
typedef enum {
EFM_SORT_MODE_FLAGS = (0xffff << 16), // upper 16 bits for flags
// flags - apper 16 bits
EFM_SORT_MODE_DIRS_FIRST = ( 1 << 16), // show dirs first then files
EFM_SORT_MODE_NOCASE = ( 1 << 17), // don't account for case when sorting by label or path
EFM_SORT_MODE_LABEL_NOT_PATH = ( 1 << 18), // use label either in path filename or inside a desktop file
// mask for field
EFM_SORT_MODE_MASK = 0xffff, // lower 16 bits is the sort field
// fields
EFM_SORT_MODE_NAME = ( 0 << 0),
EFM_SORT_MODE_SIZE = ( 1 << 0),
EFM_SORT_MODE_DATE = ( 2 << 0),
EFM_SORT_MODE_MIME = ( 3 << 0),
EFM_SORT_MODE_USER = ( 4 << 0),
EFM_SORT_MODE_GROUP = ( 5 << 0),
EFM_SORT_MODE_PERMISSIONS = ( 6 << 0),
} Efm_Sort_Mode;
typedef enum {
EFM_FOCUS_DIR_UP,
EFM_FOCUS_DIR_DOWN,
EFM_FOCUS_DIR_LEFT,
EFM_FOCUS_DIR_RIGHT,
EFM_FOCUS_DIR_PGDN,
EFM_FOCUS_DIR_PGUP
} Efm_Focus_Dir;
Evas_Object *efm_add(Evas_Object *parent);
void efm_scroller_set(Evas_Object *obj, Evas_Object *scroller);
Evas_Object *efm_scroller_get(Evas_Object *obj);
Evas_Object *efm_detail_header_get(Evas_Object *obj);
void efm_path_set(Evas_Object *obj, const char *path);
const char *efm_path_get(Evas_Object *obj);
void efm_path_view_mode_set(Evas_Object *obj, Efm_View_Mode mode);
Efm_View_Mode efm_path_view_mode_get(Evas_Object *obj);
void efm_path_sort_mode_set(Evas_Object *obj, Efm_Sort_Mode mode);
Efm_Sort_Mode efm_path_sort_mode_get(Evas_Object *obj);
Evas_Coord efm_column_min_get(Evas_Object *obj, int col);
// XXX: set/get column sizes
// XXX: callback when column sizes change
// XXX: set/get icon size
// XXX: hidden files show set/get
#define DBG(...) EINA_LOG_DOM_DBG(_log_dom, __VA_ARGS__)
#define INF(...) EINA_LOG_DOM_INFO(_log_dom, __VA_ARGS__)
#define WRN(...) EINA_LOG_DOM_WARN(_log_dom, __VA_ARGS__)
#define ERR(...) EINA_LOG_DOM_ERR(_log_dom, __VA_ARGS__)
#define CRI(...) EINA_LOG_DOM_CRIT(_log_dom, __VA_ARGS__)
extern int _log_dom;
#endif

608
src/efm/efm_back_end.c Normal file
View File

@ -0,0 +1,608 @@
static void
_size_message(Evas_Object *o, double v)
{
Edje_Message_Float msg;
// display sqrt of 0.0-?1.0 so we don't have single huge files push every
// other bar out
if (v > 0.0) v = sqrt(v);
msg.val = v;
edje_object_message_send(o, EDJE_MESSAGE_FLOAT, 1, &msg);
edje_object_message_signal_process(o);
}
static void
_size_bars_update(Smart_Data *sd)
{
Eina_List *bl, *il;
Icon *icon;
Block *block;
const char *s;
if (sd->config.view_mode != EFM_VIEW_MODE_LIST_DETAILED) return;
EINA_LIST_FOREACH(sd->blocks, bl, block)
{
if (block->realized_num <= 0) continue;
EINA_LIST_FOREACH(block->icons, il, icon)
{
if (!icon->realized) continue;
s = cmd_key_find(icon->cmd, "size");
if (s)
{
unsigned long long size = atoll(s);
if (sd->file_max > 0)
_size_message(icon->o_list_detail_swallow2[0],
(double)size / (double)sd->file_max);
else
_size_message(icon->o_list_detail_swallow2[0], 0.0);
}
}
}
}
static void
_cb_size_bars_update_job(void *data)
{
Smart_Data *sd = data;
sd->size_bars_update_job = NULL;
_size_bars_update(sd);
}
static void
_size_bars_update_queue(Smart_Data *sd)
{
if (sd->size_bars_update_job) ecore_job_del(sd->size_bars_update_job);
sd->size_bars_update_job = ecore_job_add(_cb_size_bars_update_job, sd);
}
static void
_cb_size_max_update_job(void *data)
{
Smart_Data *sd = data;
Eina_List *il;
Icon *icon;
const char *s;
unsigned long long new_max = 0;
sd->size_max_update_job = NULL;
EINA_LIST_FOREACH(sd->icons, il, icon)
{
s = cmd_key_find(icon->cmd, "size");
if (s)
{
unsigned long long size = atoll(s);
if (size > new_max) new_max = size;
}
}
if (sd->file_max != new_max)
{
sd->file_max = new_max;
_size_bars_update_queue(sd);
}
}
static void
_size_bars_max_update_queue(Smart_Data *sd)
{
if (sd->size_max_update_job) ecore_job_del(sd->size_max_update_job);
sd->size_max_update_job = ecore_job_add(_cb_size_max_update_job, sd);
}
static void
_command(Smart_Data *sd, const char *cmd)
{
Cmd *c = cmd_parse(cmd);
Msg *msg;
void *ref;
if (!c) return;
// send the cmd to our thread to deal with
msg = eina_thread_queue_send(sd->thread_data->thq, sizeof(Msg), &ref);
msg->c = c;
eina_thread_queue_send_done(sd->thread_data->thq, ref);
}
static void
_process(Smart_Data_Thread *std, Ecore_Thread *th, Eina_List *batch)
{ // process a batch of commands that come from the back-end open process
Cmd *c, *c_tmp = NULL;
Eina_List *batch_new = NULL;
Eina_List *batch_tmp = NULL;
if (!batch) return;
// sort batch into batch_new where each set of things like add, mod, del
// if they are the same cmd then the files are sorted by path. we do this
// to speed up inserts in the main loop so we can assume a sorted list
// per batch. this speeds up file adds a lot as we can just do a walk of
// the batch (as long as commands stay the same like file-add) and then also
// walk the current file/icon list at the same time and skip to the insert
// spot which makes inserts very fast as both lists are known to be
// pre-sorted so at worst we walk N file icons in the current icon list
// per batch (and loading a dir is probably a series of batches of N
// file-add's where N is white reasonable
EINA_LIST_FREE(batch, c)
{
c->sort_mode = std->sd->config.sort_mode;
if (!batch_tmp)
{
batch_tmp = eina_list_append(batch_tmp, c);
c_tmp = c;
}
else if (!strcmp(c->command, c_tmp->command))
{
// works for file-add, file-del, file-mod
batch_tmp = eina_list_sorted_insert(batch_tmp, sort_cmd, c);
}
else
{
batch_new = eina_list_merge(batch_new, batch_tmp);
batch_tmp = NULL;
}
}
if (batch_tmp)
{
batch_new = eina_list_merge(batch_new, batch_tmp);
batch_tmp = NULL;
}
if (batch_new) ecore_thread_feedback(th, batch_new);
}
static Eina_Bool
_cb_exe_del(void *data, int ev_type EINA_UNUSED, void *event)
{
Smart_Data *sd = data;
Ecore_Exe_Event_Del *ev = event;
Eina_List *l;
Pending_Exe_Del *pend;
// remove this exited slave process from our pending exe deletions
// this list should be pretty small of pending deletions so we don't
// need to optimize this with a hash or whatever
EINA_LIST_FOREACH(_pending_exe_dels, l, pend)
{
if (pend->exe == ev->exe)
{
pend->exe = NULL;
if (pend->timer)
{
ecore_timer_del(pend->timer);
pend->timer = NULL;
}
free(pend);
_pending_exe_dels = eina_list_remove_list(_pending_exe_dels, l);
break;
}
}
if (ev->exe == sd->exe_open)
{ // this process exiting is the back-end open process for active view
printf("ERROR: back-end open process died unexpectedly\n");
return ECORE_CALLBACK_DONE;
}
return ECORE_CALLBACK_PASS_ON;
}
static Eina_Bool
_cb_exe_data(void *data, int ev_type EINA_UNUSED, void *event)
{
Smart_Data *sd = data;
Ecore_Exe_Event_Data *ev = event;
int i;
// if this exe doesn't match the view it is for - pass it on and skip
if (ev->exe != sd->exe_open) return ECORE_CALLBACK_PASS_ON;
// this exe data is for thijs view
for (i = 0; ev->lines[i].line; i++) _command(sd, ev->lines[i].line);
return ECORE_CALLBACK_DONE;
}
static Eina_Bool
_cb_exe_pending_timer(void *data)
{ // timeout trying to kill off back-end open process
Pending_Exe_Del *pend = data;
pend->timer = NULL;
_pending_exe_dels = eina_list_remove(_pending_exe_dels, pend);
if (pend->exe)
{ // forcibly kill the back-end process as it did not exit on its own
ecore_exe_kill(pend->exe);
pend->exe = NULL;
}
free(pend);
return EINA_FALSE;
}
static void
_cb_thread_main(void *data, Ecore_Thread *th)
{ // thread sits processing commands read from stdout from the back-end open
Smart_Data_Thread *std = data;
Msg *msg;
void *ref;
Cmd *c;
Eina_Bool block = EINA_FALSE;
const char *prev_cmd = NULL;
Eina_List *batch = NULL;
for (;;)
{ // sit in a loop soaking up commands on the input queue
if (ecore_thread_check(th)) break;
if (!block) msg = eina_thread_queue_poll(std->thq, &ref);
else
{
usleep(4000); // wait 4ms to collect more msg's
msg = eina_thread_queue_wait(std->thq, &ref);
block = EINA_FALSE;
}
if (msg)
{
if (!batch)
{
batch = eina_list_append(batch, msg->c);
eina_stringshare_replace(&(prev_cmd), msg->c->command);
}
else if ((prev_cmd) && (!strcmp(msg->c->command, prev_cmd)))
{
batch = eina_list_append(batch, msg->c);
}
else
{
_process(std, th, batch);
batch = NULL;
eina_stringshare_replace(&(prev_cmd), NULL);
batch = eina_list_append(batch, msg->c);
}
eina_thread_queue_wait_done(std->thq, ref);
}
else
{
block = EINA_TRUE;
_process(std, th, batch);
batch = NULL;
eina_stringshare_replace(&(prev_cmd), NULL);
}
}
EINA_LIST_FREE(batch, c)
{
cmd_free(c);
}
eina_stringshare_replace(&(prev_cmd), NULL);
}
static void
_cb_thread_notify(void *data, Ecore_Thread *th EINA_UNUSED, void *msg)
{ // handle data from the view thread to the UI - this will be a batch of cmds
Smart_Data_Thread *std = data;
Smart_Data *sd = std->sd;
Eina_List *batch = msg;
Cmd *c, *cprev = NULL;
Eina_List *l, *il2, *il = NULL;
Icon *icon, *icon2;
Block *block;
const char *file, *label, *s;
int file_adds = 0, file_dels = 0;
if (!sd)
{ // on cusp point - view gone but buffered thread feedback exists
EINA_LIST_FREE(batch, c) cmd_free(c);
return;
}
printf("XXXXX BATCH %i\n", eina_list_count(batch));
EINA_LIST_FOREACH(batch, l, c)
{
c->sort_mode = sd->config.sort_mode;
#define CMD_DONE cmd_free(c); continue
if (!strcmp(c->command, "list-begin"))
{
printf("XXXXX LIST BEGIN\n");
edje_object_part_text_set(sd->o_overlay_info,
"e.text.busy_label",
"Loading");
edje_object_signal_emit(sd->o_overlay_info,
"e,state,busy,start", "e");
CMD_DONE;
}
else if (!strcmp(c->command, "list-end"))
{
printf("XXXXX LIST END\n");
edje_object_signal_emit(sd->o_overlay_info,
"e,state,busy,stop", "e");
CMD_DONE;
}
file = cmd_key_find(c, "path");
printf("XXXXX [%s] [%s]\n", c->command, file);
if (file)
{
s = strrchr(file, '/');
if (s) file = s + 1;
if (file[0] == '.') // XXX filter dor files or not
{
CMD_DONE;
}
}
if ((!file) || (!file[0]))
{ // somehow we didn't get a sane filename from the back-end
CMD_DONE;
}
label = cmd_key_find(c, "link-label");
if (!label) label = cmd_key_find(c, "label");
if ((!cprev) || (!!strcmp(cprev->command, c->command)))
{ // we start a new batch of commands - these are sorted
il = sd->icons;
}
if (!strcmp(c->command, "file-add"))
{
icon = calloc(1, sizeof(Icon));
if (!icon) abort();
file_adds++;
icon->sd = sd;
icon->cmd = c;
icon->changed = EINA_TRUE;
icon->info.file = eina_stringshare_add(file);
eina_stringshare_replace(&(icon->info.label), label);
s = cmd_key_find(c, "label-clicked");
if (!s) s = cmd_key_find(c, "link-label-clicked");
eina_stringshare_replace(&(icon->info.label_clicked), s);
s = cmd_key_find(c, "label-selected");
if (!s) s = cmd_key_find(c, "link-label-selected");
eina_stringshare_replace(&(icon->info.label_selected), s);
s = cmd_key_find(c, "mime");
if (s) eina_stringshare_replace(&(icon->info.mime), s);
if (s) printf("XXXXX mime=%s\n", icon->info.mime);
s = cmd_key_find(c, "desktop-icon");
if (!s) s = cmd_key_find(c, "link-desktop-icon");
if (s) eina_stringshare_replace(&(icon->info.pre_lookup_icon), s);
s = cmd_key_find(c, "desktop-icon.lookup");
if (!s) s = cmd_key_find(c, "link-desktop-icon.lookup");
if (!s) s = cmd_key_find(c, "icon");
if ((s) && (s[0] == '/')) eina_stringshare_replace(&(icon->info.icon), s);
s = cmd_key_find(c, "link-desktop-icon-clicked");
if (!s) s = cmd_key_find(c, "desktop-icon-clicked");
if (s) eina_stringshare_replace(&(icon->info.icon_clicked), s);
s = cmd_key_find(c, "link-desktop-icon-selected");
if (!s) s = cmd_key_find(c, "desktop-icon-selected");
if (s) eina_stringshare_replace(&(icon->info.icon_selected), s);
s = cmd_key_find(c, "mime-icon");
if (s) eina_stringshare_replace(&(icon->info.mime_icon), s);
s = cmd_key_find(c, "thumb");
if (s) eina_stringshare_replace(&(icon->info.thumb), s);
s = cmd_key_find(c, "type");
if (s)
{
if (!strcmp(s, "link"))
{
icon->info.link = EINA_TRUE;
s = cmd_key_find(c, "broken-link");
if ((s) && (!strcmp(s, "true")))
icon->info.broken = EINA_TRUE;
s = cmd_key_find(c, "link-type");
}
if (s)
{
if (!strcmp(s, "dir"))
{
icon->info.dir = EINA_TRUE;
eina_stringshare_replace((&icon->info.mime),
"inode/directory");
}
else if (!strcmp(s, "block"))
{
icon->info.special = EINA_TRUE;
eina_stringshare_replace((&icon->info.mime),
"inode/blockdevice");
}
else if (!strcmp(s, "char"))
{
icon->info.special = EINA_TRUE;
eina_stringshare_replace((&icon->info.mime),
"inode/chardevice");
}
else if (!strcmp(s, "fifo"))
{
icon->info.special = EINA_TRUE;
eina_stringshare_replace((&icon->info.mime),
"inode/fifo");
}
else if (!strcmp(s, "socket"))
{
icon->info.special = EINA_TRUE;
eina_stringshare_replace((&icon->info.mime),
"inode/socket");
}
}
}
s = cmd_key_find(c, "size");
if (s)
{
unsigned long long size = atoll(s);
if (size > sd->file_max)
{
sd->file_max = size;
_size_bars_update_queue(sd);
}
}
if (!icon->info.mime)
eina_stringshare_replace((&icon->info.mime),
"inode/file");
for ( ; il; il = il->next)
{
icon2 = il->data;
if (!strcmp(file, icon2->info.file))
{ // handle the case we get an add for an existing file
file_dels++;
il2 = il->next;
sd->icons = eina_list_remove_list(sd->icons, il);
il = il2;
if (sd->last_focused)
{
// XXX: select prev or next icon
}
s = cmd_key_find(icon->cmd, "size");
if (s)
{
unsigned long long size = atoll(s);
if (size == sd->file_max)
{
_size_bars_max_update_queue(sd);
}
}
_icon_free(icon2);
if (il) icon2 = il->data;
else break;
}
if (sort_cmd(icon2->cmd, icon->cmd) > 0)
{
sd->icons = eina_list_prepend_relative_list
(sd->icons, icon, il);
break;
}
}
if (!il)
{
sd->icons = eina_list_append(sd->icons, icon);
}
}
else if (!strcmp(c->command, "file-del"))
{
for ( ; il; il = il->next)
{
icon = il->data;
if (!strcmp(file, icon->info.file))
{
file_dels++;
il2 = il->next;
sd->icons = eina_list_remove_list(sd->icons, il);
il = il2;
if (sd->last_focused)
{
// XXX: select prev or next icon
}
s = cmd_key_find(icon->cmd, "size");
if (s)
{
unsigned long long size = atoll(s);
if (size == sd->file_max)
{
_size_bars_max_update_queue(sd);
}
}
_icon_free(icon);
break;
}
}
cmd_free(c);
c = NULL;
}
else if (!strcmp(c->command, "file-mod"))
{
for ( ; il; il = il->next)
{
icon = il->data;
if (!strcmp(file, icon->info.file))
{
icon->changed = EINA_TRUE;
eina_stringshare_replace(&(icon->info.label), label);
s = cmd_key_find(c, "label-clicked");
if (!s) s = cmd_key_find(c, "link-label-clicked");
eina_stringshare_replace(&(icon->info.label_clicked), s);
s = cmd_key_find(c, "label-selected");
if (!s) s = cmd_key_find(c, "link-label-selected");
eina_stringshare_replace(&(icon->info.label_selected), s);
s = cmd_key_find(c, "mime");
if (s) eina_stringshare_replace(&(icon->info.mime), s);
if (!icon->info.mime)
eina_stringshare_replace((&icon->info.mime),
"inode/file");
s = cmd_key_find(c, "desktop-icon");
if (!s) s = cmd_key_find(c, "link-desktop-icon");
if (s) eina_stringshare_replace(&(icon->info.pre_lookup_icon), s);
s = cmd_key_find(c, "desktop-icon.lookup");
if (!s) s = cmd_key_find(c, "link-desktop-icon.lookup");
if (!s) s = cmd_key_find(c, "icon");
if ((s) && (s[0] == '/')) eina_stringshare_replace(&(icon->info.icon), s);
s = cmd_key_find(c, "link-desktop-icon-clicked");
if (!s) s = cmd_key_find(c, "desktop-icon-clicked");
if (s) eina_stringshare_replace(&(icon->info.icon_clicked), s);
s = cmd_key_find(c, "link-desktop-icon-selected");
if (!s) s = cmd_key_find(c, "desktop-icon-selected");
if (s) eina_stringshare_replace(&(icon->info.icon_selected), s);
s = cmd_key_find(c, "mime-icon");
if (s) eina_stringshare_replace(&(icon->info.mime_icon), s);
s = cmd_key_find(c, "thumb");
if (s) eina_stringshare_replace(&(icon->info.thumb), s);
s = cmd_key_find(c, "broken-link");
if ((s) && (!strcmp(s, "true")))
icon->info.broken = EINA_TRUE;
else
icon->info.broken = EINA_FALSE;
s = cmd_key_find(icon->cmd, "size");
if (s)
{
unsigned long long size = atoll(s);
if (size == sd->file_max)
{
_size_bars_max_update_queue(sd);
}
}
s = cmd_key_find(c, "size");
if (s)
{
unsigned long long size = atoll(s);
if (size > sd->file_max)
{
sd->file_max = size;
_size_bars_update_queue(sd);
}
}
cmd_free(icon->cmd);
icon->cmd = c;
break;
}
}
}
cprev = c;
}
eina_list_free(batch);
if ((sd->config.view_mode >= EFM_VIEW_MODE_LIST) &&
((file_adds > 0) || (file_dels > 0)))
{ // if it's one of the list modes, unrealize realized icons
EINA_LIST_FOREACH(sd->blocks, l, block)
{
if (block->realized_num <= 0) continue;
EINA_LIST_FOREACH(block->icons, il, icon)
{ // unrealize the icon - we odd/event forces this
if (!icon->realized) continue;
icon->realized = EINA_FALSE;
icon->block->realized_num--;
_icon_object_clear(icon);
}
}
}
if (sd->reblock_job) ecore_job_del(sd->reblock_job);
sd->reblock_job = ecore_job_add(_cb_reblock, sd);
}
static void
_cb_thread_done(void *data, Ecore_Thread *th EINA_UNUSED)
{
Smart_Data_Thread *std = data;
if (std->sd) std->sd->thread_data = NULL;
if (std->thq)
{
eina_thread_queue_free(std->thq);
std->thq = NULL;
}
free(std);
}

279
src/efm/efm_dnd.c Normal file
View File

@ -0,0 +1,279 @@
// utils for draga and drop handling
static Eina_Bool
_cb_dnd_scroll_timer(void *data)
{
Smart_Data *sd = data;
Evas_Coord fmx, fmy, sx, sy, sw, sh, x, y;
Evas_Coord scr_edge_x, scr_edge_y, scr_mul;
evas_object_geometry_get(sd->o_smart, &fmx, &fmy, NULL, NULL);
evas_object_geometry_get(sd->o_scroller, &sx, &sy, &sw, &sh);
scr_mul = 4;
scr_edge_x = sd->icon_min_w / 2;
scr_edge_y = sd->icon_min_h / 2;
if (sd->dnd_scroll_x < scr_edge_x)
x = sx - fmx + sd->dnd_scroll_x - scr_edge_x - ((scr_edge_x - sd->dnd_scroll_x) * scr_mul);
else if (sd->dnd_scroll_x > (sw - scr_edge_x))
x = sx - fmx + sd->dnd_scroll_x + scr_edge_x + ((sd->dnd_scroll_x - (sw - scr_edge_x)) * scr_mul);
else
x = sx - fmx + sd->dnd_scroll_x;
if (sd->dnd_scroll_y < scr_edge_y)
y = sy - fmy + sd->dnd_scroll_y - scr_edge_y - ((scr_edge_y - sd->dnd_scroll_y) * scr_mul);
else if (sd->dnd_scroll_y > (sh - scr_edge_y))
y = sy - fmy + sd->dnd_scroll_y + scr_edge_y + ((sd->dnd_scroll_y - (sh - scr_edge_y)) * scr_mul);
else
y = sy - fmy + sd->dnd_scroll_y;
printf("SSS %i %i | %i %i %ix%i | %i %i\n", x, y, sx, sy, sw, sh, sd->dnd_scroll_x, sd->dnd_scroll_y);
elm_scroller_region_bring_in(sd->o_scroller, x, y, 1, 1);
return EINA_TRUE;
}
static void
_dnd_scroll_handle(Smart_Data *sd, Evas_Coord x, Evas_Coord y)
{
sd->dnd_scroll_x = x;
sd->dnd_scroll_y = y;
if (!sd->dnd_scroll_timer)
sd->dnd_scroll_timer = ecore_timer_add
(SCROLL_DND_TIMER, _cb_dnd_scroll_timer, sd);
}
static void
_dnd_scroll_end_handle(Smart_Data *sd)
{
if (sd->dnd_scroll_timer)
{
ecore_timer_del(sd->dnd_scroll_timer);
sd->dnd_scroll_timer = NULL;
}
}
static void
_dnd_over(Icon *icon)
{
_icon_over_on(icon);
}
static void
_dnd_over_none_handle(Smart_Data *sd)
{
if (sd->over_icon) _icon_over_off(sd->over_icon);
}
static void
_dnd_over_handle(Smart_Data *sd, Evas_Coord x, Evas_Coord y)
{
Icon *icon;
Eina_List *bl, *il;
Block *block;
EINA_LIST_FOREACH(sd->blocks, bl, block)
{
if (!eina_rectangle_coords_inside(&(block->bounds), x, y)) continue;
EINA_LIST_FOREACH(block->icons, il, icon)
{
if (!eina_rectangle_coords_inside(&(icon->geom), x, y)) continue;
_dnd_over(icon);
return;
}
}
_dnd_over_none_handle(sd);
}
// drop handling
static void
_cb_drop_in(void *data EINA_UNUSED, Evas_Object *o EINA_UNUSED)
{
// Smart_Data *sd = data;
printf("XXX: drop in.....\n");
// XXX: call drop in smart callback}
}
static void
_cb_drop_out(void *data, Evas_Object *o EINA_UNUSED)
{
Smart_Data *sd = data;
_dnd_scroll_end_handle(sd);
_dnd_over_none_handle(sd);
printf("XXX: drop out.....\n");
// XXX: call drop out smart callback}
}
void
_cb_drop_pos(void *data, Evas_Object *o EINA_UNUSED, Evas_Coord x, Evas_Coord y, Elm_Xdnd_Action action EINA_UNUSED)
{
Smart_Data *sd = data;
Evas_Coord fmx, fmy, fmw, fmh;
Evas_Coord sx, sy, sw, sh;
sd->dnd_action = action;
evas_object_geometry_get(sd->o_smart, &fmx, &fmy, &fmw, &fmh);
evas_object_geometry_get(sd->o_scroller, &sx, &sy, &sw, &sh);
_dnd_scroll_handle(sd, x - sx, y - sy);
if (((x - sx) > 0) && ((y - sy) > 0) &&
((x - sx) < sw) && ((y - sy) < sh))
_dnd_over_handle(sd, x - fmx, y - fmy);
else
_dnd_over_none_handle(sd);
if (sd->over_icon) sd->drop_over = sd->over_icon;
else sd->drop_over = NULL;
}
static Eina_Bool
_cb_drop(void *data, Evas_Object *o EINA_UNUSED, Elm_Selection_Data *ev)
{
Smart_Data *sd = data;
char **plist, **p, *esc, *tmp;
if (sd->drop_over) printf("XXX: DND DROP OVER [%s]\n", sd->drop_over->info.file);
else printf("XXX: DND DROP ...\n");
tmp = malloc(ev->len + 1);
if (!tmp) goto err;
memcpy(tmp, ev->data, ev->len);
tmp[ev->len] = 0;
plist = eina_str_split(tmp, "\n", -1);
for (p = plist; *p != NULL; ++p)
{
if (**p)
{
esc = _escape_parse(*p);
if (!esc) continue;
printf("XXX: DROP FILE: [%s]\n", esc);
}
}
free(*plist);
free(plist);
free(tmp);
err:
sd->drop_over = NULL;
return EINA_TRUE;
}
static void
_drop_init(Smart_Data *sd)
{ // called once we have our elm scroller around our file view
elm_drop_target_add(sd->o_scroller,
ELM_SEL_FORMAT_URILIST,
_cb_drop_in, sd,
_cb_drop_out, sd,
_cb_drop_pos, sd,
_cb_drop, sd);
}
// drag handling
static Evas_Object *
_cb_drag_icon_add(void *data, Evas_Object *parent, Evas_Coord *xoff, Evas_Coord *yoff)
{
Icon *icon = data;
Icon *icon2;
Block *block;
Evas_Object *obj = icon->sd->o_smart;
Evas_Object *o;
const char *theme_edj_file;
Evas *e;
ENTRY NULL;
Eina_List *bl, *il;
int num = 0;
if (sd->config.view_mode >= EFM_VIEW_MODE_LIST)
{
EINA_LIST_FOREACH(sd->blocks, bl, block)
{
if (block->realized_num <= 0)
{
num += eina_list_count(block->icons);
continue;
}
EINA_LIST_FOREACH(block->icons, il, icon2)
{
if (icon == icon2) goto found;
num++;
}
num += eina_list_count(block->icons);
}
}
found:
e = evas_object_evas_get(parent);
theme_edj_file = elm_theme_group_path_find(NULL,
"e/fileman/default/icon/fixed");
_icon_object_add(icon, icon->sd, e, theme_edj_file, EINA_FALSE, num);
o = icon->o_base;
evas_object_resize(o, icon->geom.w, icon->geom.h);
if (xoff) *xoff = (icon->geom.x + icon->sd->geom.x);
if (yoff) *yoff = (icon->geom.y + icon->sd->geom.y);
if (xoff && yoff)
printf("DND: drag begin %p %p off: %i %i\n", obj, o, *xoff, *yoff);
else
printf("DND: drag begin %p %p\n", obj, o);
return o;
}
static void
_cb_drag_pos(void *data EINA_UNUSED, Evas_Object *obj_drag EINA_UNUSED, Evas_Coord x, Evas_Coord y, Elm_Xdnd_Action action)
{
printf("DND: %i %i act=%i\n", x, y, action);
}
static void
_cb_drag_accept(void *data EINA_UNUSED, Evas_Object *obj_drag EINA_UNUSED, Eina_Bool accept)
{
printf("DND: accept %i\n", accept);
}
static void
_cb_drag_done(void *data, Evas_Object *obj_drag EINA_UNUSED)
{
Icon *icon = data;
Evas_Object *obj;
if (icon->sd)
{
obj = icon->sd->o_smart;
ENTRY;
printf("DND: drag done %p %p\n", obj, obj_drag);
sd->drag = EINA_FALSE;
icon->o_base = NULL; // try and not del this as dnd will do it
icon->sd->drag_icon = NULL;
}
_icon_free(icon);
}
static void
_drag_start(Icon *icon)
{
Eina_Strbuf *strbuf;
Icon *icon_dup;
strbuf = eina_strbuf_new();
if (!strbuf) return;
icon_dup = _icon_dup(icon);
if (!icon_dup) goto err;
if (icon->sd->rename_icon) _icon_rename_end(icon->sd->rename_icon);
printf("XXX: begin dnd\n");
icon->sd->drag = EINA_TRUE;
icon->sd->just_dragged = EINA_TRUE;
icon->sd->drag_icon = icon_dup;
if (_selected_icons_uri_strbuf_append(icon->sd, strbuf))
{ // begin the drag with that list of files
// XXX: if you use no modifier - do default (move)
// XXX: if you hold ctrl then do copy
// XXX: on win ... to/from removable drive it copies always
// XXX: if you hold shift then do move
// XXX; on mac option key forces a copy normally except from
// removable device then it forces a move
// XXX: this "except to/from removable drive" is bad/inconsistent imho
elm_drag_start(icon_dup->sd->o_scroller,
ELM_SEL_FORMAT_URILIST,
eina_strbuf_string_get(strbuf),
ELM_XDND_ACTION_MOVE,
_cb_drag_icon_add, icon_dup,
_cb_drag_pos, icon_dup,
_cb_drag_accept, icon_dup,
_cb_drag_done, icon_dup);
}
err:
eina_strbuf_free(strbuf);
}

650
src/efm/efm_icon.c Normal file
View File

@ -0,0 +1,650 @@
#include "efm_icon.h"
#include <Emotion.h>
// XXX: support animated files (anim gif etc.)
// XXX: support video files (mp4 etc.)
// XXX: support edj files ???
// XXX: url's ?
typedef struct _Smart_Data Smart_Data;
struct _Smart_Data
{
Evas_Object_Smart_Clipped_Data __clipped_data;
Eina_Rectangle geom;
Evas_Object *o_smart; // the smart object container itself
Evas_Object *o_image; // the image to be shown right now
Evas_Object *o_image2; // the image being loaded still
Evas_Object *o_video; // the image used for video/audio playback
Ecore_Timer *settle_timer; // time to figure out when
Ecore_Job *wakeup; // a job to wake up the loop
Eina_Stringshare *file; // file path or...
Eina_Stringshare *thumb; // thumb path
Eina_Stringshare *video; // video path
Ecore_Timer *anim_timer; // timer for animation frame flipping
int load_size; // the sie we want to load now
int orig_w, orig_h; // the sie of the img we loaded
int frame; // current frame to display
int frame_count; // number of frames in anim
double video_ratio; // aspect ratio for videos
Evas_Image_Animated_Loop_Hint loop_type; // animated loop type
Eina_Bool alpha : 1; // does the img have alpha
Eina_Bool svg : 1; // is the img a svg
Eina_Bool newfile : 1; // did we just set a new file
Eina_Bool animated : 1; // is this animated?
Eina_Bool vid_stream : 1; // is this video (has video frames?)
Eina_Bool audio : 1; // is this audio (has audo)
Eina_Bool mono_thumb : 1; // is thumb of mono white alpha img
};
static Evas_Smart *_smart = NULL;
static Evas_Smart_Class _sc = EVAS_SMART_CLASS_INIT_NULL;
static Evas_Smart_Class _sc_parent = EVAS_SMART_CLASS_INIT_NULL;
static void _image_add(Smart_Data *sd);
static int _size_choose(Smart_Data *sd, const int *sizes);
static void _cb_wakeup(void *data);
static void _wakeup(Smart_Data *sd);
static void _image_file_set(Smart_Data *sd);
static void _image_thumb_set(Smart_Data *sd);
static void _image_resized(Smart_Data *sd);
static Eina_Bool _cb_settle_timer(void *data);
static void _cb_image_preload(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED);
static void _handle_frame(Smart_Data *sd);
// sizes stored in thumbnail files that we might want to look at
static const int _thumb_sizes[] = { 512, 256, 128, 64, 32, 16, 0 };
// sizes we might want to load/render svg's at
static const int _svg_sizes[] = { 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 0 };
#define ENTRY Smart_Data *sd = evas_object_smart_data_get(obj); if (!sd) return
static void
_image_add(Smart_Data *sd)
{ // add a new hidden image object (image2) that will be busy loading
Evas_Object *o;
if (sd->o_image2) evas_object_del(sd->o_image2);
sd->o_image2 = o = evas_object_image_filled_add
(evas_object_evas_get(sd->o_smart));
evas_object_image_scale_hint_set(o, EVAS_IMAGE_SCALE_HINT_STATIC);
evas_object_smart_member_add(o, sd->o_smart); // this is a member
evas_object_image_load_head_skip_set(o, EINA_TRUE); // fileset no load head
// when the image is fully loaded then call this callback so its all
// ready and no more i/o is happening (this happens in a thread)
evas_object_event_callback_add(o, EVAS_CALLBACK_IMAGE_PRELOADED,
_cb_image_preload, sd);
}
static int
_size_choose(Smart_Data *sd, const int *sizes)
{ // find the next size up we want given the current object size
int i, max_size = 0;
// use the biggest axis as our desired size
if (sd->geom.w > sd->geom.h) max_size = sd->geom.w;
else max_size = sd->geom.h;
for (i = 0; sizes[i] > 0; i++)
{
if (i > 0)
{ // if we're not the first item in the sizes
// if size is bigger than this slot - return the size up
if (max_size > sizes[i]) return sizes[i - 1];
}
else
{ // we're after the first item in our sizes so use biggest
if (max_size > sizes[i]) return sizes[i];
}
}
return sizes[i - 1]; // no size found - choose smallest
}
static void
_cb_wakeup(void *data)
{ // we woke up - mark obj as changed....
Smart_Data *sd = data;
sd->wakeup = NULL;
evas_object_smart_changed(sd->o_smart);
}
static void
_wakeup(Smart_Data *sd)
{ // wake up the main loop so we spin around and do things
if (sd->wakeup) ecore_job_del(sd->wakeup);
sd->wakeup = ecore_job_add(_cb_wakeup, sd);
}
static void
_image_file_set_final(Smart_Data *sd, const char *file, const char *group)
{ // complete image file load/setup, gtrigger preload etc.
const char *ext;
// skip head will make animated not work - so skip it if format might
// be animated so we can flip frames
ext = strrchr(file, '.');
if (ext && ((!strcasecmp(ext, ".gif")) ||
(!strcasecmp(ext, ".webp")) ||
(!strcasecmp(ext, ".avif")) ||
(!strcasecmp(ext, ".avifs")) ||
(!strcasecmp(ext, ".jxl"))))
evas_object_image_load_head_skip_set(sd->o_image2, EINA_FALSE);
evas_object_image_file_set(sd->o_image2, file, group);
evas_object_image_preload(sd->o_image2, EINA_FALSE);
evas_object_smart_changed(sd->o_smart);
_wakeup(sd);
}
static void
_image_file_set(Smart_Data *sd)
{ // set a generic target image file
int load_size = _size_choose(sd, _svg_sizes);
if (sd->o_video)
{ // nuke previous video object if there
evas_object_del(sd->o_video);
sd->o_video = NULL;
}
if (sd->svg)
{ // if it's a svg file...
if (!sd->settle_timer)
{ // if we dont have a pending settle timer anymore use an EXACT sz
if (sd->geom.w > sd->geom.h) load_size = sd->geom.w;
else load_size = sd->geom.h;
}
}
// we already have this thumbnail size so no point loading it again
if (load_size == sd->load_size) return;
_image_add(sd);
sd->load_size = load_size;
if (sd->svg)
{ // it's a svg so tell the svg loader to render at this size
if (sd->load_size == 0) return;
evas_object_image_load_size_set(sd->o_image2,
sd->load_size, sd->load_size);
}
_image_file_set_final(sd, sd->file, NULL);
}
static void
_image_thumb_set(Smart_Data *sd)
{ // set a thumb file from our stored thumbnails
char buf[64];
int load_size = _size_choose(sd, _thumb_sizes);
// we already have this thumbnail size so no point loading it again
if (load_size == sd->load_size) return;
_image_add(sd);
if (sd->newfile)
{
Eet_File *ef = eet_open(sd->thumb, EET_FILE_MODE_READ);
if (ef)
{
int size;
char *mono = eet_read(ef, "image/thumb/mono", &size);
if (mono)
{
sd->mono_thumb = *mono;
free(mono);
}
eet_close(ef);
}
}
sd->load_size = load_size;
snprintf(buf, sizeof(buf), "image/thumb/%i", sd->load_size);
_image_file_set_final(sd, sd->thumb, buf);
}
static void
_cb_vid_frame(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{ // we decoded a frame - make sure vide3o is shown now we have a frame
Smart_Data *sd = data;
evas_object_show(sd->o_video);
}
static void
_cb_vid_resize(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{ // the video changed video size - tell owner we loaded or resized
Smart_Data *sd = data;
emotion_object_size_get(sd->o_video, &(sd->orig_w), &(sd->orig_h));
sd->video_ratio = emotion_object_ratio_get(sd->o_video);
if (sd->newfile)
{ // it's a new file, so say we loaded as its the first time
evas_object_smart_callback_call(sd->o_smart, "loaded", NULL);
sd->newfile = EINA_FALSE;
}
else // already playing the file - not new, si resized
evas_object_smart_callback_call(sd->o_smart, "resized", NULL);
}
static void
_cb_vid_open_done(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{ // we finished opening - now go to 0 and play
Smart_Data *sd = data;
emotion_object_position_set(sd->o_video, 0.0);
emotion_object_play_set(sd->o_video, EINA_TRUE);
}
static void
_cb_vid_play_finish(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{ // we finished playing go back to 0 and restart so we loop
Smart_Data *sd = data;
emotion_object_play_set(sd->o_video, EINA_FALSE);
emotion_object_position_set(sd->o_video, 0.0);
emotion_object_play_set(sd->o_video, EINA_TRUE);
}
static void
_image_video_set(Smart_Data *sd)
{ // set a video file
static Eina_Bool emotion_initted = EINA_FALSE;
Evas_Object *o;
if (sd->o_image)
{ // nuke previous image object if there
evas_object_del(sd->o_image);
sd->o_image = NULL;
}
if (sd->o_image2)
{ // nuke previous image object if there
evas_object_del(sd->o_image2);
sd->o_image2 = NULL;
}
if (sd->o_video)
{ // nuke previous video object if there
evas_object_del(sd->o_video);
sd->o_video = NULL;
}
if (!emotion_initted)
{ // emotion needs an init - so init it first time only
emotion_init();
emotion_initted = EINA_TRUE;
}
// XXX: sometimes this stalls because gstreamer is updating registry data
// in gstreamer_module_init -> gst_init_check -> g_option_context_parse
// -> gst_update_registry -> gst_poll_wait
sd->o_video = o = emotion_object_add(evas_object_evas_get(sd->o_smart));
emotion_object_keep_aspect_set(o, EMOTION_ASPECT_KEEP_NONE);
evas_object_smart_member_add(o, sd->o_smart); // this is a member
evas_object_smart_callback_add(o, "frame_decode", _cb_vid_frame, sd);
evas_object_smart_callback_add(o, "frame_resize", _cb_vid_resize, sd);
evas_object_smart_callback_add(o, "open_done", _cb_vid_open_done, sd);
evas_object_smart_callback_add(o, "playback_finished", _cb_vid_play_finish, sd);
// other callbacks - we don't need these for now
// evas_object_smart_callback_add(o, "decode_stop", _cb_vid_stop, obj);
// evas_object_smart_callback_add(o, "progress_change", _cb_vid_progress, obj);
// evas_object_smart_callback_add(o, "position_update", _cb_position_update, obj);
// evas_object_smart_callback_add(o, "length_change", _cb_length_change, obj);
// evas_object_smart_callback_add(o, "title_change", _cb_title_change, obj);
// evas_object_smart_callback_add(o, "audio_level_change", _cb_audio_change, obj);
// evas_object_smart_callback_add(o, "playback_started", _cb_play_start, obj);
emotion_object_file_set(o, sd->video);
emotion_object_audio_mute_set(o, EINA_TRUE);
emotion_object_audio_volume_set(o, 0.0);
}
static void
_image_resized(Smart_Data *sd)
{ // the image was just resized to handle that settling "idle" timer
if (sd->svg)
{ // it's an svg - so set up a timer so if we dont resize again for 0.2s
if (sd->settle_timer) ecore_timer_reset(sd->settle_timer);
else sd->settle_timer = ecore_timer_add(0.2, _cb_settle_timer, sd);
}
else
{ // not a svg - nuke settle timer from space - the only way to be sure
if (sd->settle_timer)
{
ecore_timer_del(sd->settle_timer);
sd->settle_timer = NULL;
}
}
}
static void
_image_update(Smart_Data *sd)
{ // update the image - perhaps size changed...
if (sd->thumb) _image_thumb_set(sd);
else if (sd->file) _image_file_set(sd);
}
static Eina_Bool
_cb_settle_timer(void *data)
{ // when the image settles and stops being resized after a bit...
Smart_Data *sd = data;
sd->settle_timer = NULL;
_image_update(sd);
return EINA_FALSE;
}
static int
_frame_num_get(Smart_Data *sd)
{
int fr, fr2;
// ping pong - first and last frame only appear once at each end
if (sd->loop_type == EVAS_IMAGE_ANIMATED_HINT_PINGPONG)
{
fr = sd->frame % ((sd->frame_count * 2) - 2);
fr2 = sd->frame % sd->frame_count + 1;
if (fr >= sd->frame_count) fr = sd->frame_count - 1 - fr2;
}
// loop
else fr = sd->frame % sd->frame_count;
return fr;
}
static Eina_Bool
_cb_anim_timer(void *data)
{
Smart_Data *sd = data;
int fr;
sd->anim_timer = NULL;
sd->frame++;
fr = _frame_num_get(sd);
printf("T: %1.3f %i / %i\n", ecore_time_get(), fr, sd->frame_count);
evas_object_image_animated_frame_set(sd->o_image, fr);
_handle_frame(sd);
return EINA_FALSE;
}
static void
_handle_frame(Smart_Data *sd)
{
int fr;
double t;
fr = _frame_num_get(sd);
t = evas_object_image_animated_frame_duration_get(sd->o_image, fr, 0);
if (sd->anim_timer) ecore_timer_del(sd->anim_timer);
sd->anim_timer = ecore_timer_add(t, _cb_anim_timer, sd);
}
static void
_cb_image_preload(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{ // whenb an image that was being loaded in the background has now finished
Smart_Data *sd = data;
if (sd->o_image) evas_object_del(sd->o_image);
sd->o_image = sd->o_image2;
if (!sd->o_image2) return;
sd->o_image2 = NULL;
evas_object_image_size_get(sd->o_image, &(sd->orig_w), &(sd->orig_h));
sd->alpha = evas_object_image_alpha_get(sd->o_image);
evas_object_show(sd->o_image);
if (sd->newfile)
{
sd->newfile = EINA_FALSE;
evas_object_smart_callback_call(sd->o_smart, "loaded", NULL);
}
sd->animated = evas_object_image_animated_get(sd->o_image);
if (sd->animated)
{
sd->loop_type = evas_object_image_animated_loop_type_get(sd->o_image);
sd->frame_count = evas_object_image_animated_frame_count_get(sd->o_image);
if (sd->frame_count < 3) sd->loop_type = EVAS_IMAGE_ANIMATED_HINT_LOOP;
else if (sd->frame_count < 2) sd->animated = EINA_FALSE;
if (sd->animated) _handle_frame(sd);
}
}
// gui code
static void
_smart_add(Evas_Object *obj)
{ // create a new efm icon
Smart_Data *sd;
sd = calloc(1, sizeof(Smart_Data));
if (!sd) return;
evas_object_smart_data_set(obj, sd);
_sc_parent.add(obj);
sd->o_smart = obj;
}
static void
_smart_del(Evas_Object *obj)
{ // delete/free efm view
ENTRY;
if (sd->o_image)
{
evas_object_del(sd->o_image);
sd->o_image = NULL;
}
if (sd->o_image2)
{
evas_object_del(sd->o_image2);
sd->o_image2 = NULL;
}
if (sd->o_video)
{
evas_object_del(sd->o_video);
sd->o_video = NULL;
}
if (sd->settle_timer)
{
ecore_timer_del(sd->settle_timer);
sd->settle_timer = NULL;
}
if (sd->wakeup)
{
ecore_job_del(sd->wakeup);
sd->wakeup = NULL;
}
if (sd->anim_timer)
{
ecore_timer_del(sd->anim_timer);
sd->anim_timer = NULL;
}
eina_stringshare_replace(&(sd->thumb), NULL);
eina_stringshare_replace(&(sd->file), NULL);
eina_stringshare_replace(&(sd->video), NULL);
_sc_parent.del(obj);
evas_object_smart_data_set(obj, NULL);
}
static void
_smart_move(Evas_Object *obj, Evas_Coord x, Evas_Coord y)
{ // efm icon object moved
ENTRY;
if ((sd->geom.x == x) && (sd->geom.y == y)) return;
sd->geom.x = x;
sd->geom.y = y;
evas_object_smart_changed(obj);
}
static void
_smart_resize(Evas_Object *obj, Evas_Coord w, Evas_Coord h)
{ // efm icon object resized
ENTRY;
if ((sd->geom.w == w) && (sd->geom.h == h)) return;
sd->geom.w = w;
sd->geom.h = h;
_image_resized(sd);
_image_update(sd);
evas_object_smart_changed(obj);
}
static void
_smart_calculate(Evas_Object *obj)
{ // recalce position/size
ENTRY;
if (sd->o_image)
evas_object_geometry_set(sd->o_image,
sd->geom.x, sd->geom.y,
sd->geom.w, sd->geom.h);
if (sd->o_image2)
evas_object_geometry_set(sd->o_image2,
sd->geom.x, sd->geom.y,
sd->geom.w, sd->geom.h);
if (sd->o_video)
evas_object_geometry_set(sd->o_video,
sd->geom.x, sd->geom.y,
sd->geom.w, sd->geom.h);
_image_update(sd);
}
Evas_Object *
efm_icon_add(Evas_Object *parent)
{ // add new icon object
if (!_smart)
{
evas_object_smart_clipped_smart_set(&_sc_parent);
_sc = _sc_parent;
_sc.name = "efm_icon";
_sc.version = EVAS_SMART_CLASS_VERSION;
_sc.add = _smart_add;
_sc.del = _smart_del;
_sc.resize = _smart_resize;
_sc.move = _smart_move;
_sc.calculate = _smart_calculate;
};
if (!_smart) _smart = evas_smart_class_new(&_sc);
return evas_object_smart_add(evas_object_evas_get(parent), _smart);
}
void
efm_icon_file_set(Evas_Object *obj, const char *file)
{ // set a regular file as the icon
ENTRY;
if ((sd->file) && (file) && (!strcmp(sd->file, file))) return;
if ((!sd->file) && (!file)) return;
eina_stringshare_replace(&(sd->thumb), NULL);
eina_stringshare_replace(&(sd->file), file);
eina_stringshare_replace(&(sd->video), NULL);
sd->svg = EINA_FALSE;
if ((sd->file) &&
((eina_fnmatch("*.svg", sd->file, EINA_FNMATCH_CASEFOLD) ||
eina_fnmatch("*.svgz", sd->file, EINA_FNMATCH_CASEFOLD) ||
eina_fnmatch("*.svg.gz", sd->file, EINA_FNMATCH_CASEFOLD))))
sd->svg = EINA_TRUE;
sd->load_size = -1;
sd->newfile = EINA_TRUE;
sd->mono_thumb = EINA_FALSE;
sd->frame = 0;
if (sd->anim_timer)
{
ecore_timer_del(sd->anim_timer);
sd->anim_timer = NULL;
}
if (sd->settle_timer)
{ // thumbnails dont need settle timers like svg's might
ecore_timer_del(sd->settle_timer);
sd->settle_timer = NULL;
}
_image_resized(sd);
_image_file_set(sd);
}
void
efm_icon_thumb_set(Evas_Object *obj, const char *thumb)
{ // specifically add generated thumb file
ENTRY;
if ((sd->thumb) && (thumb) && (!strcmp(sd->thumb, thumb))) return;
if ((!sd->thumb) && (!thumb)) return;
eina_stringshare_replace(&(sd->file), NULL);
eina_stringshare_replace(&(sd->thumb), thumb);
eina_stringshare_replace(&(sd->video), NULL);
sd->svg = EINA_FALSE;
sd->load_size = -1;
sd->newfile = EINA_TRUE;
sd->mono_thumb = EINA_FALSE;
sd->frame = 0;
if (sd->anim_timer)
{
ecore_timer_del(sd->anim_timer);
sd->anim_timer = NULL;
}
if (sd->settle_timer)
{ // thumbnails dont need settle timers like svg's might
ecore_timer_del(sd->settle_timer);
sd->settle_timer = NULL;
}
_image_thumb_set(sd);
}
void
efm_icon_video_set(Evas_Object *obj, const char *video)
{ // specifically load file as a video (or audio) and not as a still image
ENTRY;
if ((sd->video) && (video) && (!strcmp(sd->video, video))) return;
if ((!sd->video) && (!video)) return;
eina_stringshare_replace(&(sd->file), NULL);
eina_stringshare_replace(&(sd->thumb), NULL);
eina_stringshare_replace(&(sd->video), video);
sd->svg = EINA_FALSE;
sd->load_size = -1;
sd->newfile = EINA_TRUE;
sd->mono_thumb = EINA_FALSE;
sd->frame = 0;
sd->video_ratio = 0.0;
if (sd->anim_timer)
{
ecore_timer_del(sd->anim_timer);
sd->anim_timer = NULL;
}
if (sd->settle_timer)
{ // thumbnails dont need settle timers like svg's might
ecore_timer_del(sd->settle_timer);
sd->settle_timer = NULL;
}
_image_resized(sd);
_image_video_set(sd);
}
void
efm_icon_size_get(Evas_Object *obj, int *w, int *h)
{ // get image pixel size
ENTRY;
if ((sd->o_video) && (sd->video_ratio > 0.0))
{
int vid_w, vid_h;
vid_w = ((double)sd->orig_h + 0.5) * sd->video_ratio;
vid_h = sd->orig_h;
if (w) *w = vid_w;
if (h) *h = vid_h;
}
else
{
if (w) *w = sd->orig_w;
if (h) *h = sd->orig_h;
}
}
Eina_Bool
efm_icon_alpha_get(Evas_Object *obj)
{ // get image alpha flag
ENTRY EINA_FALSE;
return sd->alpha;
}
Eina_Bool
efm_icon_mono_get(Evas_Object *obj)
{ // get thumb mono flag
ENTRY EINA_FALSE;
return sd->mono_thumb;
}

17
src/efm/efm_icon.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef EFM_ICON_H
# define EFM_ICON_H 1
# include <Elementary.h>
Evas_Object *efm_icon_add(Evas_Object *parent);
void efm_icon_file_set(Evas_Object *obj, const char *file);
void efm_icon_thumb_set(Evas_Object *obj, const char *thumb);
void efm_icon_video_set(Evas_Object *obj, const char *video);
void efm_icon_size_get(Evas_Object *obj, int *w, int *h);
Eina_Bool efm_icon_alpha_get(Evas_Object *obj);
Eina_Bool efm_icon_mono_get(Evas_Object *obj);
// smart callbacks:
// "loaded"
#endif

20
src/efm/efm_private.h Normal file
View File

@ -0,0 +1,20 @@
// functions used between the split up parts of the efm view
static void _command (Smart_Data *sd, const char *cmd);
static void _process (Smart_Data_Thread *std, Ecore_Thread *th, Eina_List *batch);
static Eina_Bool _cb_exe_del (void *data, int ev_type EINA_UNUSED, void *event);
static Eina_Bool _cb_exe_data (void *data, int ev_type EINA_UNUSED, void *event);
static Eina_Bool _cb_exe_pending_timer (void *data);
static void _cb_thread_main (void *data, Ecore_Thread *th);
static void _cb_thread_notify (void *data, Ecore_Thread *th EINA_UNUSED, void *msg);
static void _cb_thread_done (void *data, Ecore_Thread *th EINA_UNUSED);
static void _icon_object_clear (Icon *icon);
static void _icon_object_add (Icon *icon, Smart_Data *sd, Evas *e, const char *theme_edj_file, Eina_Bool clip_set, int num);
static void _icon_free (Icon *icon);
static void _block_free (Block *block);
static void _cb_reblock (void *data);
static void _drag_start (Icon *icon);

175
src/efm/efm_structs.h Normal file
View File

@ -0,0 +1,175 @@
// data structs used for the efm view
//
typedef struct _Smart_Data Smart_Data;
typedef struct _Smart_Data_Thread Smart_Data_Thread;
typedef struct _Block Block;
typedef struct _Icon Icon;
typedef struct _File_Info File_Info;
// data kept around as long as the thread is so it knows what it is for
struct _Smart_Data_Thread
{
Smart_Data *sd;
Eina_Thread_Queue *thq;
};
// an icon view gui data
struct _Smart_Data
{
Evas_Object_Smart_Clipped_Data __clipped_data;
Eina_Rectangle geom;
Evas_Object *o_smart;
Evas_Object *o_back;
Evas_Object *o_clip;
Evas_Object *o_scroller;
Evas_Object *o_focus;
Evas_Object *o_sel;
Evas_Object *o_over;
Evas_Object *o_detail_header;
Evas_Object *o_detail_header_item[7];
Evas_Object *o_list_detailed_dummy;
Evas_Object *o_list_detail[6];
Evas_Object *o_list_detail_swallow[6];
Evas_Object *o_overlay_grid_fill;
Evas_Object *o_overlay_grid;
Evas_Object *o_overlay_info;
Eina_Stringshare *path;
Ecore_Exe *exe_open;
Ecore_Event_Handler *handler_exe_del;
Ecore_Event_Handler *handler_exe_data;
Smart_Data_Thread *thread_data;
Ecore_Thread *thread;
Eina_List *icons;
Eina_List *blocks;
Ecore_Job *reblock_job;
Ecore_Job *refocus_job;
Ecore_Job *size_bars_update_job;
Ecore_Job *size_max_update_job;
Icon *last_selected;
Icon *last_focused_before;
Icon *last_focused;
Icon *drag_icon;
Icon *over_icon;
Icon *drop_over;
Icon *rename_icon;
Evas_Coord icon_min_w, icon_min_h;
Evas_Coord list_min_w, list_min_h;
Evas_Coord list_detailed_min_w, list_detailed_min_h;
Evas_Coord back_down_x, back_down_y;
Evas_Coord back_x, back_y;
Evas_Coord sel_x1, sel_y1, sel_x2, sel_y2;
Evas_Coord dnd_scroll_x, dnd_scroll_y;
Evas_Coord detail_down_x, detail_down_y;
Evas_Coord detail_down_start_min_w;
Evas_Coord detail_min_w[6];
Evas_Coord detail_header_min_h[7];
double focus_start_time;
double focus_pos;
Ecore_Animator *focus_animator;
Ecore_Timer *scroll_timer;
Ecore_Timer *dnd_scroll_timer;
Ecore_Timer *dnd_over_open_timer;
Elm_Xdnd_Action dnd_action;
unsigned long long file_max;
Eina_Bool reblocked : 1;
Eina_Bool relayout : 1;
Eina_Bool focused : 1;
Eina_Bool focus_show : 1;
Eina_Bool key_control : 1;
Eina_Bool back_down : 1;
Eina_Bool sel_show : 1;
Eina_Bool drag : 1;
Eina_Bool just_dragged : 1;
Eina_Bool cnp_have : 1;
Eina_Bool cnp_cut : 1;
Eina_Bool detail_down : 1;
struct {
Efm_View_Mode view_mode;
Efm_Sort_Mode sort_mode;
int icon_size;
} config;
};
// file info gagthered
struct _File_Info
{
const char *file;
const char *label;
const char *label_selected;
const char *label_clicked;
const char *mime;
const char *icon;
const char *icon_selected;
const char *icon_clicked;
const char *mime_icon;
const char *pre_lookup_icon;
const char *thumb;
Eina_Bool dir : 1;
Eina_Bool link : 1;
Eina_Bool broken : 1;
Eina_Bool special : 1;
};
// a block of icons as a group
struct _Block
{
Eina_Rectangle bounds; // relative to top-left of obj
Smart_Data *sd;
Eina_List *icons;
int realized_num;
int selected_num;
Eina_Bool changed : 1;
};
// an icon in a block of icons
struct _Icon
{
Eina_Rectangle geom;
Evas_Object *o_base;
Evas_Object *o_icon;
Evas_Object *o_entry;
Evas_Object *o_list_detail_swallow[6];
Evas_Object *o_list_detail_swallow2[6];
Smart_Data *sd;
Cmd *cmd;
Block *block;
Eina_List *block_list;
Ecore_Timer *longpress_timer;
File_Info info;
Evas_Coord down_x, down_y;
Eina_Bool realized : 1;
Eina_Bool selected : 1;
Eina_Bool down : 1;
Eina_Bool over : 1;
Eina_Bool changed : 1;
Eina_Bool edje : 1;
Eina_Bool renaming : 1;
};
typedef struct _Pending_Exe_Del Pending_Exe_Del;
// an entry in our list of back-end open processes waiting to exit
struct _Pending_Exe_Del
{
Ecore_Exe *exe;
Ecore_Timer *timer;
};
// a message from thread to front-end ui in main loop
typedef struct
{
Eina_Thread_Queue_Msg head;
Cmd *c;
} Msg;

1463
src/efm/efm_util.c Normal file

File diff suppressed because it is too large Load Diff

189
src/efm/main.c Normal file
View File

@ -0,0 +1,189 @@
// test gui front-end for new efm
#include "efm.h"
//#include "efm_icon.h"
static Evas_Object *o_detail_header_box = NULL;
static Evas_Object *o_detail_header = NULL;
static void
_cb_icons(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
efm_path_view_mode_set(data, EFM_VIEW_MODE_ICONS);
if (o_detail_header)
{
evas_object_del(o_detail_header);
o_detail_header = NULL;
}
}
static void
_cb_icons_custom(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
efm_path_view_mode_set(data, EFM_VIEW_MODE_ICONS_CUSTOM);
if (o_detail_header)
{
evas_object_del(o_detail_header);
o_detail_header = NULL;
}
}
static void
_cb_list(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
efm_path_view_mode_set(data, EFM_VIEW_MODE_LIST);
if (o_detail_header)
{
evas_object_del(o_detail_header);
o_detail_header = NULL;
}
}
static void
_cb_list_detailed(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
Evas_Object *o;
efm_path_view_mode_set(data, EFM_VIEW_MODE_LIST_DETAILED);
if (!o_detail_header)
{
o_detail_header = o = efm_detail_header_get(data);
evas_object_size_hint_fill_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL);
evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, 0.0);
elm_box_pack_end(o_detail_header_box, o);
evas_object_show(o);
}
}
EAPI_MAIN int
elm_main(int argc, char **argv)
{
const char *path;
Evas_Object *o, *win, *sc, *efm, *bx, *bx2;
char buf[PATH_MAX];
if (argc > 1)
{
path = ecore_file_realpath(argv[1]);
}
else
{
getcwd(buf, sizeof(buf));
path = buf;
}
elm_need_efreet();
elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED);
elm_app_compile_bin_dir_set(PACKAGE_BIN_DIR);
elm_app_compile_lib_dir_set(PACKAGE_LIB_DIR);
elm_app_compile_data_dir_set(PACKAGE_DATA_DIR);
elm_app_info_set(elm_main, "efm", "checkme");
win = o = elm_win_util_standard_add("Main", "Files");
elm_win_icon_name_set(o, "Files");
elm_win_role_set(o, "efm_win_files");
elm_win_autodel_set(o, EINA_TRUE);
/*
ic = o = evas_object_image_add(evas_object_evas_get(win));
icon = efreet_icon_path_find(elm_config_icon_theme_get(),
"inode-directory", 0);
if (icon) printf("%s\n", icon);
evas_object_image_file_set(o, icon, NULL);
elm_win_icon_object_set(win, o);
evas_object_show(o);
*/
bx = o = elm_box_add(win);
evas_object_size_hint_fill_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL);
evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
elm_box_horizontal_set(o, EINA_FALSE);
elm_win_resize_object_add(win, o);
evas_object_show(o);
bx2 = o = elm_box_add(win);
evas_object_size_hint_fill_set(o, EVAS_HINT_FILL, 0);
evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, 0);
elm_box_homogeneous_set(o, EINA_TRUE);
elm_box_horizontal_set(o, EINA_TRUE);
elm_box_pack_end(bx, o);
evas_object_show(o);
o = elm_button_add(win);
evas_object_size_hint_fill_set(o, EVAS_HINT_FILL, 0);
evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, 0);
elm_object_text_set(o, "X");
elm_box_pack_end(bx2, o);
evas_object_show(o);
o_detail_header_box = o = elm_box_add(win);
evas_object_size_hint_fill_set(o, EVAS_HINT_FILL, 0);
evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, 0);
elm_box_pack_end(bx, o);
evas_object_show(o);
sc = o = elm_scroller_add(win);
evas_object_size_hint_fill_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL);
evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
elm_box_pack_end(bx, o);
evas_object_show(o);
efm = o = efm_add(win);
evas_object_size_hint_fill_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL);
evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
elm_object_content_set(sc, o);
efm_scroller_set(o, sc);
efm_path_set(o, path);
evas_object_show(o);
bx2 = o = elm_box_add(win);
evas_object_size_hint_fill_set(o, EVAS_HINT_FILL, 0);
evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, 0);
elm_box_homogeneous_set(o, EINA_TRUE);
elm_box_horizontal_set(o, EINA_TRUE);
elm_box_pack_end(bx, o);
evas_object_show(o);
o = elm_button_add(win);
evas_object_size_hint_fill_set(o, EVAS_HINT_FILL, 0);
evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, 0);
elm_object_text_set(o, "Icons");
evas_object_smart_callback_add(o, "clicked", _cb_icons, efm);
elm_box_pack_end(bx2, o);
evas_object_show(o);
o = elm_button_add(win);
evas_object_size_hint_fill_set(o, EVAS_HINT_FILL, 0);
evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, 0);
elm_object_text_set(o, "List");
evas_object_smart_callback_add(o, "clicked", _cb_list, efm);
elm_box_pack_end(bx2, o);
evas_object_show(o);
o = elm_button_add(win);
evas_object_size_hint_fill_set(o, EVAS_HINT_FILL, 0);
evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, 0);
elm_object_text_set(o, "Detailed");
evas_object_smart_callback_add(o, "clicked", _cb_list_detailed, efm);
elm_box_pack_end(bx2, o);
evas_object_show(o);
o = elm_button_add(win);
evas_object_size_hint_fill_set(o, EVAS_HINT_FILL, 0);
evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, 0);
elm_object_text_set(o, "Custom");
evas_object_smart_callback_add(o, "clicked", _cb_icons_custom, efm);
elm_box_pack_end(bx2, o);
evas_object_show(o);
evas_object_resize(win, ELM_SCALE_SIZE(700), ELM_SCALE_SIZE(300));
evas_object_show(win);
elm_object_focus_set(sc, EINA_TRUE);
elm_run();
return 0;
}
ELM_MAIN()

18
src/efm/meson.build Normal file
View File

@ -0,0 +1,18 @@
dir = join_paths(dir_bin)
inc = include_directories(
'.',
'../..',
'../shared/commands',
'../shared/common'
)
executable('efm', [
'../shared/common/cmd.c',
'efm.c',
'sort.c',
'efm_icon.c',
'main.c'
],
include_directories: inc,
dependencies: deps,
install: true,
install_dir: dir)

240
src/efm/sort.c Normal file
View File

@ -0,0 +1,240 @@
#include "cmd.h"
#include "sort.h"
#include "efm.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
// sorting helpers
enum
{
SORT_LESS = -1,
SORT_MORE = 1,
SORT_INVALID = -100
};
static char
_str_mode_file_type(int mode)
{
char c = '?';
if (S_ISREG(mode)) c = '-';
else if (S_ISDIR(mode)) c = 'd';
else if (S_ISBLK(mode)) c = 'b';
else if (S_ISCHR(mode)) c = 'c';
#ifdef S_ISFIFO
else if (S_ISFIFO(mode)) c = 'p';
#endif
#ifdef S_ISLNK
else if (S_ISLNK(mode)) c = 'l';
#endif
#ifdef S_ISSOCK
else if (S_ISSOCK(mode)) c = 's';
#endif
#ifdef S_ISDOOR
else if (S_ISDOOR(mode)) c = 'D';
#endif
return c;
}
static void
_str_mode(int mode, char str[11])
{ // generate string mode i9nto dest str buffer
static const char *rwx[] = {
"---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx"
};
str[0] = _str_mode_file_type(mode);
strcpy(&str[1], rwx[(mode >> 6)& 7]);
strcpy(&str[4], rwx[(mode >> 3)& 7]);
strcpy(&str[7], rwx[(mode & 7)]);
if (mode & S_ISUID) str[3] = (mode & S_IXUSR) ? 's' : 'S';
if (mode & S_ISGID) str[6] = (mode & S_IXGRP) ? 's' : 'l';
if (mode & S_ISVTX) str[9] = (mode & S_IXOTH) ? 't' : 'T';
str[10] = 0;
}
static int
_sort_str_field(const Cmd *c1, const Cmd *c2, int (*cmpfn) (const char *s1, const char *s2), const char *field)
{ // a string fields - get it and sort using compare func
const char *field1, *field2, *path1, *path2;
int res;
field1 = cmd_key_find(c1, field);
if (!field1) return 0;
field2 = cmd_key_find(c2, field);
if (!field2) return 0;
res = cmpfn(field1, field2);
if (!res)
{ // they are the same string! use pure filename path as it's unique
path1 = cmd_key_find(c1, "path");
path2 = cmd_key_find(c2, "path");
res = cmpfn(path1, path2);
if (!res) return strcmp(path1, path2);
return res;
}
return cmpfn(field1, field2);
}
static int
_sort_num_field(const Cmd *c1, const Cmd *c2, const char *field)
{ // user a named field that is a number like mtime, ctime, size etc ...
const char *f, *path1, *path2;
unsigned long long field1 = 0, field2 = 0;
// get field and convert to ulonglong
f = cmd_key_find(c1, field);
if (f) field1 = strtoull(f, NULL, 10);
// get field and convert to ulonglong
f = cmd_key_find(c2, field);
if (f) field2 = strtoull(f, NULL, 10);
if (field1 == field2)
{ // they are the same string! use pure filename path as it's unique
path1 = cmd_key_find(c1, "path");
path2 = cmd_key_find(c2, "path");
return strcmp(path1, path2);
}
// sort result
if (field1 > field2) return SORT_MORE;
return SORT_LESS;
}
static int
_sort_path(const Cmd *c1, const Cmd *c2, int (*cmpfn) (const char *s1, const char *s2))
{ // use pure filename path not label
return _sort_str_field(c1, c2, cmpfn, "path");
}
static int
_sort_label(const Cmd *c1, const Cmd *c2, int (*cmpfn) (const char *s1, const char *s2))
{ // sort by label or path - some files like .desktop files have labels
const char *path1, *path2;
int res;
// get a label instead of filename path element if available
path1 = cmd_key_find(c1, "label");
if (!path1) path1 = cmd_key_find(c1, "path");
// get a label instead of filename path element if available
path2 = cmd_key_find(c2, "label");
if (!path2) path2 = cmd_key_find(c2, "path");
res = cmpfn(path1, path2);
if (!res)
{ // they are the same string! use pure filename path as it's unique
path1 = cmd_key_find(c1, "path");
path2 = cmd_key_find(c2, "path");
res = cmpfn(path1, path2);
if (!res) return strcmp(path1, path2);
return res;
}
return res;
}
static int
_sort_mode(const Cmd *c1, const Cmd *c2)
{ // sort by mode string like "drwxr-xr-x" or "-rw-r--r--"
const char *f;
int mode1 = 0, mode2 = 0, ret = 0;
char m1[11], m2[11];
// get mdoe and generate mode string
f = cmd_key_find(c1, "mode");
if (f) mode1 = strtoull(f, NULL, 16);
_str_mode(mode1, m1);
// get mdoe and generate mode string
f = cmd_key_find(c2, "mode");
if (f) mode2 = strtoull(f, NULL, 16);
_str_mode(mode2, m2);
// sort by the mode string
ret = strcmp(m1, m2);
return ret;
}
static int
_sort_dir_not_dir(const Cmd *c1, const Cmd *c2)
{
const char *type1, *type2;
// get file types for both - and if link resolve the link dest type
type1 = cmd_key_find(c1, "type");
if ((type1) && (!strcmp(type1, "link")))
type1 = cmd_key_find(c1, "link-type");
if (!type1) type1 = "file";
type2 = cmd_key_find(c2, "type");
if ((type2) && (!strcmp(type2, "link")))
type2 = cmd_key_find(c2, "link-type");
if (!type2) type2 = "file";
// 1st is dir, 2nd is not dir
if (( !strcmp(type1, "dir")) && (!!strcmp(type2, "dir"))) return SORT_LESS;
// 1st is not dir, 2nd is dir
if ((!!strcmp(type1, "dir")) && ( !strcmp(type2, "dir"))) return SORT_MORE;
// both are dir or both are not dir
return SORT_INVALID;
}
// sorter logic
static int
_sort_cmd_do(const Cmd *c1, const Cmd *c2)
{
int dir_not_dir = SORT_INVALID;
Efm_Sort_Mode sort_mode;
int (*cmpfn) (const char *s1, const char *s2) = strcmp;
Eina_Bool label_sort = EINA_FALSE;
sort_mode = c1->sort_mode;
// handle flags
if (sort_mode & EFM_SORT_MODE_DIRS_FIRST)
dir_not_dir = _sort_dir_not_dir(c1, c2);
if (sort_mode & EFM_SORT_MODE_NOCASE)
cmpfn = strcasecmp;
if (sort_mode & EFM_SORT_MODE_LABEL_NOT_PATH)
label_sort = EINA_TRUE;
// handle each sort mode
// if one is a dir and one is not - other sorting compares are not useful
// so just return this status
if (dir_not_dir != SORT_INVALID) return dir_not_dir;
// handle the actual specific sort field to use
switch (sort_mode & EFM_SORT_MODE_MASK)
{
case EFM_SORT_MODE_NAME:
if (label_sort) return _sort_label(c1, c2, cmpfn);
return _sort_path(c1, c2, cmpfn);
case EFM_SORT_MODE_SIZE:
return _sort_num_field(c1, c2, "size");
case EFM_SORT_MODE_DATE:
return _sort_num_field(c1, c2, "mtime");
case EFM_SORT_MODE_MIME:
return _sort_str_field(c1, c2, strcmp, "mime");
case EFM_SORT_MODE_USER:
return _sort_str_field(c1, c2, strcmp, "user");
case EFM_SORT_MODE_GROUP:
return _sort_str_field(c1, c2, strcmp, "group");
case EFM_SORT_MODE_PERMISSIONS:
return _sort_mode(c1, c2);
default: // unknown - so just plain path
goto def;
break;
}
def:
return _sort_path(c1, c2, cmpfn);
}
int
sort_cmd(const void *c1, const void *c2)
{
return _sort_cmd_do(c1, c2);
}

6
src/efm/sort.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef SORT_H
# define SORT_H 1
int sort_cmd(const void *c1, const void *c2);
#endif

2
src/meson.build Normal file
View File

@ -0,0 +1,2 @@
subdir('backends')
subdir('efm')

View File

226
src/shared/common/cmd.c Normal file
View File

@ -0,0 +1,226 @@
// basic command parsing, building and sending funcs used by other files to
// deal with a string command stream sanely so all the parsing of the core
// formatting is on one place
#include "cmd.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
// parse a single command line that is:
// cmd xxx=yyy a=bbb ...
// and return the command struct
Cmd *
cmd_parse(const char *cmd)
{
char *b;
const char *s, *begin;
int d, dmax;
Cmd *cnew, *c = malloc(sizeof(Cmd) + (2 * sizeof(char *)));
if (strncmp(cmd, "CMD ", 4)) return NULL;
cmd += 4;
if (!c) return NULL;
c->buf_size = strlen(cmd) + 1;
c->buf = malloc(c->buf_size); // buf that unescapes always same/smaller
c->command = NULL;
c->dict[0] = c->dict[1] = NULL;
// parse out command string until spaces - then skip them, then begin dict
b = c->buf;
for (s = cmd; (!isspace(*s)) && (*s); s++)
{
*b = *s;
b++;
}
*b = 0;
b++;
c->command = c->buf;
for (; (*s) && isspace(*s); s++);
d = 0;
dmax = 2;
while (*s)
{
begin = b;
// parse up to =
while ((*s) && (*s != '=') && !isspace(*s)) *b++ = *s++;
if (*s == '=')
{
s++; // skip =
*b++ = 0;
c->dict[d++] = begin;
begin = b;
// parse up to space - handle escapes
while ((*s) && (!isspace(*s)))
{
if (*s == '\\')
{
s++;
if (*s == 'x')
{
s++;
if (s[0] && s[1])
{
int hex1 = 0, hex2 = 0;
if ((s[0] >= '0') && (s[0] <= '9')) hex1 = s[0] - '0';
if ((s[0] >= 'a') && (s[0] <= 'f')) hex1 = 10 + s[0] - 'a';
if ((s[0] >= 'A') && (s[0] <= 'F')) hex1 = 10 + s[0] - 'A';
if ((s[1] >= '0') && (s[1] <= '9')) hex2 = s[1] - '0';
if ((s[1] >= 'a') && (s[1] <= 'f')) hex2 = 10 + s[1] - 'a';
if ((s[1] >= 'A') && (s[1] <= 'F')) hex2 = 10 + s[1] - 'A';
*b++ = (hex1 << 4) | hex2;
s += 2;
}
}
else if (*s == ' ') *b++ = ' ';
else if (*s == 'a') *b++ = '\a';
else if (*s == 'b') *b++ = '\b';
else if (*s == 't') *b++ = '\t';
else if (*s == 'n') *b++ = '\n';
else if (*s == 'v') *b++ = '\v';
else if (*s == 'f') *b++ = '\f';
else if (*s == 'r') *b++ = '\r';
else if (*s == '`') *b++ = '`';
else if (((*s >= '>') && (*s <= '*')) ||
((*s >= ';') && (*s <= '?')) ||
((*s >= '[') && (*s <= ']')) ||
((*s >= '{') && (*s <= '~')))
*b++ = *s++;
}
else *b++ = *s++;
}
*b++ = 0;
c->dict[d++] = begin;
dmax += 2;
cnew = realloc(c, sizeof(Cmd) + (sizeof(char *) * (dmax + 2)));
if (!cnew)
{
free(c->buf);
free(c);
return NULL;
}
c = cnew;
c->dict[d] = NULL;
c->dict[d + 1] = NULL;
}
// skip spaces
for (; (*s) && isspace(*s); s++);
}
return c;
}
void
cmd_free(Cmd *c)
{
if (!c) return;
free(c->buf);
free(c);
}
Cmd *
cmd_dup(const Cmd *c)
{
Cmd *c2;
int i, dict_num = 0;
for (i = 0; c->dict[i]; i++); // count dict keys
dict_num = i + 2;
c2 = calloc(1, sizeof(Cmd) + (sizeof(char *) * (dict_num + 2)));
if (!c2) return NULL;
c2->buf_size = c->buf_size;
if (c2->buf_size != 0)
{
c2->buf = malloc(c->buf_size);
if (!c2->buf) goto err;
memcpy(c2->buf, c->buf, c->buf_size);
}
c2->command = c2->buf;
for (i = 0; c->dict[i]; i++)
c2->dict[i] = c2->buf + (c->dict[i] - c->buf);
return c2;
err:
cmd_free(c2);
return NULL;
}
void
cmd_dump_sterr(Cmd *c)
{
int i = 0;
fprintf(stderr, "CMD: [%s]\n", c->command);
while (c->dict[i])
{
fprintf(stderr, " %s=%s\n", c->dict[i], c->dict[i + 1]);
i += 2;
}
}
Eina_Strbuf *
cmd_strbuf_new(const char *command)
{
Eina_Strbuf *strbuf;
strbuf = eina_strbuf_new();
eina_strbuf_append(strbuf, "CMD ");
eina_strbuf_append(strbuf, command);
return strbuf;
}
void
cmd_strbuf_append(Eina_Strbuf *strbuf, const char *key, const char *val)
{
const char *s;
eina_strbuf_append_char(strbuf, ' ');
eina_strbuf_append(strbuf, key);
eina_strbuf_append_char(strbuf, '=');
for (s = val; *s; s++)
{
if ((*s <= '*') ||
((*s >= ';') && (*s <= '?')) ||
((*s >= '[') && (*s <= ']')) ||
(*s == '`') ||
(*s >= '{'))
{
unsigned char tmp;
tmp = s[0];
eina_strbuf_append_printf(strbuf, "\\x%02x", tmp);
}
else
eina_strbuf_append_char(strbuf, *s);
}
}
void
cmd_strbuf_print_consume(Eina_Strbuf *strbuf)
{
eina_strbuf_append_char(strbuf, '\n');
write(1,
eina_strbuf_string_get(strbuf),
eina_strbuf_length_get(strbuf));
eina_strbuf_free(strbuf);
}
void
cmd_strbuf_exe_consume(Eina_Strbuf *strbuf, Ecore_Exe *exe)
{
eina_strbuf_append_char(strbuf, '\n');
ecore_exe_send(exe,
eina_strbuf_string_get(strbuf),
eina_strbuf_length_get(strbuf));
eina_strbuf_free(strbuf);
}
const char *
cmd_key_find(const Cmd *c, const char *key)
{
int i;
for (i = 0; c->dict[i]; i += 2)
{
if (!strcmp(key, c->dict[i])) return c->dict[i + 1];
}
return NULL;
}

37
src/shared/common/cmd.h Normal file
View File

@ -0,0 +1,37 @@
#ifndef CMD_H
# define CMD_H 1
# include <Eina.h>
# include <Ecore.h>
# include "efm.h"
// commands:
// file-add // a dile in a dir being monitored has been added
// file-del // a file in a dir being monitored has been deleted
// file-mod // a file in a dir being monitored has been modified
// dir-del // the dir being monitored/listed has been deleted
//
// future:
// dir-usage // how much data, number of files, dirs etc.
typedef struct _Cmd
{
size_t buf_size;
char *buf;
const char *command;
Efm_Sort_Mode sort_mode;
const char *dict[];
} Cmd;
Cmd *cmd_parse(const char *cmd);
void cmd_free(Cmd *c);
Cmd *cmd_dup(const Cmd *c);
void cmd_dump_sterr(Cmd *c);
Eina_Strbuf *cmd_strbuf_new(const char *command);
void cmd_strbuf_append(Eina_Strbuf *strbuf, const char *key, const char *val);
void cmd_strbuf_print_consume(Eina_Strbuf *strbuf);
void cmd_strbuf_exe_consume(Eina_Strbuf *strbuf, Ecore_Exe *exe);
const char *cmd_key_find(const Cmd *c, const char *key);
#endif

50
src/shared/common/sha.c Normal file
View File

@ -0,0 +1,50 @@
#include "sha.h"
#include <Eina.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
void
sha1_stat(const struct stat *st, unsigned char dst[20])
{
char buf[128];
#ifdef STAT_NSEC
# if (defined __USE_MISC && defined st_mtime)
# define STAT_NSEC_MTIME(st) (unsigned long long)((st)->st_mtim.tv_nsec)
# define STAT_NSEC_CTIME(st) (unsigned long long)((st)->st_ctim.tv_nsec)
# else
# define STAT_NSEC_MTIME(st) (unsigned long long)((st)->st_mtimensec)
# define STAT_NSEC_CTIME(st) (unsigned long long)((st)->st_ctimensec)
# endif
#else
# define STAT_NSEC_MTIME(st) (unsigned long long)(0)
# define STAT_NSEC_CTIME(st) (unsigned long long)(0)
#endif
snprintf(buf, sizeof(buf),
"%llu %llu %llu %llu %llu %llu %llu %llu",
(unsigned long long)(st->st_mode),
(unsigned long long)(st->st_uid),
(unsigned long long)(st->st_gid),
(unsigned long long)(st->st_size),
(unsigned long long)(st->st_mtime),
(unsigned long long)(st->st_ctime),
STAT_NSEC_MTIME(st),
STAT_NSEC_CTIME(st));
eina_sha1((unsigned char *)buf, strlen(buf), dst);
}
void
sha1_str(unsigned char sha[20], char shastr[41])
{
const char *chmap = "0123456789abcdef";
int i;
for (i = 0; i < 20; i++)
{
shastr[(i * 2) ] = chmap[(sha[i] >> 4) & 0xf];
shastr[(i * 2) + 1] = chmap[ sha[i] & 0xf];
}
shastr[i * 2] = 0;
}

11
src/shared/common/sha.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef SHA_H
# define SHA_H 1
// get nsec in stat
#define STAT_NSEC 1
#include <sys/stat.h>
void sha1_stat(const struct stat *st, unsigned char dst[20]);
void sha1_str(unsigned char sha[20], char shastr[41]);
#endif

View File

@ -0,0 +1,73 @@
// XXX: should make this config
static inline Eina_Bool
check_thumb_image(const char *path EINA_UNUSED, const char *mime)
{
if (eina_fnmatch("image/*", mime, EINA_FNMATCH_CASEFOLD) ||
eina_fnmatch("application/x-docbook+xml", mime, EINA_FNMATCH_CASEFOLD))
return EINA_TRUE;
return EINA_FALSE;
}
static inline Eina_Bool
check_thumb_font(const char *path EINA_UNUSED, const char *mime)
{
if (eina_fnmatch("font/*", mime, EINA_FNMATCH_CASEFOLD))
return EINA_TRUE;
return EINA_FALSE;
}
static inline Eina_Bool
check_thumb_paged(const char *path EINA_UNUSED, const char *mime)
{
if (eina_fnmatch("application/*pdf", mime, EINA_FNMATCH_CASEFOLD) ||
eina_fnmatch("application/acrobat", mime, EINA_FNMATCH_CASEFOLD) ||
eina_fnmatch("application/postscript", mime, EINA_FNMATCH_CASEFOLD) ||
eina_fnmatch("application/vnd.ms-powerpoint", mime, EINA_FNMATCH_CASEFOLD) ||
eina_fnmatch("application/msword", mime, EINA_FNMATCH_CASEFOLD) ||
eina_fnmatch("application/vnd.ms-word", mime, EINA_FNMATCH_CASEFOLD) ||
eina_fnmatch("application/vnd.openxmlformats-officedocument.wordprocessingml.document", mime, EINA_FNMATCH_CASEFOLD) ||
eina_fnmatch("application/vnd.oasis.opendocument.text*", mime, EINA_FNMATCH_CASEFOLD))
return EINA_TRUE;
return EINA_FALSE;
}
static inline Eina_Bool
check_thumb_music(const char *path EINA_UNUSED, const char *mime)
{
if (eina_fnmatch("audio/mpeg", mime, EINA_FNMATCH_CASEFOLD) ||
eina_fnmatch("audio/ogg", mime, EINA_FNMATCH_CASEFOLD) ||
eina_fnmatch("audio/aac", mime, EINA_FNMATCH_CASEFOLD) ||
eina_fnmatch("audio/flac", mime, EINA_FNMATCH_CASEFOLD))
return EINA_TRUE;
return EINA_FALSE;
}
static inline Eina_Bool
check_thumb_video(const char *path EINA_UNUSED, const char *mime)
{
if (eina_fnmatch("video/*", mime, EINA_FNMATCH_CASEFOLD))
return EINA_TRUE;
return EINA_FALSE;
}
static inline Eina_Bool
check_thumb_edje(const char *path EINA_UNUSED, const char *mime)
{
if (eina_fnmatch("application/x-edje", mime, EINA_FNMATCH_CASEFOLD))
return EINA_TRUE;
return EINA_FALSE;
}
static inline Eina_Bool
check_thumb_any(const char *path, const char *mime)
{
if (check_thumb_image(path, mime) ||
check_thumb_font (path, mime) ||
check_thumb_paged(path, mime) ||
check_thumb_music(path, mime) ||
check_thumb_video(path, mime) ||
check_thumb_edje (path, mime))
return EINA_TRUE;
return EINA_FALSE;
}