diff --git a/lib/menubar/icon_theme.lua b/lib/menubar/icon_theme.lua index e21e0d699..e7d1b8a2b 100644 --- a/lib/menubar/icon_theme.lua +++ b/lib/menubar/icon_theme.lua @@ -91,6 +91,8 @@ end local icon_theme = { mt = {} } +local index_theme_cache = {} + --- Class constructor of `icon_theme` -- @tparam string icon_theme_name Internal name of icon theme -- @tparam table base_directories Paths used for lookup @@ -103,7 +105,18 @@ icon_theme.new = function(icon_theme_name, base_directories) self.icon_theme_name = icon_theme_name self.base_directories = base_directories self.extensions = { "png", "svg", "xpm" } - self.index_theme = index_theme(self.icon_theme_name, self.base_directories) + + -- Instantiate index_theme (cached). + if not index_theme_cache[self.icon_theme_name] then + index_theme_cache[self.icon_theme_name] = {} + end + local cache_key = table.concat(self.base_directories, ':') + if not index_theme_cache[self.icon_theme_name][cache_key] then + index_theme_cache[self.icon_theme_name][cache_key] = index_theme( + self.icon_theme_name, + self.base_directories) + end + self.index_theme = index_theme_cache[self.icon_theme_name][cache_key] return setmetatable(self, { __index = icon_theme }) end @@ -147,6 +160,7 @@ local directory_size_distance = function(self, subdirectory, icon_size) end local lookup_icon = function(self, icon_name, icon_size) + local checked_already = {} for _, subdir in ipairs(self.index_theme:get_subdirectories()) do for _, basedir in ipairs(self.base_directories) do for _, ext in ipairs(self.extensions) do @@ -156,6 +170,8 @@ local lookup_icon = function(self, icon_name, icon_size) icon_name, ext) if awful.util.file_readable(filename) then return filename + else + checked_already[filename] = true end end end @@ -165,24 +181,24 @@ local lookup_icon = function(self, icon_name, icon_size) local minimal_size = 0xffffffff -- Any large number will do. local closest_filename = nil for _, subdir in ipairs(self.index_theme:get_subdirectories()) do - for _, basedir in ipairs(self.base_directories) do - for _, ext in ipairs(self.extensions) do - local filename = string.format("%s/%s/%s/%s.%s", - basedir, self.icon_theme_name, subdir, - icon_name, ext) - local dist = directory_size_distance(self, subdir, icon_size) - if awful.util.file_readable(filename) and dist < minimal_size then - closest_filename = filename - minimal_size = dist + local dist = directory_size_distance(self, subdir, icon_size) + if dist < minimal_size then + for _, basedir in ipairs(self.base_directories) do + for _, ext in ipairs(self.extensions) do + local filename = string.format("%s/%s/%s/%s.%s", + basedir, self.icon_theme_name, subdir, + icon_name, ext) + if not checked_already[filename] then + if awful.util.file_readable(filename) then + closest_filename = filename + minimal_size = dist + end + end end end end end - if closest_filename then - return closest_filename - end - - return nil + return closest_filename end local find_icon_path_helper -- Gets called recursively. diff --git a/lib/menubar/menu_gen.lua b/lib/menubar/menu_gen.lua index 630cfa111..86fda43d4 100644 --- a/lib/menubar/menu_gen.lua +++ b/lib/menubar/menu_gen.lua @@ -112,7 +112,7 @@ function menu_gen.generate() if target_category then local name = trim(program.Name) or "" local cmdline = trim(program.cmdline) or "" - local icon = utils.lookup_icon(trim(program.icon_path)) or nil + local icon = program.icon_path or nil table.insert(result, { name = name, cmdline = cmdline, icon = icon, diff --git a/lib/menubar/utils.lua b/lib/menubar/utils.lua index 16a65f8a9..57868ee78 100644 --- a/lib/menubar/utils.lua +++ b/lib/menubar/utils.lua @@ -67,40 +67,59 @@ local function is_format_supported(icon_file) end local icon_lookup_path = nil +--- Get a list of icon lookup paths. +-- @treturn table A list of directories, without trailing slash. local function get_icon_lookup_path() if not icon_lookup_path then + local add_if_readable = function(t, path) + if awful_util.dir_readable(path) then + table.insert(t, path) + end + end icon_lookup_path = {} local icon_theme_paths = {} local icon_theme = theme.icon_theme local paths = glib.get_system_data_dirs() table.insert(paths, 1, glib.get_user_data_dir()) - table.insert(paths, 1, glib.get_home_dir() .. '/.icons') - for k,dir in ipairs(paths)do - if icon_theme then - table.insert(icon_theme_paths, dir..'/icons/' .. icon_theme .. '/') + table.insert(paths, 1, glib.build_filenamev({glib.get_home_dir(), + '.icons'})) + for k,dir in ipairs(paths) do + local icons_dir = glib.build_filenamev({dir, 'icons'}) + if awful_util.dir_readable(icons_dir) then + if icon_theme then + add_if_readable(icon_theme_paths, + glib.build_filenamev({icons_dir, + icon_theme})) + end + -- Fallback theme. + add_if_readable(icon_theme_paths, + glib.build_filenamev({icons_dir, 'hicolor'})) end - table.insert(icon_theme_paths, dir..'/icons/hicolor/') -- fallback theme end for i, icon_theme_directory in ipairs(icon_theme_paths) do for j, size in ipairs(all_icon_sizes) do - table.insert(icon_lookup_path, icon_theme_directory .. size .. '/apps/') + add_if_readable(icon_lookup_path, + glib.build_filenamev({icon_theme_directory, + size, 'apps'})) end end for k,dir in ipairs(paths)do -- lowest priority fallbacks - table.insert(icon_lookup_path, dir..'/pixmaps/') - table.insert(icon_lookup_path, dir..'/icons/') + add_if_readable(icon_lookup_path, + glib.build_filenamev({dir, 'pixmaps'})) + add_if_readable(icon_lookup_path, + glib.build_filenamev({dir, 'icons'})) end end return icon_lookup_path end --- Lookup an icon in different folders of the filesystem. --- @param icon_file Short or full name of the icon. --- @return full name of the icon. -function utils.lookup_icon(icon_file) +-- @tparam string icon_file Short or full name of the icon. +-- @treturn string|boolean Full name of the icon, or false on failure. +function utils.lookup_icon_uncached(icon_file) if not icon_file or icon_file == "" then - return default_icon + return false end if icon_file:sub(1, 1) == '/' and is_format_supported(icon_file) then @@ -109,24 +128,36 @@ function utils.lookup_icon(icon_file) return awful_util.file_readable(icon_file) and icon_file or nil else for i, directory in ipairs(get_icon_lookup_path()) do - if is_format_supported(icon_file) and awful_util.file_readable(directory .. icon_file) then - return directory .. icon_file + if is_format_supported(icon_file) and + awful_util.file_readable(directory .. "/" .. icon_file) then + return directory .. "/" .. icon_file else -- Icon is probably specified without path and format, -- like 'firefox'. Try to add supported extensions to -- it and see if such file exists. for _, format in ipairs(icon_formats) do - local possible_file = directory .. icon_file .. "." .. format + local possible_file = directory .. "/" .. icon_file .. "." .. format if awful_util.file_readable(possible_file) then return possible_file end end end end - return default_icon + return false end end +local lookup_icon_cache = {} +--- Lookup an icon in different folders of the filesystem (cached). +-- @param icon_file Short or full name of the icon. +-- @return full name of the icon. +function utils.lookup_icon(icon) + if not lookup_icon_cache[icon] and lookup_icon_cache[icon] ~= false then + lookup_icon_cache[icon] = utils.lookup_icon_uncached(icon) + end + return lookup_icon_cache[icon] or default_icon +end + --- Parse a .desktop file. -- @param file The .desktop file. -- @return A table with file entries.