294 lines
8.9 KiB
Lua
294 lines
8.9 KiB
Lua
-- Grab environment
|
|
|
|
local io = io
|
|
local os = os
|
|
local table = table
|
|
local type = type
|
|
local ipairs = ipairs
|
|
local pairs = pairs
|
|
|
|
local Gtk = require("lgi").Gtk
|
|
|
|
module("freedesktop.utils")
|
|
|
|
terminal = 'xterm'
|
|
|
|
icon_theme = nil
|
|
gtk_icon_theme = Gtk.IconTheme.get_default()
|
|
|
|
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 gtk_icon_info = Gtk.IconTheme.lookup_icon(gtk_icon_theme, arg.icon, 48, 0)
|
|
if gtk_icon_info then
|
|
filename = Gtk.IconInfo.get_filename(gtk_icon_info)
|
|
if filename then
|
|
return filename
|
|
end
|
|
end
|
|
|
|
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 function check_nil(f, v)
|
|
-- Almost the same as
|
|
-- return f and f or v
|
|
-- but it will return false if f = false
|
|
if f == nil then return v else return f end
|
|
end
|
|
|
|
--- Parses .desktop file considering groups.
|
|
-- @param file Path to file
|
|
-- @return A table with group enries. Each group entry is table with file entries.
|
|
local function parse_file(file)
|
|
local result = {}
|
|
local group = nil
|
|
|
|
for line in io.lines(file) do
|
|
group = line:match("^%[([^%[%]%c]+)%]") or group
|
|
if group then
|
|
result[group] = check_nil(result[group], {})
|
|
|
|
for key, value in line:gmatch("(%w+)=(.+)") do
|
|
result[group][key] = value
|
|
end
|
|
end
|
|
end
|
|
|
|
return result
|
|
end
|
|
|
|
-- Using only 'Desktop Entry' group.
|
|
local program = parse_file(arg.file)['Desktop Entry']
|
|
program.show = check_nil(program.show, true)
|
|
program.file = arg.file
|
|
|
|
-- 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
|
|
|