256 lines
7.8 KiB
Lua
256 lines
7.8 KiB
Lua
|
-- Grab environment
|
||
|
|
||
|
local io = io
|
||
|
local os = os
|
||
|
local table = table
|
||
|
local type = type
|
||
|
local ipairs = ipairs
|
||
|
local pairs = pairs
|
||
|
|
||
|
module("freedesktop.utils")
|
||
|
|
||
|
terminal = 'xterm'
|
||
|
|
||
|
icon_theme = nil
|
||
|
|
||
|
all_icon_sizes = {
|
||
|
'128x128',
|
||
|
'96x96',
|
||
|
'72x72',
|
||
|
'64x64',
|
||
|
'48x48',
|
||
|
'36x36',
|
||
|
'32x32',
|
||
|
'24x24',
|
||
|
'22x22',
|
||
|
'16x16'
|
||
|
}
|
||
|
all_icon_types = {
|
||
|
'apps',
|
||
|
'actions',
|
||
|
'devices',
|
||
|
'places',
|
||
|
'categories',
|
||
|
'status',
|
||
|
'mimetypes'
|
||
|
}
|
||
|
all_icon_paths = { os.getenv("HOME") .. '/.icons/', '/usr/share/icons/' }
|
||
|
|
||
|
icon_sizes = {}
|
||
|
|
||
|
local mime_types = {}
|
||
|
|
||
|
function get_lines(...)
|
||
|
local f = io.popen(...)
|
||
|
return function () -- iterator
|
||
|
local data = f:read()
|
||
|
if data == nil then f:close() end
|
||
|
return data
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function file_exists(filename)
|
||
|
local file = io.open(filename, 'r')
|
||
|
local result = (file ~= nil)
|
||
|
if result then
|
||
|
file:close()
|
||
|
end
|
||
|
return result
|
||
|
end
|
||
|
|
||
|
function lookup_icon(arg)
|
||
|
if arg.icon:sub(1, 1) == '/' and (arg.icon:find('.+%.png') or arg.icon:find('.+%.xpm')) then
|
||
|
-- icons with absolute path and supported (AFAICT) formats
|
||
|
return arg.icon
|
||
|
else
|
||
|
local icon_path = {}
|
||
|
local icon_themes = {}
|
||
|
local icon_theme_paths = {}
|
||
|
if icon_theme and type(icon_theme) == 'table' then
|
||
|
icon_themes = icon_theme
|
||
|
elseif icon_theme then
|
||
|
icon_themes = { icon_theme }
|
||
|
end
|
||
|
for i, theme in ipairs(icon_themes) do
|
||
|
for j, path in ipairs(all_icon_paths) do
|
||
|
table.insert(icon_theme_paths, path .. theme .. '/')
|
||
|
end
|
||
|
-- TODO also look in parent icon themes, as in freedesktop.org specification
|
||
|
end
|
||
|
table.insert(icon_theme_paths, '/usr/share/icons/hicolor/') -- fallback theme cf spec
|
||
|
|
||
|
local isizes = icon_sizes
|
||
|
for i, sz in ipairs(all_icon_sizes) do
|
||
|
table.insert(isizes, sz)
|
||
|
end
|
||
|
|
||
|
for i, icon_theme_directory in ipairs(icon_theme_paths) do
|
||
|
for j, size in ipairs(arg.icon_sizes or isizes) do
|
||
|
for k, icon_type in ipairs(all_icon_types) do
|
||
|
table.insert(icon_path, icon_theme_directory .. size .. '/' .. icon_type .. '/')
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
-- lowest priority fallbacks
|
||
|
table.insert(icon_path, '/usr/share/pixmaps/')
|
||
|
table.insert(icon_path, '/usr/share/icons/')
|
||
|
table.insert(icon_path, '/usr/share/app-install/icons/')
|
||
|
|
||
|
for i, directory in ipairs(icon_path) do
|
||
|
if (arg.icon:find('.+%.png') or arg.icon:find('.+%.xpm')) and file_exists(directory .. arg.icon) then
|
||
|
return directory .. arg.icon
|
||
|
elseif file_exists(directory .. arg.icon .. '.png') then
|
||
|
return directory .. arg.icon .. '.png'
|
||
|
elseif file_exists(directory .. arg.icon .. '.xpm') then
|
||
|
return directory .. arg.icon .. '.xpm'
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function lookup_file_icon(arg)
|
||
|
load_mime_types()
|
||
|
|
||
|
local extension = arg.filename:match('%a+$')
|
||
|
local mime = mime_types[extension] or ''
|
||
|
local mime_family = mime:match('^%a+') or ''
|
||
|
|
||
|
-- possible icons in a typical gnome theme (i.e. Tango icons)
|
||
|
local possible_filenames = {
|
||
|
mime,
|
||
|
'gnome-mime-' .. mime,
|
||
|
mime_family,
|
||
|
'gnome-mime-' .. mime_family,
|
||
|
extension
|
||
|
}
|
||
|
|
||
|
for i, filename in ipairs(possible_filenames) do
|
||
|
local icon = lookup_icon({icon = filename, icon_sizes = (arg.icon_sizes or all_icon_sizes)})
|
||
|
if icon then
|
||
|
return icon
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- If we don't find ad icon, then pretend is a plain text file
|
||
|
return lookup_icon({ icon = 'txt', icon_sizes = arg.icon_sizes or all_icon_sizes })
|
||
|
end
|
||
|
|
||
|
--- Load system MIME types
|
||
|
-- @return A table with file extension <--> MIME type mapping
|
||
|
function load_mime_types()
|
||
|
if #mime_types == 0 then
|
||
|
for line in io.lines('/etc/mime.types') do
|
||
|
if not line:find('^#') then
|
||
|
local parsed = {}
|
||
|
for w in line:gmatch('[^%s]+') do
|
||
|
table.insert(parsed, w)
|
||
|
end
|
||
|
if #parsed > 1 then
|
||
|
for i = 2, #parsed do
|
||
|
mime_types[parsed[i]] = parsed[1]:gsub('/', '-')
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
--- Parse a .desktop file
|
||
|
-- @param file The .desktop file
|
||
|
-- @param requested_icon_sizes A list of icon sizes (optional). If this list is given, it will be used as a priority list for icon sizes when looking up for icons. If you want large icons, for example, you can put '128x128' as the first item in the list.
|
||
|
-- @return A table with file entries.
|
||
|
function parse_desktop_file(arg)
|
||
|
local program = { show = true, file = arg.file }
|
||
|
for line in io.lines(arg.file) do
|
||
|
for key, value in line:gmatch("(%w+)=(.+)") do
|
||
|
program[key] = value
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- Don't show the program if NoDisplay is true
|
||
|
-- Only show the program if there is not OnlyShowIn attribute
|
||
|
-- or if it's equal to 'awesome'
|
||
|
if program.NoDisplay == "true" or program.OnlyShowIn ~= nil and program.OnlyShowIn ~= "awesome" then
|
||
|
program.show = false
|
||
|
end
|
||
|
|
||
|
-- Look up for a icon.
|
||
|
if program.Icon then
|
||
|
program.icon_path = lookup_icon({ icon = program.Icon, icon_sizes = (arg.icon_sizes or all_icon_sizes) })
|
||
|
if program.icon_path ~= nil and not file_exists(program.icon_path) then
|
||
|
program.icon_path = nil
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- Split categories into a table.
|
||
|
if program.Categories then
|
||
|
program.categories = {}
|
||
|
for category in program.Categories:gmatch('[^;]+') do
|
||
|
table.insert(program.categories, category)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if program.Exec then
|
||
|
local cmdline = program.Exec:gsub('%%c', program.Name)
|
||
|
cmdline = cmdline:gsub('%%[fmuFMU]', '')
|
||
|
cmdline = cmdline:gsub('%%k', program.file)
|
||
|
if program.icon_path then
|
||
|
cmdline = cmdline:gsub('%%i', '--icon ' .. program.icon_path)
|
||
|
else
|
||
|
cmdline = cmdline:gsub('%%i', '')
|
||
|
end
|
||
|
if program.Terminal == "true" then
|
||
|
cmdline = terminal .. ' -e ' .. cmdline
|
||
|
end
|
||
|
program.cmdline = cmdline
|
||
|
end
|
||
|
|
||
|
return program
|
||
|
end
|
||
|
|
||
|
--- Parse a directory with .desktop files
|
||
|
-- @param dir The directory.
|
||
|
-- @param icons_size, The icons sizes, optional.
|
||
|
-- @return A table with all .desktop entries.
|
||
|
function parse_desktop_files(arg)
|
||
|
local programs = {}
|
||
|
local files = get_lines('find '.. arg.dir ..' -name "*.desktop" 2>/dev/null')
|
||
|
for file in files do
|
||
|
arg.file = file
|
||
|
table.insert(programs, parse_desktop_file(arg))
|
||
|
end
|
||
|
return programs
|
||
|
end
|
||
|
|
||
|
--- Parse a directory files and subdirs
|
||
|
-- @param dir The directory.
|
||
|
-- @param icons_size, The icons sizes, optional.
|
||
|
-- @return A table with all .desktop entries.
|
||
|
function parse_dirs_and_files(arg)
|
||
|
local files = {}
|
||
|
local paths = get_lines('find '..arg.dir..' -maxdepth 1 -type d')
|
||
|
for path in paths do
|
||
|
if path:match("[^/]+$") then
|
||
|
local file = {}
|
||
|
file.filename = path:match("[^/]+$")
|
||
|
file.path = path
|
||
|
file.show = true
|
||
|
file.icon = lookup_icon({ icon = "folder", icon_sizes = (arg.icon_sizes or all_icon_sizes) })
|
||
|
table.insert(files, file)
|
||
|
end
|
||
|
end
|
||
|
local paths = get_lines('find '..arg.dir..' -maxdepth 1 -type f')
|
||
|
for path in paths do
|
||
|
if not path:find("%.desktop$") then
|
||
|
local file = {}
|
||
|
file.filename = path:match("[^/]+$")
|
||
|
file.path = path
|
||
|
file.show = true
|
||
|
file.icon = lookup_file_icon({ filename = file.filename, icon_sizes = (arg.icon_sizes or all_icon_sizes) })
|
||
|
table.insert(files, file)
|
||
|
end
|
||
|
end
|
||
|
return files
|
||
|
end
|
||
|
|