2012-03-08 20:24:21 +01:00
|
|
|
---------------------------------------------------------------------------
|
2015-02-20 15:45:53 +01:00
|
|
|
--- Utility module for menubar
|
2014-05-19 13:37:13 +02:00
|
|
|
--
|
2012-03-08 20:24:21 +01:00
|
|
|
-- @author Antonio Terceiro
|
|
|
|
-- @copyright 2009, 2011-2012 Antonio Terceiro, Alexander Yakushev
|
2014-05-19 13:37:13 +02:00
|
|
|
-- @module menubar.utils
|
2012-03-08 20:24:21 +01:00
|
|
|
---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
-- Grab environment
|
|
|
|
local table = table
|
|
|
|
local ipairs = ipairs
|
|
|
|
local string = string
|
2016-02-26 18:25:55 +01:00
|
|
|
local screen = screen
|
2017-02-14 00:16:45 +01:00
|
|
|
local gfs = require("gears.filesystem")
|
2012-03-08 20:24:21 +01:00
|
|
|
local theme = require("beautiful")
|
2016-03-01 03:17:13 +01:00
|
|
|
local lgi = require("lgi")
|
|
|
|
local gio = lgi.Gio
|
|
|
|
local glib = lgi.GLib
|
2017-07-30 19:42:20 +02:00
|
|
|
local w_textbox = require("wibox.widget.textbox")
|
2017-03-15 06:08:40 +01:00
|
|
|
local gdebug = require("gears.debug")
|
2016-10-19 12:29:54 +02:00
|
|
|
local protected_call = require("gears.protected_call")
|
2017-03-12 00:57:32 +01:00
|
|
|
local gstring = require("gears.string")
|
2018-01-15 22:35:41 +01:00
|
|
|
local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1)
|
2012-03-08 20:24:21 +01:00
|
|
|
|
2012-06-12 10:36:28 +02:00
|
|
|
local utils = {}
|
2012-03-08 20:24:21 +01:00
|
|
|
|
|
|
|
-- NOTE: This icons/desktop files module was written according to the
|
|
|
|
-- following freedesktop.org specifications:
|
|
|
|
-- Icons: http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-0.11.html
|
|
|
|
-- Desktop files: http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.0.html
|
|
|
|
|
|
|
|
-- Options section
|
|
|
|
|
2013-12-29 15:10:31 +01:00
|
|
|
--- Terminal which applications that need terminal would open in.
|
2012-06-12 10:36:28 +02:00
|
|
|
utils.terminal = 'xterm'
|
2012-03-08 20:24:21 +01:00
|
|
|
|
2015-02-20 15:45:53 +01:00
|
|
|
--- The default icon for applications that don't provide any icon in
|
2012-03-08 20:24:21 +01:00
|
|
|
-- their .desktop files.
|
2012-06-12 10:36:28 +02:00
|
|
|
local default_icon = nil
|
2012-03-08 20:24:21 +01:00
|
|
|
|
2017-11-02 20:45:38 +01:00
|
|
|
--- Name of the WM for the OnlyShowIn entry in the .desktop file.
|
2012-11-22 12:11:47 +01:00
|
|
|
utils.wm_name = "awesome"
|
2012-03-08 20:24:21 +01:00
|
|
|
|
2017-09-28 22:57:39 +02:00
|
|
|
--- Possible escape characters (not including the preceding backslash) in
|
|
|
|
--.desktop files and their equates.
|
|
|
|
local escape_sequences = {
|
|
|
|
[ [[\\]] ] = [[\]],
|
|
|
|
[ [[\n]] ] = '\n',
|
|
|
|
[ [[\r]] ] = '\r',
|
|
|
|
[ [[\s]] ] = ' ',
|
|
|
|
[ [[\t]] ] = '\t',
|
|
|
|
}
|
|
|
|
|
2017-11-15 15:21:22 +01:00
|
|
|
-- Maps keys in desktop entries to suitable getter function.
|
|
|
|
-- The order of entries is as in the spec.
|
|
|
|
-- https://standards.freedesktop.org/desktop-entry-spec/latest/ar01s05.html
|
|
|
|
local keys_getters
|
|
|
|
do
|
|
|
|
local function get_string(kf, key)
|
|
|
|
return kf:get_string("Desktop Entry", key)
|
|
|
|
end
|
|
|
|
local function get_strings(kf, key)
|
|
|
|
return kf:get_string_list("Desktop Entry", key, nil)
|
|
|
|
end
|
|
|
|
local function get_localestring(kf, key)
|
|
|
|
return kf:get_locale_string("Desktop Entry", key, nil)
|
|
|
|
end
|
|
|
|
local function get_localestrings(kf, key)
|
|
|
|
return kf:get_locale_string_list("Desktop Entry", key, nil, nil)
|
|
|
|
end
|
|
|
|
local function get_boolean(kf, key)
|
|
|
|
return kf:get_boolean("Desktop Entry", key)
|
|
|
|
end
|
|
|
|
|
|
|
|
keys_getters = {
|
|
|
|
Type = get_string,
|
|
|
|
Version = get_string,
|
|
|
|
Name = get_localestring,
|
|
|
|
GenericName = get_localestring,
|
|
|
|
NoDisplay = get_boolean,
|
|
|
|
Comment = get_localestring,
|
|
|
|
Icon = get_localestring,
|
|
|
|
Hidden = get_boolean,
|
|
|
|
OnlyShowIn = get_strings,
|
|
|
|
NotShowIn = get_strings,
|
|
|
|
DBusActivatable = get_boolean,
|
|
|
|
TryExec = get_string,
|
|
|
|
Exec = get_string,
|
|
|
|
Path = get_string,
|
|
|
|
Terminal = get_boolean,
|
|
|
|
Actions = get_strings,
|
|
|
|
MimeType = get_strings,
|
|
|
|
Categories = get_strings,
|
|
|
|
Implements = get_strings,
|
|
|
|
Keywords = get_localestrings,
|
|
|
|
StartupNotify = get_boolean,
|
|
|
|
StartupWMClass = get_string,
|
|
|
|
URL = get_string,
|
|
|
|
}
|
|
|
|
end
|
2017-09-28 22:57:39 +02:00
|
|
|
|
2012-03-08 20:24:21 +01:00
|
|
|
-- Private section
|
|
|
|
|
2017-06-13 01:59:30 +02:00
|
|
|
local do_protected_call, call_callback
|
|
|
|
do
|
|
|
|
-- Lua 5.1 cannot yield across a protected call. Instead of hardcoding a
|
|
|
|
-- check, we check for this problem: The following coroutine yields true on
|
|
|
|
-- success (so resume() returns true, true). On failure, pcall returns
|
|
|
|
-- false and a message, so resume() returns true, false, message.
|
|
|
|
local _, has_yieldable_pcall = coroutine.resume(coroutine.create(function()
|
|
|
|
return pcall(coroutine.yield, true)
|
|
|
|
end))
|
|
|
|
if has_yieldable_pcall then
|
|
|
|
do_protected_call = protected_call.call
|
|
|
|
call_callback = function(callback, ...)
|
|
|
|
return callback(...)
|
|
|
|
end
|
|
|
|
else
|
|
|
|
do_protected_call = function(f, ...)
|
|
|
|
return f(...)
|
|
|
|
end
|
|
|
|
call_callback = protected_call.call
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-03-08 20:24:21 +01:00
|
|
|
local all_icon_sizes = {
|
2018-01-15 22:35:41 +01:00
|
|
|
'scalable',
|
|
|
|
'128x128',
|
2012-03-08 20:24:21 +01:00
|
|
|
'96x96',
|
|
|
|
'72x72',
|
|
|
|
'64x64',
|
|
|
|
'48x48',
|
|
|
|
'36x36',
|
|
|
|
'32x32',
|
|
|
|
'24x24',
|
|
|
|
'22x22',
|
|
|
|
'16x16'
|
|
|
|
}
|
|
|
|
|
2015-02-20 15:45:53 +01:00
|
|
|
--- List of supported icon formats.
|
2015-02-09 19:49:01 +01:00
|
|
|
local icon_formats = { "png", "xpm", "svg" }
|
2012-03-08 20:24:21 +01:00
|
|
|
|
2015-02-20 15:45:53 +01:00
|
|
|
--- Check whether the icon format is supported.
|
2012-03-08 20:24:21 +01:00
|
|
|
-- @param icon_file Filename of the icon.
|
|
|
|
-- @return true if format is supported, false otherwise.
|
|
|
|
local function is_format_supported(icon_file)
|
|
|
|
for _, f in ipairs(icon_formats) do
|
|
|
|
if icon_file:match('%.' .. f) then
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
2015-02-09 19:23:42 +01:00
|
|
|
local icon_lookup_path = nil
|
2015-10-11 19:53:00 +02:00
|
|
|
--- Get a list of icon lookup paths.
|
|
|
|
-- @treturn table A list of directories, without trailing slash.
|
2015-02-09 19:23:42 +01:00
|
|
|
local function get_icon_lookup_path()
|
2018-01-15 22:35:41 +01:00
|
|
|
if icon_lookup_path then return icon_lookup_path end
|
|
|
|
|
|
|
|
local function ensure_args(t, paths)
|
|
|
|
if type(paths) == 'string' then paths = { paths } end
|
|
|
|
return t or {}, paths
|
|
|
|
end
|
|
|
|
|
|
|
|
local function add_if_readable(t, paths)
|
|
|
|
t, paths = ensure_args(t, paths)
|
|
|
|
|
|
|
|
for _, path in ipairs(paths) do
|
2017-02-14 00:16:45 +01:00
|
|
|
if gfs.dir_readable(path) then
|
2015-10-11 19:53:00 +02:00
|
|
|
table.insert(t, path)
|
|
|
|
end
|
|
|
|
end
|
2018-01-15 22:35:41 +01:00
|
|
|
return t
|
|
|
|
end
|
|
|
|
|
|
|
|
local function add_with_dir(t, paths, dir)
|
|
|
|
t, paths = ensure_args(t, paths)
|
|
|
|
dir = { nil, dir }
|
|
|
|
|
|
|
|
for _, path in ipairs(paths) do
|
|
|
|
dir[1] = path
|
|
|
|
table.insert(t, glib.build_filenamev(dir))
|
2012-03-08 20:24:21 +01:00
|
|
|
end
|
2018-01-15 22:35:41 +01:00
|
|
|
return t
|
|
|
|
end
|
|
|
|
|
|
|
|
icon_lookup_path = {}
|
|
|
|
local theme_priority = { 'hicolor' }
|
|
|
|
if theme.icon_theme then table.insert(theme_priority, 1, theme.icon_theme) end
|
|
|
|
|
|
|
|
local paths = add_with_dir({}, glib.get_home_dir(), '.icons')
|
|
|
|
add_with_dir(paths, {
|
|
|
|
glib.get_user_data_dir(), -- $XDG_DATA_HOME, typically $HOME/.local/share
|
|
|
|
unpack(glib.get_system_data_dirs()) -- $XDG_DATA_DIRS, typically /usr/{,local/}share
|
|
|
|
}, 'icons')
|
|
|
|
add_with_dir(paths, glib.get_system_data_dirs(), 'pixmaps')
|
|
|
|
|
|
|
|
local icon_theme_paths = {}
|
|
|
|
for _, theme_dir in ipairs(theme_priority) do
|
|
|
|
add_if_readable(icon_theme_paths,
|
|
|
|
add_with_dir({}, paths, theme_dir))
|
|
|
|
end
|
|
|
|
|
|
|
|
local app_in_theme_paths = {}
|
|
|
|
for _, icon_theme_directory in ipairs(icon_theme_paths) do
|
|
|
|
for _, size in ipairs(all_icon_sizes) do
|
|
|
|
table.insert(app_in_theme_paths,
|
|
|
|
glib.build_filenamev({ icon_theme_directory,
|
|
|
|
size, 'apps' }))
|
2018-04-22 08:03:28 +02:00
|
|
|
table.insert(app_in_theme_paths,
|
|
|
|
glib.build_filenamev({ icon_theme_directory,
|
|
|
|
size, 'categories' }))
|
2015-02-05 19:42:49 +01:00
|
|
|
end
|
2015-02-09 19:23:42 +01:00
|
|
|
end
|
2018-01-15 22:35:41 +01:00
|
|
|
add_if_readable(icon_lookup_path, app_in_theme_paths)
|
|
|
|
|
|
|
|
return add_if_readable(icon_lookup_path, paths)
|
2015-02-09 19:23:42 +01:00
|
|
|
end
|
|
|
|
|
2017-05-25 01:22:13 +02:00
|
|
|
--- Remove CR newline from the end of the string.
|
|
|
|
-- @param s string to trim
|
|
|
|
function utils.rtrim(s)
|
|
|
|
if not s then return end
|
|
|
|
if string.byte(s, #s) == 13 then
|
|
|
|
return string.sub(s, 1, #s - 1)
|
|
|
|
end
|
|
|
|
return s
|
|
|
|
end
|
|
|
|
|
2017-09-28 22:57:39 +02:00
|
|
|
--- Unescape strings read from desktop files.
|
|
|
|
-- Possible sequences are \n, \r, \s, \t, \\, which have the usual meanings.
|
|
|
|
-- @tparam string s String to unescape
|
|
|
|
-- @treturn string The unescaped string
|
|
|
|
function utils.unescape(s)
|
|
|
|
if not s then return end
|
|
|
|
|
|
|
|
-- Ignore the second return value of string.gsub() (number replaced)
|
|
|
|
s = string.gsub(s, "\\.", function(sequence)
|
|
|
|
return escape_sequences[sequence] or sequence
|
|
|
|
end)
|
|
|
|
return s
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Separate semi-colon separated lists.
|
|
|
|
-- Semi-colons in lists are escaped as '\;'.
|
|
|
|
-- @tparam string s String to parse
|
|
|
|
-- @treturn table A table containing the separated strings. Each element is
|
|
|
|
-- unescaped by utils.unescape().
|
|
|
|
function utils.parse_list(s)
|
|
|
|
if not s then return end
|
|
|
|
|
|
|
|
-- Append terminating semi-colon if not already there.
|
|
|
|
if string.sub(s, -1) ~= ';' then
|
|
|
|
s = s .. ';'
|
|
|
|
end
|
|
|
|
|
|
|
|
local segments = {}
|
|
|
|
local part = ""
|
|
|
|
-- Iterate over sub-strings between semicolons
|
|
|
|
for word, backslashes in string.gmatch(s, "([^;]-(\\*));") do
|
|
|
|
if #backslashes % 2 ~= 0 then
|
|
|
|
-- The semicolon was escaped, remember this part for later
|
|
|
|
part = part .. word:sub(1, -2) .. ";"
|
|
|
|
else
|
|
|
|
table.insert(segments, utils.unescape(part .. word))
|
|
|
|
part = ""
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return segments
|
|
|
|
end
|
|
|
|
|
2015-02-09 19:23:42 +01:00
|
|
|
--- Lookup an icon in different folders of the filesystem.
|
2015-10-12 00:25:39 +02:00
|
|
|
-- @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)
|
2015-02-09 19:23:42 +01:00
|
|
|
if not icon_file or icon_file == "" then
|
2015-10-12 00:25:39 +02:00
|
|
|
return false
|
2015-02-09 19:23:42 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
if icon_file:sub(1, 1) == '/' and is_format_supported(icon_file) then
|
|
|
|
-- If the path to the icon is absolute and its format is
|
|
|
|
-- supported, do not perform a lookup.
|
2017-02-14 00:16:45 +01:00
|
|
|
return gfs.file_readable(icon_file) and icon_file or nil
|
2015-02-09 19:23:42 +01:00
|
|
|
else
|
2016-02-07 14:29:40 +01:00
|
|
|
for _, directory in ipairs(get_icon_lookup_path()) do
|
2015-10-11 19:53:00 +02:00
|
|
|
if is_format_supported(icon_file) and
|
2017-02-14 00:16:45 +01:00
|
|
|
gfs.file_readable(directory .. "/" .. icon_file) then
|
2015-10-11 19:53:00 +02:00
|
|
|
return directory .. "/" .. icon_file
|
2012-03-08 20:24:21 +01:00
|
|
|
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
|
2015-10-11 19:53:00 +02:00
|
|
|
local possible_file = directory .. "/" .. icon_file .. "." .. format
|
2017-02-14 00:16:45 +01:00
|
|
|
if gfs.file_readable(possible_file) then
|
2012-03-08 20:24:21 +01:00
|
|
|
return possible_file
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2015-10-12 00:25:39 +02:00
|
|
|
return false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
local lookup_icon_cache = {}
|
|
|
|
--- Lookup an icon in different folders of the filesystem (cached).
|
2015-11-07 13:55:04 +01:00
|
|
|
-- @param icon Short or full name of the icon.
|
2015-10-12 00:25:39 +02:00
|
|
|
-- @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)
|
2012-03-08 20:24:21 +01:00
|
|
|
end
|
2015-10-12 00:25:39 +02:00
|
|
|
return lookup_icon_cache[icon] or default_icon
|
2012-03-08 20:24:21 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
--- Parse a .desktop file.
|
|
|
|
-- @param file The .desktop file.
|
|
|
|
-- @return A table with file entries.
|
2016-02-26 22:22:01 +01:00
|
|
|
function utils.parse_desktop_file(file)
|
2012-03-08 20:24:21 +01:00
|
|
|
local program = { show = true, file = file }
|
2012-12-01 15:42:00 +01:00
|
|
|
|
|
|
|
-- Parse the .desktop file.
|
|
|
|
-- We are interested in [Desktop Entry] group only.
|
2017-11-15 15:02:25 +01:00
|
|
|
local keyfile = glib.KeyFile()
|
|
|
|
if not keyfile:load_from_file(file, glib.KeyFileFlags.NONE) then
|
|
|
|
return nil
|
2012-03-08 20:24:21 +01:00
|
|
|
end
|
|
|
|
|
2012-12-01 15:42:00 +01:00
|
|
|
-- In case [Desktop Entry] was not found
|
2017-11-15 15:02:25 +01:00
|
|
|
if not keyfile:has_group("Desktop Entry") then
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
|
|
|
|
for _, key in pairs(keyfile:get_keys("Desktop Entry")) do
|
2017-11-15 15:21:22 +01:00
|
|
|
local getter = keys_getters[key] or function(kf, k)
|
|
|
|
return kf:get_string("Desktop Entry", k)
|
2017-11-15 15:02:25 +01:00
|
|
|
end
|
2017-11-15 15:21:22 +01:00
|
|
|
program[key] = getter(keyfile, key)
|
2017-11-15 15:02:25 +01:00
|
|
|
end
|
2012-12-01 15:42:00 +01:00
|
|
|
|
2015-11-27 11:44:47 +01:00
|
|
|
-- In case the (required) 'Name' entry was not found
|
|
|
|
if not program.Name or program.Name == '' then return nil end
|
|
|
|
|
2017-11-15 15:21:22 +01:00
|
|
|
-- Don't show program if NoDisplay attribute is true
|
|
|
|
if program.NoDisplay then
|
2012-03-08 20:24:21 +01:00
|
|
|
program.show = false
|
2017-09-28 22:57:39 +02:00
|
|
|
else
|
|
|
|
-- Only check these values is NoDisplay is true (or non-existent)
|
2012-03-08 20:24:21 +01:00
|
|
|
|
2017-09-28 22:57:39 +02:00
|
|
|
-- Only show the program if there is no OnlyShowIn attribute
|
|
|
|
-- or if it contains wm_name
|
|
|
|
if program.OnlyShowIn then
|
|
|
|
program.show = false -- Assume false until found
|
|
|
|
for _, wm in ipairs(program.OnlyShowIn) do
|
|
|
|
if wm == utils.wm_name then
|
|
|
|
program.show = true
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
|
|
|
else
|
|
|
|
program.show = true
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Only need to check NotShowIn if the program is being shown
|
|
|
|
if program.show and program.NotShowIn then
|
|
|
|
for _, wm in ipairs(program.NotShowIn) do
|
|
|
|
if wm == utils.wm_name then
|
|
|
|
program.show = false
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2012-03-08 20:24:21 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
-- Look up for a icon.
|
|
|
|
if program.Icon then
|
2012-06-30 21:51:11 +02:00
|
|
|
program.icon_path = utils.lookup_icon(program.Icon)
|
2012-03-08 20:24:21 +01:00
|
|
|
end
|
|
|
|
|
2017-09-28 22:57:39 +02:00
|
|
|
-- Make the variable lower-case like the rest of them
|
2012-03-08 20:24:21 +01:00
|
|
|
if program.Categories then
|
2017-09-28 22:57:39 +02:00
|
|
|
program.categories = program.Categories
|
2012-03-08 20:24:21 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
if program.Exec then
|
|
|
|
-- Substitute Exec special codes as specified in
|
|
|
|
-- http://standards.freedesktop.org/desktop-entry-spec/1.1/ar01s06.html
|
2015-01-20 19:28:46 +01:00
|
|
|
if program.Name == nil then
|
|
|
|
program.Name = '['.. file:match("([^/]+)%.desktop$") ..']'
|
|
|
|
end
|
2012-03-08 20:24:21 +01:00
|
|
|
local cmdline = program.Exec:gsub('%%c', program.Name)
|
|
|
|
cmdline = cmdline:gsub('%%[fuFU]', '')
|
|
|
|
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
|
2018-04-22 08:06:38 +02:00
|
|
|
if program.Terminal == true then
|
2012-06-12 10:36:28 +02:00
|
|
|
cmdline = utils.terminal .. ' -e ' .. cmdline
|
2012-03-08 20:24:21 +01:00
|
|
|
end
|
|
|
|
program.cmdline = cmdline
|
|
|
|
end
|
|
|
|
|
|
|
|
return program
|
|
|
|
end
|
|
|
|
|
2016-02-26 22:19:35 +01:00
|
|
|
--- Parse a directory with .desktop files recursively.
|
2016-03-01 03:17:13 +01:00
|
|
|
-- @tparam string dir_path The directory path.
|
|
|
|
-- @tparam function callback Will be fired when all the files were parsed
|
|
|
|
-- with the resulting list of menu entries as argument.
|
|
|
|
-- @tparam table callback.programs Paths of found .desktop files.
|
|
|
|
function utils.parse_dir(dir_path, callback)
|
|
|
|
|
2017-08-21 15:12:38 +02:00
|
|
|
local function get_readable_path(file)
|
|
|
|
return file:get_path() or file:get_uri()
|
|
|
|
end
|
|
|
|
|
2017-05-01 13:23:05 +02:00
|
|
|
local function parser(file, programs)
|
2016-03-01 03:17:13 +01:00
|
|
|
-- Except for "NONE" there is also NOFOLLOW_SYMLINKS
|
2016-04-30 19:20:59 +02:00
|
|
|
local query = gio.FILE_ATTRIBUTE_STANDARD_NAME .. "," .. gio.FILE_ATTRIBUTE_STANDARD_TYPE
|
2017-05-01 13:23:05 +02:00
|
|
|
local enum, err = file:async_enumerate_children(query, gio.FileQueryInfoFlags.NONE)
|
2016-03-01 03:17:13 +01:00
|
|
|
if not enum then
|
2017-08-21 15:12:38 +02:00
|
|
|
gdebug.print_warning(get_readable_path(file) .. ": " .. tostring(err))
|
2016-03-01 03:17:13 +01:00
|
|
|
return
|
2012-12-01 15:42:00 +01:00
|
|
|
end
|
2016-03-01 03:17:13 +01:00
|
|
|
local files_per_call = 100 -- Actual value is not that important
|
|
|
|
while true do
|
|
|
|
local list, enum_err = enum:async_next_files(files_per_call)
|
|
|
|
if enum_err then
|
2017-08-21 15:12:38 +02:00
|
|
|
gdebug.print_error(get_readable_path(file) .. ": " .. tostring(enum_err))
|
2016-03-01 03:17:13 +01:00
|
|
|
return
|
|
|
|
end
|
|
|
|
for _, info in ipairs(list) do
|
|
|
|
local file_type = info:get_file_type()
|
2017-05-01 13:23:05 +02:00
|
|
|
local file_child = enum:get_child(info)
|
2016-03-01 03:17:13 +01:00
|
|
|
if file_type == 'REGULAR' then
|
2017-05-01 13:23:05 +02:00
|
|
|
local path = file_child:get_path()
|
2017-06-28 09:14:27 +02:00
|
|
|
if path then
|
|
|
|
local success, program = pcall(utils.parse_desktop_file, path)
|
|
|
|
if not success then
|
|
|
|
gdebug.print_error("Error while reading '" .. path .. "': " .. program)
|
|
|
|
elseif program then
|
|
|
|
table.insert(programs, program)
|
|
|
|
end
|
2016-03-01 03:17:13 +01:00
|
|
|
end
|
|
|
|
elseif file_type == 'DIRECTORY' then
|
2017-05-01 13:23:05 +02:00
|
|
|
parser(file_child, programs)
|
2016-03-01 03:17:13 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
if #list == 0 then
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
|
|
|
enum:async_close()
|
2012-03-08 20:24:21 +01:00
|
|
|
end
|
2016-03-01 03:17:13 +01:00
|
|
|
|
2017-06-13 01:59:30 +02:00
|
|
|
gio.Async.start(do_protected_call)(function()
|
2016-03-01 03:17:13 +01:00
|
|
|
local result = {}
|
2017-05-01 13:23:05 +02:00
|
|
|
parser(gio.File.new_for_path(dir_path), result)
|
2017-06-13 01:59:30 +02:00
|
|
|
call_callback(callback, result)
|
2017-05-01 13:19:19 +02:00
|
|
|
end)
|
2012-03-08 20:24:21 +01:00
|
|
|
end
|
|
|
|
|
2015-09-20 16:42:24 +02:00
|
|
|
function utils.compute_textbox_width(textbox, s)
|
2017-03-15 06:08:40 +01:00
|
|
|
gdebug.deprecate("Use 'width, _ = textbox:get_preferred_size(s)' directly.", {deprecated_in=4})
|
2016-02-26 18:25:55 +01:00
|
|
|
s = screen[s or mouse.screen]
|
2016-02-07 14:29:40 +01:00
|
|
|
local w, _ = textbox:get_preferred_size(s)
|
2015-09-20 16:42:24 +02:00
|
|
|
return w
|
|
|
|
end
|
|
|
|
|
2015-02-19 22:11:38 +01:00
|
|
|
--- Compute text width.
|
|
|
|
-- @tparam str text Text.
|
2016-02-26 18:25:55 +01:00
|
|
|
-- @tparam number|screen s Screen
|
2015-02-19 22:11:38 +01:00
|
|
|
-- @treturn int Text width.
|
2015-09-20 16:42:24 +02:00
|
|
|
function utils.compute_text_width(text, s)
|
2017-07-30 19:42:20 +02:00
|
|
|
local w, _ = w_textbox(gstring.xml_escape(text)):get_preferred_size(s)
|
2017-03-31 01:25:04 +02:00
|
|
|
return w
|
2015-02-19 22:11:38 +01:00
|
|
|
end
|
|
|
|
|
2012-06-12 10:36:28 +02:00
|
|
|
return utils
|
|
|
|
|
2012-03-08 20:24:21 +01:00
|
|
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|