From 511d05a8a7ad2bb1d06ce19d4dceae59f2b75e71 Mon Sep 17 00:00:00 2001 From: itdominator <1itdominator@gmail.com> Date: Sat, 24 Apr 2021 06:39:09 -0500 Subject: [PATCH] added initial gtk icon logiv --- src/shellfm/windows/Window.py | 18 +- src/shellfm/windows/WindowController.py | 3 +- src/shellfm/windows/view/Icon.py | 178 ++++++++++++++++++ src/shellfm/windows/view/View.py | 25 ++- src/shellfm/windows/view/__init__.py | 1 + src/shellfm/windows/view/utils/Launcher.py | 40 +++- src/shellfm/windows/view/utils/Settings.py | 13 +- .../windows/view/utils/icons/archive.png | Bin 0 -> 1670 bytes .../windows/view/utils/icons/audio.png | Bin 0 -> 1544 bytes src/shellfm/windows/view/utils/icons/bin.png | Bin 0 -> 858 bytes src/shellfm/windows/view/utils/icons/dir.png | Bin 0 -> 850 bytes src/shellfm/windows/view/utils/icons/doc.png | Bin 0 -> 702 bytes src/shellfm/windows/view/utils/icons/pdf.png | Bin 0 -> 925 bytes .../windows/view/utils/icons/presentation.png | Bin 0 -> 882 bytes .../windows/view/utils/icons/spreadsheet.png | Bin 0 -> 707 bytes src/shellfm/windows/view/utils/icons/text.png | Bin 0 -> 798 bytes .../windows/view/utils/icons/trash.png | Bin 0 -> 989 bytes .../windows/view/utils/icons/video.png | Bin 0 -> 1313 bytes src/shellfm/windows/view/utils/icons/web.png | Bin 0 -> 1845 bytes 19 files changed, 265 insertions(+), 13 deletions(-) create mode 100644 src/shellfm/windows/view/Icon.py create mode 100644 src/shellfm/windows/view/utils/icons/archive.png create mode 100644 src/shellfm/windows/view/utils/icons/audio.png create mode 100644 src/shellfm/windows/view/utils/icons/bin.png create mode 100644 src/shellfm/windows/view/utils/icons/dir.png create mode 100644 src/shellfm/windows/view/utils/icons/doc.png create mode 100644 src/shellfm/windows/view/utils/icons/pdf.png create mode 100644 src/shellfm/windows/view/utils/icons/presentation.png create mode 100644 src/shellfm/windows/view/utils/icons/spreadsheet.png create mode 100644 src/shellfm/windows/view/utils/icons/text.png create mode 100644 src/shellfm/windows/view/utils/icons/trash.png create mode 100644 src/shellfm/windows/view/utils/icons/video.png create mode 100644 src/shellfm/windows/view/utils/icons/web.png diff --git a/src/shellfm/windows/Window.py b/src/shellfm/windows/Window.py index 5284e08..5445881 100644 --- a/src/shellfm/windows/Window.py +++ b/src/shellfm/windows/Window.py @@ -11,12 +11,24 @@ class Window: def create_view(self): view = View() self.views.append(view) + return view def pop_view(self): self.views.pop() - def delete_view(self, index): - del self.views[index] + def delete_view(self, vid): + i = -1 + for view in self.views: + i += 1 + if view.id == vid: + del self.views[i] + break - def get_view(self, index): + + def get_view_by_id(self, vid): + for view in self.views: + if view.id == vid: + return view + + def get_view_by_index(self, index): return self.views[index] diff --git a/src/shellfm/windows/WindowController.py b/src/shellfm/windows/WindowController.py index 587a271..b3b2ee5 100644 --- a/src/shellfm/windows/WindowController.py +++ b/src/shellfm/windows/WindowController.py @@ -24,8 +24,7 @@ class WindowController: def add_view_for_window(self, win_id): for window in self.windows: if window.id == win_id: - window.create_view() - break + return window.create_view() def pop_window(self): self.windows.pop() diff --git a/src/shellfm/windows/view/Icon.py b/src/shellfm/windows/view/Icon.py new file mode 100644 index 0000000..c82aba9 --- /dev/null +++ b/src/shellfm/windows/view/Icon.py @@ -0,0 +1,178 @@ +# Python Imports +import os, subprocess, hashlib, threading +from os.path import isdir, isfile, join + + +# Gtk imports +import gi +gi.require_version('Gtk', '3.0') +gi.require_version('Gdk', '3.0') + +from gi.repository import Gtk +from gi.repository import Gio +from xdg.DesktopEntry import DesktopEntry + + +# Application imports + + + + +def threaded(fn): + def wrapper(*args, **kwargs): + threading.Thread(target=fn, args=args, kwargs=kwargs).start() + return wrapper + +class Icon: + def __init__(self): + self.SCRIPT_PTH = os.path.dirname(os.path.realpath(__file__)) + "/" + self.INTERNAL_ICON_PTH = self.SCRIPT_PTH + "./utils/icons/text.png" + + + def createIcon(self, dir, file): + fullPath = dir + "/" + file + return self.getIconImage(file, fullPath) + + def createThumbnail(self, dir, file): + fullPath = dir + "/" + file + try: + fileHash = hashlib.sha256(str.encode(fullPath)).hexdigest() + hashImgPth = self.get_home() + "/.thumbnails/normal/" + fileHash + ".png" + + thumbnl = self.createScaledImage(hashImgPth, self.viIconWH) + if thumbnl == None: # If no icon whatsoever, return internal default + thumbnl = Gtk.Image.new_from_file(self.SCRIPT_PTH + "./utils/icons/video.png") + + return thumbnl + except Exception as e: + print("Thumbnail generation issue:") + print( repr(e) ) + return Gtk.Image.new_from_file(self.SCRIPT_PTH + "./utils/icons/video.png") + + + def getIconImage(self, file, fullPath): + try: + thumbnl = None + + # Video icon + if file.lower().endswith(self.fvideos): + thumbnl = Gtk.Image.new_from_file(self.SCRIPT_PTH + "./utils/icons/video.png") + # Image Icon + elif file.lower().endswith(self.fimages): + thumbnl = self.createScaledImage(fullPath, self.viIconWH) + # .desktop file parsing + elif fullPath.lower().endswith( ('.desktop',) ): + thumbnl = self.parseDesktopFiles(fullPath) + # System icons + else: + thumbnl = self.getSystemThumbnail(fullPath, self.systemIconImageWH[0]) + + if thumbnl == None: # If no icon whatsoever, return internal default + thumbnl = Gtk.Image.new_from_file(self.INTERNAL_ICON_PTH) + + return thumbnl + except Exception as e: + print("Icon generation issue:") + print( repr(e) ) + return Gtk.Image.new_from_file(self.INTERNAL_ICON_PTH) + + def parseDesktopFiles(self, fullPath): + try: + xdgObj = DesktopEntry(fullPath) + icon = xdgObj.getIcon() + altIconPath = "" + + if "steam" in icon: + steamIconsDir = self.get_home() + "/.thumbnails/steam_icons/" + name = xdgObj.getName() + fileHash = hashlib.sha256(str.encode(name)).hexdigest() + + if isdir(steamIconsDir) == False: + os.mkdir(steamIconsDir) + + hashImgPth = steamIconsDir + fileHash + ".jpg" + if isfile(hashImgPth) == True: + # Use video sizes since headers are bigger + return self.createScaledImage(hashImgPth, self.viIconWH) + + execStr = xdgObj.getExec() + parts = execStr.split("steam://rungameid/") + id = parts[len(parts) - 1] + imageLink = "https://steamcdn-a.akamaihd.net/steam/apps/" + id + "/header.jpg" + proc = subprocess.Popen(["wget", "-O", hashImgPth, imageLink]) + proc.wait() + + # Use video thumbnail sizes since headers are bigger + return self.createScaledImage(hashImgPth, self.viIconWH) + elif os.path.exists(icon): + return self.createScaledImage(icon, self.systemIconImageWH) + else: + iconsDirs = ["/usr/share/pixmaps", "/usr/share/icons", self.get_home() + "/.icons" ,] + altIconPath = "" + + for iconsDir in iconsDirs: + altIconPath = self.traverseIconsFolder(iconsDir, icon) + if altIconPath is not "": + break + + return self.createScaledImage(altIconPath, self.systemIconImageWH) + except Exception as e: + print(".desktop icon generation issue:") + print( repr(e) ) + return None + + + def traverseIconsFolder(self, path, icon): + altIconPath = "" + + for (dirpath, dirnames, filenames) in os.walk(path): + for file in filenames: + appNM = "application-x-" + icon + if icon in file or appNM in file: + altIconPath = dirpath + "/" + file + break + + return altIconPath + + + def getSystemThumbnail(self, filename, size): + try: + if os.path.exists(filename): + gioFile = Gio.File.new_for_path(filename) + info = gioFile.query_info('standard::icon' , 0, Gio.Cancellable()) + icon = info.get_icon().get_names()[0] + iconTheme = Gtk.IconTheme.get_default() + iconData = iconTheme.lookup_icon(icon , size , 0) + if iconData: + iconPath = iconData.get_filename() + return Gtk.Image.new_from_file(iconPath) # This seems to cause a lot of core dump issues... + else: + return None + else: + return None + except Exception as e: + print("system icon generation issue:") + print( repr(e) ) + return None + + + def createScaledImage(self, path, wxh): + try: + pixbuf = Gtk.Image.new_from_file(path).get_pixbuf() + scaledPixBuf = pixbuf.scale_simple(wxh[0], wxh[1], 2) # 2 = BILINEAR and is best by default + return Gtk.Image.new_from_pixbuf(scaledPixBuf) + except Exception as e: + print("Image Scaling Issue:") + print( repr(e) ) + return None + + def createFromFile(self, path): + try: + return Gtk.Image.new_from_file(path) + except Exception as e: + print("Image from file Issue:") + print( repr(e) ) + return None + + def returnGenericIcon(self): + return Gtk.Image.new_from_file(self.INTERNAL_ICON_PTH) diff --git a/src/shellfm/windows/view/View.py b/src/shellfm/windows/view/View.py index 72ffd61..913b7e0 100644 --- a/src/shellfm/windows/view/View.py +++ b/src/shellfm/windows/view/View.py @@ -4,24 +4,38 @@ import os from os import listdir from os.path import isdir, isfile, join +from random import randint + # Lib imports # Application imports from .utils import Settings, Launcher -from . import Path +from . import Path, Icon -class View(Settings, Launcher, Path): +class View(Settings, Launcher, Icon, Path): def __init__(self): + self.id = "" self.files = [] self.dirs = [] self.vids = [] self.images = [] self.desktop = [] self.ungrouped = [] + self.id_length = 10 self.set_to_home() + self.generate_id() + + + def random_with_N_digits(self, n): + range_start = 10**(n-1) + range_end = (10**n)-1 + return randint(range_start, range_end) + + def generate_id(self): + self.id = str(self.random_with_N_digits(self.id_length)) def load_directory(self): path = self.get_path() @@ -128,6 +142,11 @@ class View(Settings, Launcher, Path): home = self.get_home() + "/" return path.replace(home, "") + def get_end_of_path(self): + parts = self.get_current_directory().split("/") + size = len(parts) + return parts[size - 1] + def get_dot_dots(self): return self.hashSet(['.', '..']) @@ -145,7 +164,7 @@ class View(Settings, Launcher, Path): if not os.path.exists(hashImgPth) : fullPath = join(current_directory, video[0]) self.logger.debug(f"Hash Path: {hashImgPth}\nFile Path: {fullPath}") - self.generateVideoThumbnail(fullPath, hashImgPth) + self.generate_video_thumbnail(fullPath, hashImgPth) return videos_set diff --git a/src/shellfm/windows/view/__init__.py b/src/shellfm/windows/view/__init__.py index da63bd2..d4dc901 100644 --- a/src/shellfm/windows/view/__init__.py +++ b/src/shellfm/windows/view/__init__.py @@ -1,4 +1,5 @@ from .utils import * +from .Icon import Icon from .Path import Path from .View import View diff --git a/src/shellfm/windows/view/utils/Launcher.py b/src/shellfm/windows/view/utils/Launcher.py index 6e3dcac..f4403a6 100644 --- a/src/shellfm/windows/view/utils/Launcher.py +++ b/src/shellfm/windows/view/utils/Launcher.py @@ -67,12 +67,50 @@ class Launcher: return True - def generateVideoThumbnail(self, fullPath, hashImgPth): + def generate_video_thumbnail(self, fullPath, hashImgPth): try: proc = subprocess.Popen([self.FFMPG_THUMBNLR, "-t", "65%", "-s", "300", "-c", "jpg", "-i", fullPath, "-o", hashImgPth]) proc.wait() except Exception as e: self.logger.debug(repr(e)) + self.ffprobe_generate_video_thumbnail(fullPath, hashImgPth) + + + def generate_video_thumbnail(self, fullPath, hashImgPth): + proc = None + try: + # Stream duration + command = ["ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries", "stream=duration", "-of", "default=noprint_wrappers=1:nokey=1", fullPath] + data = subprocess.run(command, stdout=subprocess.PIPE) + duration = data.stdout.decode('utf-8') + + # Format (container) duration + if "N/A" in duration: + command = ["ffprobe", "-v", "error", "-show_entries", "format=duration", "-of", "default=noprint_wrappers=1:nokey=1", fullPath] + data = subprocess.run(command , stdout=subprocess.PIPE) + duration = data.stdout.decode('utf-8') + + # Stream duration type: image2 + if "N/A" in duration: + command = ["ffprobe", "-v", "error", "-select_streams", "v:0", "-f", "image2", "-show_entries", "stream=duration", "-of", "default=noprint_wrappers=1:nokey=1", fullPath] + data = subprocess.run(command, stdout=subprocess.PIPE) + duration = data.stdout.decode('utf-8') + + # Format (container) duration type: image2 + if "N/A" in duration: + command = ["ffprobe", "-v", "error", "-f", "image2", "-show_entries", "format=duration", "-of", "default=noprint_wrappers=1:nokey=1", fullPath] + data = subprocess.run(command , stdout=subprocess.PIPE) + duration = data.stdout.decode('utf-8') + + # Get frame roughly 35% through video + grabTime = str( int( float( duration.split(".")[0] ) * 0.35) ) + command = ["ffmpeg", "-ss", grabTime, "-an", "-i", fullPath, "-s", "320x180", "-vframes", "1", hashImgPth] + proc = subprocess.Popen(command, stdout=subprocess.PIPE) + proc.wait() + except Exception as e: + print("Video thumbnail generation issue in thread:") + print( repr(e) ) + self.logger.debug(repr(e)) def check_remux_space(self): diff --git a/src/shellfm/windows/view/utils/Settings.py b/src/shellfm/windows/view/utils/Settings.py index 41a4663..f2dfc94 100644 --- a/src/shellfm/windows/view/utils/Settings.py +++ b/src/shellfm/windows/view/utils/Settings.py @@ -11,15 +11,20 @@ from os import path class Settings: logger = None + GTK_ORIENTATION = 1 # HORIZONTAL (0) VERTICAL (1) ABS_THUMBS_PTH = None # Used for thumbnail generation and is set by passing in REMUX_FOLDER = None # Used for Remuxed files and is set by passing in FFMPG_THUMBNLR = None # Used for thumbnail generator binary and is set by passing in HIDE_HIDDEN_FILES = True - lock_folder = True - go_past_home = False + lock_folder = False + go_past_home = True - subpath = "/LazyShare" # modify 'home' folder path - locked_folders = "Synced Backup::::venv::::flasks".split("::::") + iconContainerWH = [128, 128] + systemIconImageWH = [56, 56] + viIconWH = [256, 128] + + subpath = "" # modify 'home' folder path + locked_folders = "venv::::flasks".split("::::") mplayer_options = "-quiet -really-quiet -xy 1600 -geometry 50%:50%".split() music_app = "/opt/deadbeef/bin/deadbeef" media_app = "mpv" diff --git a/src/shellfm/windows/view/utils/icons/archive.png b/src/shellfm/windows/view/utils/icons/archive.png new file mode 100644 index 0000000000000000000000000000000000000000..7943e4e39efd8fc25296d3a28da55f9919ed31a2 GIT binary patch literal 1670 zcmX|B2~?BU68@7|3|f`OvI*4)PmiPx*dwQ<#g+iF#HTz=z$z{fF_2%fnfwq4LW0@Y zR3H!tTNZ+Vl;9%)3?QILSyd9EfQkZAx+x%vU9_b?ZBOTXckaxc@0^)CcgYG53)!%4 z%Q^r6HV{Mik>ELIb)ZqOz85T84WPII#0WGR{c!q!Q?P5x4n4{R0GGzqfjGG(-xXeT zhJubjWL6x+qj0DIkH_-_nF-uj3Y+T5;>4HD@7)5E99BsYAVjAE;c$e5CC0Lt*a#Yx zLEW);YyxJyx61HOglH@dK&CLTp;R6SK~Kb4!ElhY8VqEyQ#fEe4FUoKvHlRm0Z%1D zAd877)8NehbTEO6r9lw;b1$!04wV9dNz_;tgTZ2QR}1l?gRxX5m&)}zm4fF|y!LqR z_Bttw>Hq-5v+x5EL2#CTrK?LdY%svC`G#qz1E%~Vj*gD5t`2`Y7!1b20Tb-!OAPSq zYcuwlj05e)!S-f_ETc#&ER;KDzZKJl_SXBWVZME9A zI&Du=!$3z%8hASW3^hF=PH=`QOo$UE#0%;184MV-^Dvm8C)HU zY-;E+z|i*?^|y@rez?PJ&2ZDKckft7E%!z(_st_?7W3FB49obadBQsS@czArWA`Q} zCubf#_;&iq-1OAk%+$jC^TmaQ7mF|!mtMSBUS2K<5&s6C;)f6^j0_;&u|cnM*zUH= z1Bdtgc#qF#L?S;m^;~9FcJ_$bY_?de;}a7Tua=izyT^R;U-(V<5cl~<&>lZ8`t=yb z->FX{A`Q*IY4Rq2HE@88_5G;X{vBU@xM6*BkCn6sdpghU_C{axQp}2ZZc;*}B-9GVD?J%TMEvJ0QNS;z3fL1T~^tn!*0J{uy8e=k7>{i14rbezP8=wqL;A*ANnWp z071Uimf%6!EZlEP*zHb`?g-cw3Lt+(BG&+Q$J~rj2JkY&3GzmSUFsv+3s2k(eD7+%#EyR#PeKiIx&k$Rk3!A}W$Qv?sEa$aYMo8LUSW7C79UnlW@M(`&Yt-MR_eBnnrgtt(=QwnCUoJx+2dLXUlO6iLI z)5O<+U_(#D8#!>~+r!axy0-eb>z%E;fgu|r(Br+&cC+IS(FBUw-Skr(aG-v-_2w>Q zxs3W920)um?5wq{@BAZtTh0)kE1wJw1z+t%+ASe(*yL|mN~Q7+buG@N4FIz@Gi;Gq z(`+kf>^_W~>h-r@6Y0YqT5GixMj^4mv-jgrPCfM>;uiayb`Ulh?Yv1}UBOfw-bj-M z0wEd?=aC$dAsaEZc^K#vdYkfzT78@Pm)=+O-XiOD@qk8^YPrl)wuniPb^B zy%!jNyj9#b!`wa~m}qpu{vuY_9t#Cp#l-y^qq>Y(xTL4t^jq3)942=h=h6R*=*-)+ zjBOx_TmU8|k_e1|UKOD`x~wN=Z0FIWFK~*=C%I-Zp3!~d;FnE&yEwbnnHx(G!To}U?63HnOl`}=c@ vLkCR>kcaNm+1dL1FZ}w{WAOd{q017TgD*%|V_I@oKTu*&*gkzg%=te7aFZ2g literal 0 HcmV?d00001 diff --git a/src/shellfm/windows/view/utils/icons/audio.png b/src/shellfm/windows/view/utils/icons/audio.png new file mode 100644 index 0000000000000000000000000000000000000000..c0101346b611e7495861f4efe2c511f76b51e5e7 GIT binary patch literal 1544 zcmZuxdpOg382_1#wYlcDP%Wj4+h|9w2Q!nE80M04nOQb$)AnPKVADonltl zL5HREOw8q+D5;PxatTMONfadx#+mciInQ&x&-;Gg=Y2n)_ws$7_xa|L{Pt@ij1d3; zXcB$A0+lv;UEylVd;BubQE8xDPa+wvJbZX;uF~I-6K5kW&<%wn zj+uR`M&*(DT~$eYas}1jLP%A>r5w}z6ZPsB9f^l3j7`ITF2BQLe)^b{n?>IuoIfcm zEm5SuXS-zxN4p=-(6<8jY&HIo<9Mj%<4;>2mRqb2g_zm>W|aVtCPNHgroh!(N`nK_ zQ<~#9w=68{I*@LlaoG{u7Y6ps8f2joYdbHGQO&k>d})pwX)Z^;Ky)gLfW%p(zS;|S zhq0bZqG5|MOKeB2ftAYB62Xfmt(YKfk!{`B;!kpKLB*r1)t#q5ui%^%MzJmM;M%13 zk}Qd+%b=65%lY8%(Y2h~IDw16)&P$VXgnJJI3Cag^}HM0Ic#M`(VmLA1Cfn;y7SWC z?3N-Vj_#s!&k${bt9F3_rW<;B;VS&EXZ8oj526(CF_?pEUw@7qW^0`d)42!9rKq&u z1Y*JfJF85Tp@#+%o%647;s;2GXL-;o=hc86t@f!4CIu_q@$IS5-Ags!P9W*3%CuQS z4|mm$qaG%0klZ{e;D%hU#Y{1mZ4}pru@!GB3tYwW^SaUTwT1_0?*AGEaVBJ5b3Ik6 ze)dAQPW0J|?IKONhRS`hh?e(cf+^_MxtTMm^tH%Bfsd|CEvv#lA#=P29wT3F#X?ao zfkhFyPfg@-mHTwON^C0b8)<7_Di8hFBCwIiQUN+w?8!ZAS__NX4(blQ{_}IrrKQ;5 z7CSQ|^vh(5B6T@`q=`D9%V2l5ZTWydwpL4@>(kDp!*b^tU$?kqPLwI;Rj19dWyh-F zB~s1Y`s+eGX3Awxm&{O+jEjX_)E+%OQzX>rZ|H`Ru_**Wx|df7&beK-x;0trxp*$z zZeu?Gy8Wk<@fH&EPHAPCZuAkK;W|71a&`X|8St00&W}Kkdh8GJjr6W>@!F?BS1D0SAL zxZ;T3&N)bu{|c(QglMKWcIar;P{EA=x07mnrAy!G?UV4fsV_`~-JAB+n?!(bdXGO( z7iyVCIW3zU*jl_-7}~9GD_y(ifg#7+7a&M}23(10r0G)fyU5@#BsY%n7xv~;)ioT1 zu^9$v*!6(K|1)hd)X2ED)qw6VK=PVLE(Uj{i?k=xTnbJu;liYBC?EEVrD$%N(6GyU z%Fm@y_BvJQ*^kx!eUOjLzqRdl3OS?e55>5vEYC7$jhM!2S~>pOZKs}&5eShx;A6D@ zyLbP$j&#@}>FsXB;d4F}2O3c|nYZTZqfS#p?gxyVxAC@&7Vpa#^mUr04nA8b32D39 z!7VLJWB2(IXRl54?~Jk(DtwKKdSQ==p%L@NNS7w*_l5c|k73`|Fb||N=Ss%qr|hKp zFcNPwoI@=KZ0n!Q419iA7#B6nT5v0>`HfkgGTQ5`MsO>O$XbkVT!UHdTL6(U;Cknq{Q{mCL=3J`#dGHam+6_rCl di=8|}I5;?NKlFb92xL>4nJ zNUsNB#yF{oGC)De64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1UVK#D># zOL9^f0)R3_3ZBXNc?uyJsky1DHrHsBf%ipdZ&9wFhW=V}MVHE09J8Z@~aA5*-~K z5fM>PP>_?8^XAPPi0p+67cO4Bc=_^WAotm`XF&4#^XCu}$awMM#mt#A=gyrwZ{EE5 z^XD&EuwdP~bsINs+_GiMwQJYz-@pIh!GlMS9=(438e*XO^6L^n`(#Rj{DK+SIFyvN zwDk>)J-vfMH*MLv|M1}tA3uHm{N?Lc&6p-z1_s79PZ!6KjC*fqlC<5TCeqS|*mRL?Kgc7(I8Vi=8|}I5;?NKlFb92xL>4nJ z$hLzpWB=2SsX#%=64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1UVK#D># zOL9^f0)R3_3ZBXNc?uyJsky1DHrHsBfZYpno_di4|!2nE;;ZdJgpSz-K{;CN})=gZxe$uiHlb3Cnx_r~r6&t6n*feeBrs*p;&sep2 z#_G*8S8tiQdMnTtp!q-(xF)ho0G+E>666=mz{teP#m&ttEFvl4>6lif#XQ z8W13Ilv$zc>xRv19^Jh6-?ox*^G&|HnMr59#~t{-#It^DV2glL{i@!-kE4TcEV%OT z-p;}>w+z1?PA#6E70US!LfC%He;CD4G5_F<15KQ|YZz6wZP-1z`D{=_^8Ki6{avaJ zN7k*_@#bs#uBW^^zIST;wfe&9^4sO*Om&4j>bpKNANjrI3)7R|Q=T#KpZwhZ_*dum zPKyse-tD=U{igWAzqi#}eLQv9Y-+XEJ!hQx&W=4!!(c)y8wWhvU6w0CUnUvw{_K};>3gl1wceg9u(=*FzrTHMS*!j2>yL7`K8m_0?z;72 ikl~}S#Zrnb|3dd~^!AY~RX+CuWU8mDpUXO@geCx;cxYb$ literal 0 HcmV?d00001 diff --git a/src/shellfm/windows/view/utils/icons/doc.png b/src/shellfm/windows/view/utils/icons/doc.png new file mode 100644 index 0000000000000000000000000000000000000000..f8388267bc53ad5b95537b2977873701ac3de87b GIT binary patch literal 702 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD3?#3*wSy#>i=8|}I5;?NKlFb92xL>4nJ zNUsNB#yF{oGC)De64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1U#L5e~$ zOL9^f0)R3_3ZBXNc?uyJskx~NA*s0qIf*5ykA0+kAalscA{WaJky1SjSyc%+tR zmXzFY>2w2127x4<^9w4AGSf3k7@VCI97{@yGLuS6GV}9vgEN3maLmcfPF2V#DJihh z*H11=O)SYQOHIzt&CSm%2DwN-Co?%UuQ;_>KdDl;I8onN&p zZ_9Puk?XiC-}6{u%6r8rAArhKrhnG%zo$Rxf&SbtZY#d}uKt>}<$cc94;{z8cOCoQ zef;}`Q$J^&|26ykuVq(%{r~^}l(nx4&>f;BL4LsuGV1!hQ>M<92A` za$u2V;P7(L&tTlTfNe{YTmTot0$~9MRfh(*0}QPUOht^WN18--M73=5e-P5pBywZE zqa&Bf?#D-!zU*O~{q@o;^8-1PL-!t-%^{%RaKQdUf!l(-hV)bR3I~jz$_qT0S@&69 z;D^cqV^xO-=8c(K|8@#XgfTm*UyupEDR0WO-Js**PYaPZ#^2g)85_AZEX$o=90q!r N!PC{xWt~$(69DZ(DkcB` literal 0 HcmV?d00001 diff --git a/src/shellfm/windows/view/utils/icons/pdf.png b/src/shellfm/windows/view/utils/icons/pdf.png new file mode 100644 index 0000000000000000000000000000000000000000..9f40122a8e9ef281901e085651fa66de960cae4c GIT binary patch literal 925 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD3?#3*wSy#>i=8|}I5;?NKlFb92xL>4nJ zNUsNB#yF{oGC)De64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1U#L5e~$ zOL9^f0)R3_3ZBXNc?uyJskx~NA*s0qIf*5ykA0+kAalscA{WaJky1SjSyc%+tR zmXzFY>2w2127x4<^9w4AGSf3k7@VCI97{@yGLuS6GV}9vgEN3maLmcfPF2V#DJihh z*H11=O)SYQOHIzt&CSm%2DwN-Co?%UuQ;_>KdDl;I8onN&pK?PTIAN%F-q_%-x$%8-V~{Ru^Jlh}&z$X_xi~y`b$XGV{4yf+ zWn?H&`^%{CSBbH&a#P=y6~8Sj`Mhk&=VePjFJJm)Mi|9-EZE1`1tA5moJqLU6Kq8j5VGvjv*QM-d>CJKja|7`e0Z0qDT7{ z@Ue=x9@zCTit+dV|C5)xEuOis`2EaGr$@H>bMLhORy~vHEa0IcWLx}nu9@QL5C7Jh zX&z=3(1Zr%7`Gi@ z+rcECz{PMtSfD}Gfx-O%LmLBA2_x$XCeaErj?Z?Dt0hEM9Jpw!5zzSR;LT>oh)-;D z-n^G8s(8a2u>7j+ zJjt8L#OQU^+W%$ol~c1VVm|%2De->yIiSp{$#TD@bqZhb;MWIIABs=S_AyQP;aJC9 z@7=`^``2qPQ>12fE92L^Z&7o_S!&dSSYtQ+%hYC)a9y}J+~C8l_ggjg7FAu;*!ziN zM)gzP8CSILH(b-+-*8WRe#6G_PuBw(7%L7eyZw(>d&4WXDQgbA*>(Eg)|m(N)`VR6 iI#nofzso27&zyp}+m6{Mw{HSQ6oaR$pUXO@geCx$l#&Sm literal 0 HcmV?d00001 diff --git a/src/shellfm/windows/view/utils/icons/presentation.png b/src/shellfm/windows/view/utils/icons/presentation.png new file mode 100644 index 0000000000000000000000000000000000000000..3a339af593f4932996667370db88dbdc050f86b2 GIT binary patch literal 882 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD3?#3*wSy#>i=8|}I5;?NKlFb92xL>4nJ zNUsNB#yF{oGC)De64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1U#L5e~$ zOL9^f0)R3_3ZBXNc?uyJskx~NA*s0qIf*5ykA0+kAalscA{WaJky1SjSyc%+tR zmXzFY>2w2127x4<^9w4AGSf3k7@VCI97{@yGLuS6GV}9vgEN3maLmcfPF2V#DJihh z*H11=O)SYQOHIzt&CSm%2DwN-Co?%UuQ;_>KdDl;I8onN&pG({c6t+>by80{rP(O=NlPcPs@Hg1J-h0;pch9pXZf+o>%^PQTx|5 zr(f5ce_eC=b=?(Y$m7;ukK2AdZvXSF_s@$Ne_qZ6+WY6_>_4yO{&_X;&#U=B_xydm z2$g7u zy(C%^MBZ?E3;YYq@; zP29q_Hf(L_y2CGRR%D2_ugaMC>EHP$zLTdlCB8p;vvcF`{ZEo=ISURHn)rH$X~^?& zh(7%Ls^Ln8EX%38Wp@R3xwkT3+GD7vwS`yEq18%~fz_G)&hCqCIt-jT41yI53I>e( z4zS)}l77Hd)1b+~;P1dNje${yky(O?;{vOQ0(Zy*GuMdCoH08VIIx~u=h(<~#qWH6 zP>XN&fu|<}v#v1|hgwf#t@C7L6$x-?{LlGi@A>yXrfLN8E%<5_DY1cji=7a2S>tsN zu^0ZA7`pjycDFR@XDUo!JN7Z%Qf#;D<(k(Al4T^K>V2yp{_(l7vHrrm=jk?$lke2b zU%SUt#jfn^@|Uq(d)qEN-}U)cZ)+Sh vg(L2s-MoMEqZICF(UN^ZN^i1n34LRJ(>cXB_UJWGf@bh^^>bP0l+XkK!91hU literal 0 HcmV?d00001 diff --git a/src/shellfm/windows/view/utils/icons/spreadsheet.png b/src/shellfm/windows/view/utils/icons/spreadsheet.png new file mode 100644 index 0000000000000000000000000000000000000000..710efa631bbe46a69e7d43808c675b0f5c2411c0 GIT binary patch literal 707 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD3?#3*wSy#>i=8|}I5;?NKlFb92xL>4nJ zNUsNB#yF{oGC)De64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1U#L5e~$ zOL9^f0)R3_3ZBXNc?uyJskx~NA*s0qIf*5ykA0+kAalscA{WaJky1SjSyc%+tR zmXzFY>2w2127x4<^9w4AGSf3k7@VCI97{@yGLuS6GV}9vgEN3maLmcfPF2V#DJihh z*H11=O)SYQOHIzt&CSm%2DwN-Co?%UuQ;_>KdDl;I8onN&pjHh&2juQ60FprD3nqb#g8ju=dvf#l z7rV~$aGeiCp6-jiz1R4Auk{DY`fUgbSP~q(B0PLcbk?3cpyJje?d?aq=3id8;PRq{ zmlrL)wtVfa_3LhJ*nV&4|NsBZl4mjk-62{MbP0l+XkKZ6g@< literal 0 HcmV?d00001 diff --git a/src/shellfm/windows/view/utils/icons/text.png b/src/shellfm/windows/view/utils/icons/text.png new file mode 100644 index 0000000000000000000000000000000000000000..2546fcd90b78ab5752f74234520af202a6c3987b GIT binary patch literal 798 zcmXAlYe-XJ7{}kuB}=C`tC3Uis2C#YEF(%oowrN5E?>zZM=u=!!)$Hk)mC9(O>wRa ztp}7FMumD>MN&vAS7Q~pfuQN{Z5`z>l!S*Rq_&wNlqwpPlwd-drieN{O^})_sR}Fz z8;E)wQ_-|GPasfGxSS>$aRsSXlNw#vk)VN4;2IsS6Vx?l>Ewdk>>NQ&YxNMQKNTO7 zl|i&xt#-TJ@AuEm&3V0Ehr zKmx%31Q;lRal74+_xJaM_|(*t&1RdPo}QVRnVp@TpPvUFFc5()%gf6EgXYT0%IfMW z7=Vpn07O6&L}2y7b9YXF(PUauDMg&vSYE>31A3#Wwe5Px?T3%Y#>cG-&Ly`u5Dfiv z4d-1#h`|??3T3JV|KwQ-Q^;v|mUFqm$Q!9Hd?=PZ8S}+wB(iH?XMS864LScrkDYpO zq(^b9EU(~AbKi*BvGM%Z;IrkV9%D!xcquds+dsF6t-bHn;)u?m3SshkMqElKD#~Ft zzKiPIl3TmOXR1P|+k|#8n7f&gJ6UlP(HXtG0n2bYC&ctdZXd8H6DQQ7=*u#$Gc`@j zOG)MYU~KvaIr^iozD2e@aiyOuz+~qGBf=|9EOs1rC%K82rd64gp9(Qc?ox7`qifS$ zhy@>^@%xDRdtjr6wV$w*vv`E1m^CWO4=K1F!8X=g-_0UrwK;PvXzd%yk7wArTRwcf nSXpJ**OfNuzE@@2Gn)FB={Hgt{G?7Rd?*r?RhF8IYp?zTm=%Df literal 0 HcmV?d00001 diff --git a/src/shellfm/windows/view/utils/icons/trash.png b/src/shellfm/windows/view/utils/icons/trash.png new file mode 100644 index 0000000000000000000000000000000000000000..c6514b98b5644a1398e5b94566627683bdf8313c GIT binary patch literal 989 zcmV<310wv1P)15yzTLCe34-$-L*zeV%iVi<#7%{pXzDkMHky2mk=z;1Ymqjw*`_ z;l=cX!@yxS)3eMo)!7q(s1S;8XDeoIfk&q@7uoI?s zN^{=g#zud^U7~af3UN1l@tmQLOr|QHWPuroi5&@B4xE5LwSSN~G+$j==zSl}UMNQg zyH_50>t(Oi+-x&5w89uZw-ptSj&|nuPG2fiPCt#GwwmD#Jm6oxx$^wCcxirh-cQ9a ziluU6K|Pn5D~^E<4p7JT?N5^#IMVgKv(20OG$aWqB{-E1 z9oKKm#x_8Yf!EY3G??PhH00od3`|8Cr#dh|?Xc^Z-nF9>;EQ2fnv!5>XX9e5(~w%Q zWxxzw@&_hiR$9Lcoc|vHx!)lO6-mr}?nq6T47i&p1j1BE6+d+ySi293cx+=BS^bPd z2pc)RW6($#nyaDBQ#bUD)T*xo(~>w=@g&O4P0907%sO^H(WGl-(aW~-b3x1wa&{7m zuWn2M!9yrPfv)`{h@?zgx|KUDMpnH#&O}b58kogEan(fp3IGm{0yF@Eai?E1TUpQI zP%w=Iic?7KIfYR^3~mA204Ox%tPdgo9_IQMM*@lfDHI}T5|U27TT;IPe*mD6XwTM& zhIg`@LxX@qgMnb0tVSaItJ7O4fL$Dd0pPc<&*3~0q)-r0(BKH6aSoNuc<)UB0GrMp z6mp=g$)0LI{yHl_Lcst-1y5iV&y7F1{N=AVPXJ(u4pd6_>+}0v$EYsS1=O&N8hVrM zpZ|RC94JwwF%&23;_5K`+J-nN-|HR00000 LNkvXXu0mjfZ-lz6 literal 0 HcmV?d00001 diff --git a/src/shellfm/windows/view/utils/icons/video.png b/src/shellfm/windows/view/utils/icons/video.png new file mode 100644 index 0000000000000000000000000000000000000000..55afa98662c7439702c4391be32151b59c65e253 GIT binary patch literal 1313 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7OGoEX7WqAsj$Z!;#Vf4nJ zNUsNB#yF{oGC)De64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1UVK#D># zOL9^f0)R3_3ZBXNc?uyJsky1DHrHsBf%ipdZ&9wFl@+Mo$;Vkcv5P@A_uQq{heETG?y0yzC9fraSuz>xc-iKB^S-C2j9}#|+2Trc2JzJRTnfTzEQIH^v=t-uh~( zxc^nT;G^AFyj-F_E#k~I^^GcclIec6N$|KDEZ^HL2yRXD%)^JLrNU0ZK4B(tA;`e?y7+bL3Cv<0)SasI_CAaj~N#c(YL7*x>s=RUEzB;@PrBh& zH)D%og8`esF@{4D4id~9$qb1+3OxwHP0QHrcBOO2*;w_|95lANuJ3MPXxI5(O|0kK zRl|QgH_Ue({_&96Vc&zq|1bC+7nk<1Yuu@Ae>qY6-K)v7Yqj^V-D!HvaJ2dMZM$~H zu!p<&;w*k~%)4!W;alV}{X=v0Uhk~<**mx5&CKevXN-+f=bm`FyWqv#SP7|{IXP;~ zcdqPSDZPH*cNRIBH>Ot885zEOeQr=yWB#r(Ve!YC*VuRDXUi=Ow$PzK?rCL+Fg2guB&_nRD$dv!BcjdVIbiOV!A>s5L(B z_M7webFwb`{Y$Vp;Qn~>iWvW|`+K5R742;lemwcu^H&o81xx0 uulP@kQGh{%aR*z&{2E6WF!;j1pYh0Ho3#3@qwc_>hQZU-&t;ucLK6Tg{6(q& literal 0 HcmV?d00001 diff --git a/src/shellfm/windows/view/utils/icons/web.png b/src/shellfm/windows/view/utils/icons/web.png new file mode 100644 index 0000000000000000000000000000000000000000..17017ce3f5c04869fd808d91732939ea9b32b36d GIT binary patch literal 1845 zcmX9-30PBC7JeZt0R&N&im4Vm5LuK(q#_nd0tCVUMu-({*@BToAxV=L*+e!GkXn@r ziUOk+5JanpgIGYI)*&h-ERqnyl7#n?m%Ok@b?nd&oyW}mzH{$?&iTLZo^$Vi%IPuT zRu-Er006L}MTEv;XY!g6@Yp_1EnQ3Cg%lctKp-eo`<0k$&W|`O1OVdwHNz!tzVs;; zbOgiV!8qPAFf&EK0x~l*JvrPH!qgN#%abQayT0({W{mXN8i~OH(^&u=%LtrksXVR| zgU!ldIWbrn{PYx%HMdbK!l)b8sALhy<_Unflnkc`Rwf4oXU~*Vv1II8GK9y^7I4zo zAP^GbLTopXZa#0EirfR|UQfRzGrPO?&Y85umTaIGS*bWSRZ zD`W}1nAtmpDPF#w-d>5tN4f#P=q^2q5r*9|K0dBcDCXzqr>3S*6jdsf7@nJ(TU=ax z^X84gV89~F%gakkOIT!KVF5u9wOXxGsW2B?7{Cbs2N(x)F^*QN#qdzr=R?%L4N;wk z!d-?UTqF@B32nP1(p3`aCW-QpM0-eLeI#+d!*tK#Sf62r@5n*Fk;8jOjs%P)QO1%f zW6aR8RO;B#@Yly8$2rmC=?Ab4>^;QlZta9Ma9XfOQ)x*il%Eyr)$clua!?v zPtV-AI9ppWTYp~JP^G+GgLR~8tyA6mhwA<qq?n0{jdpZeC|;T_Bc>935wYc z?RSIdZcyw_h~Wtx^oEXn0e#~Sb>4-#9zZ=0q23Ot{|WT03#PlnhkW70eQ_glMx^$_R@Y<)VrQXIbz5PntJ0g8Rbn*LUbxsP8jj*_-Z(cX zN{bSF%odkOt+)PVn?cJf{v^KG_!od*Z`y6_gfo9@lyz1e6<-f@N(DIgw^V+X{8VBDgh-5c#-s8%>=Ds{ zKfZ$B2p)baC-hl5^DPUkA4$M1z~aT=lsHFr2wwAQ^^hG?0?{1_9}F`e&AR*C-`<=x z*RYSBq9v~+{b;!tNT=fS*ot>s1gZ^;zlQsQ*$T>i*<;Y?y|^T*Id-^@z$QlD35-#i zJ2sI;MpjWp9kw)4zj?RIGhcdL7=G-=JU=#Yy(dm>A@;T}0CztyE+AaJ=M8AMMO$_| z7Id+bi9q0dam5HecC>c_tpmQK;`iVX>Wcn}ut+z3R7*7gNk8~2Q z1}q%HZJIat|JlfU$d-TD#L3I&q0rktK6eLt#iF1#&8Ilzqp1VwcF)ZjcT=&PXup+d zrtYX6^88blp$1-*%0m?Q_y0cl4qR{hAfh?`YOtxbIN6Bo?^I^~=S_`RJz#u_xuM8K zT55i|B_nE=rK5wevMuH?>Bb+{SGUyyyt<;S|E!pu1J}QO|8A5sa;v2BD^p^p&yHcv RE^NR6T3Afz&y?h%{{oe`z-j;h literal 0 HcmV?d00001