1--------------------------------------------------------------------------- 2--- (Deprecated) class module for icon lookup for menubar 3-- 4-- @author Kazunobu Kuriyama 5-- @copyright 2015 Kazunobu Kuriyama 6-- @classmod menubar.icon_theme 7--------------------------------------------------------------------------- 8 9-- This implementation is based on the specifications: 10-- Icon Theme Specification 0.12 11-- http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-0.12.html 12 13local beautiful = require("beautiful") 14local gfs = require("gears.filesystem") 15local GLib = require("lgi").GLib 16local index_theme = require("menubar.index_theme") 17 18local ipairs = ipairs 19local setmetatable = setmetatable 20local string = string 21local table = table 22local math = math 23 24local get_pragmatic_base_directories = function() 25 local dirs = {} 26 27 local dir = GLib.build_filenamev({GLib.get_home_dir(), ".icons"}) 28 if gfs.dir_readable(dir) then 29 table.insert(dirs, dir) 30 end 31 32 dir = GLib.build_filenamev({GLib.get_user_data_dir(), "icons"}) 33 if gfs.dir_readable(dir) then 34 table.insert(dirs, dir) 35 end 36 37 for _, v in ipairs(GLib.get_system_data_dirs()) do 38 dir = GLib.build_filenamev({v, "icons"}) 39 if gfs.dir_readable(dir) then 40 table.insert(dirs, dir) 41 end 42 end 43 44 local need_usr_share_pixmaps = true 45 for _, v in ipairs(GLib.get_system_data_dirs()) do 46 dir = GLib.build_filenamev({v, "pixmaps"}) 47 if gfs.dir_readable(dir) then 48 table.insert(dirs, dir) 49 end 50 if dir == "/usr/share/pixmaps" then 51 need_usr_share_pixmaps = false 52 end 53 end 54 55 dir = "/usr/share/pixmaps" 56 if need_usr_share_pixmaps and gfs.dir_readable(dir) then 57 table.insert(dirs, dir) 58 end 59 60 return dirs 61end 62 63local get_default_icon_theme_name = function() 64 local icon_theme_names = { "Adwaita", "gnome", "hicolor" } 65 for _, dir in ipairs(get_pragmatic_base_directories()) do 66 for _, icon_theme_name in ipairs(icon_theme_names) do 67 local filename = string.format("%s/%s/index.theme", dir, icon_theme_name) 68 if gfs.file_readable(filename) then 69 return icon_theme_name 70 end 71 end 72 end 73 return "hicolor" 74end 75 76local icon_theme = { mt = {} } 77 78local index_theme_cache = {} 79 80--- Class constructor of `icon_theme` 81-- @deprecated menubar.icon_theme.new 82-- @tparam string icon_theme_name Internal name of icon theme 83-- @tparam table base_directories Paths used for lookup 84-- @treturn table An instance of the class `icon_theme` 85icon_theme.new = function(icon_theme_name, base_directories) 86 icon_theme_name = icon_theme_name or beautiful.icon_theme or get_default_icon_theme_name() 87 base_directories = base_directories or get_pragmatic_base_directories() 88 89 local self = {} 90 self.icon_theme_name = icon_theme_name 91 self.base_directories = base_directories 92 self.extensions = { "png", "svg", "xpm" } 93 94 -- Instantiate index_theme (cached). 95 if not index_theme_cache[self.icon_theme_name] then 96 index_theme_cache[self.icon_theme_name] = {} 97 end 98 local cache_key = table.concat(self.base_directories, ':') 99 if not index_theme_cache[self.icon_theme_name][cache_key] then 100 index_theme_cache[self.icon_theme_name][cache_key] = index_theme( 101 self.icon_theme_name, 102 self.base_directories) 103 end 104 self.index_theme = index_theme_cache[self.icon_theme_name][cache_key] 105 106 return setmetatable(self, { __index = icon_theme }) 107end 108 109local directory_matches_size = function(self, subdirectory, icon_size) 110 local kind, size, min_size, max_size, threshold = self.index_theme:get_per_directory_keys(subdirectory) 111 112 if kind == "Fixed" then 113 return icon_size == size 114 elseif kind == "Scalable" then 115 return icon_size >= min_size and icon_size <= max_size 116 elseif kind == "Threshold" then 117 return icon_size >= size - threshold and icon_size <= size + threshold 118 end 119 120 return false 121end 122 123local directory_size_distance = function(self, subdirectory, icon_size) 124 local kind, size, min_size, max_size, threshold = self.index_theme:get_per_directory_keys(subdirectory) 125 126 if kind == "Fixed" then 127 return math.abs(icon_size - size) 128 elseif kind == "Scalable" then 129 if icon_size < min_size then 130 return min_size - icon_size 131 elseif icon_size > max_size then 132 return icon_size - max_size 133 end 134 return 0 135 elseif kind == "Threshold" then 136 if icon_size < size - threshold then 137 return min_size - icon_size 138 elseif icon_size > size + threshold then 139 return icon_size - max_size 140 end 141 return 0 142 end 143 144 return 0xffffffff -- Any large number will do. 145end 146 147local lookup_icon = function(self, icon_name, icon_size) 148 local checked_already = {} 149 for _, subdir in ipairs(self.index_theme:get_subdirectories()) do 150 for _, basedir in ipairs(self.base_directories) do 151 for _, ext in ipairs(self.extensions) do 152 if directory_matches_size(self, subdir, icon_size) then 153 local filename = string.format("%s/%s/%s/%s.%s", 154 basedir, self.icon_theme_name, subdir, 155 icon_name, ext) 156 if gfs.file_readable(filename) then 157 return filename 158 else 159 checked_already[filename] = true 160 end 161 end 162 end 163 end 164 end 165 166 local minimal_size = 0xffffffff -- Any large number will do. 167 local closest_filename = nil 168 for _, subdir in ipairs(self.index_theme:get_subdirectories()) do 169 local dist = directory_size_distance(self, subdir, icon_size) 170 if dist < minimal_size then 171 for _, basedir in ipairs(self.base_directories) do 172 for _, ext in ipairs(self.extensions) do 173 local filename = string.format("%s/%s/%s/%s.%s", 174 basedir, self.icon_theme_name, subdir, 175 icon_name, ext) 176 if not checked_already[filename] then 177 if gfs.file_readable(filename) then 178 closest_filename = filename 179 minimal_size = dist 180 end 181 end 182 end 183 end 184 end 185 end 186 return closest_filename 187end 188 189local find_icon_path_helper -- Gets called recursively. 190find_icon_path_helper = function(self, icon_name, icon_size) 191 local filename = lookup_icon(self, icon_name, icon_size) 192 if filename then 193 return filename 194 end 195 196 for _, parent in ipairs(self.index_theme:get_inherits()) do 197 local parent_icon_theme = icon_theme(parent, self.base_directories) 198 filename = find_icon_path_helper(parent_icon_theme, icon_name, icon_size) 199 if filename then 200 return filename 201 end 202 end 203 204 return nil 205end 206 207local lookup_fallback_icon = function(self, icon_name) 208 for _, dir in ipairs(self.base_directories) do 209 for _, ext in ipairs(self.extensions) do 210 local filename = string.format("%s/%s.%s", 211 dir, 212 icon_name, ext) 213 if gfs.file_readable(filename) then 214 return filename 215 end 216 end 217 end 218 return nil 219end 220 221--- Look up an image file based on a given icon name and/or a preferable size. 222-- @deprecated menubar.icon_theme:find_icon_path 223-- @tparam string icon_name Icon name to be looked up 224-- @tparam number icon_size Prefereable icon size 225-- @treturn string Absolute path to the icon file, or nil if not found 226function icon_theme:find_icon_path(icon_name, icon_size) 227 icon_size = icon_size or 16 228 if not icon_name or icon_name == "" then 229 return nil 230 end 231 232 local filename = find_icon_path_helper(self, icon_name, icon_size) 233 if filename then 234 return filename 235 end 236 237 if self.icon_theme_name ~= "hicolor" then 238 filename = find_icon_path_helper(icon_theme("hicolor", self.base_directories), icon_name, icon_size) 239 if filename then 240 return filename 241 end 242 end 243 244 return lookup_fallback_icon(self, icon_name) 245end 246 247icon_theme.mt.__call = function(_, ...) 248 return icon_theme.new(...) 249end 250 251return setmetatable(icon_theme, icon_theme.mt) 252 253-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 254