Use spaces for indenting instead of tabs
authorTuomas Jormola <tj@solitudo.net>
Thu, 17 Mar 2016 20:57:23 +0000 (22:57 +0200)
committerTuomas Jormola <tj@solitudo.net>
Thu, 17 Mar 2016 20:57:23 +0000 (22:57 +0200)
delightful/utils.lua
delightful/widgets/battery.lua
delightful/widgets/cpu.lua
delightful/widgets/datetime.lua
delightful/widgets/imap.lua
delightful/widgets/memory.lua
delightful/widgets/network.lua
delightful/widgets/pulseaudio.lua
delightful/widgets/weather.lua

index f20f1bf..c633d7b 100644 (file)
@@ -24,295 +24,295 @@ module('delightful.utils')
 
 -- get configuration with defaults applied
 function get_empty_config(config_description)
-       local empty_config = {}
-       for config_id, config_description_entry in pairs(config_description) do
-               local config_key = config_description_entry.name
-               empty_config[config_key] =
-                               evaluate_config_value(config_description_entry.default,
-                                               empty_config, config_description_entry.coerce)
-       end
-       return empty_config
+    local empty_config = {}
+    for config_id, config_description_entry in pairs(config_description) do
+        local config_key = config_description_entry.name
+        empty_config[config_key] =
+                evaluate_config_value(config_description_entry.default,
+                        empty_config, config_description_entry.coerce)
+    end
+    return empty_config
 end
 
 -- fully evaluate given configuration against description
 function normalize_config(config_data, config_description)
-       for config_id, config_description_entry in pairs(config_description) do
-               local config_key = config_description_entry.name
-               local config_value
-               if config_data[config_key] == nil then
-                       config_value = config_description_entry.default
-               else
-                       config_value = config_data[config_key]
-               end
-               config_data[config_key] =
-                               evaluate_config_value(config_value,
-                                               config_data,
-                                               config_description_entry.coerce)
-       end
-       return config_data
+    for config_id, config_description_entry in pairs(config_description) do
+        local config_key = config_description_entry.name
+        local config_value
+        if config_data[config_key] == nil then
+            config_value = config_description_entry.default
+        else
+            config_value = config_data[config_key]
+        end
+        config_data[config_key] =
+                evaluate_config_value(config_value,
+                        config_data,
+                        config_description_entry.coerce)
+    end
+    return config_data
 end
 
 -- evaluate a configuration value
 function evaluate_config_value(value, data, coerce)
-       if type(value) == 'function' then
-               value = value(data)
-       end
-       if coerce then
-               value = coerce(value)
-       end
-       if value ~= nil and type(value) == 'string' and #value < 1 then
-               value = nil
-       elseif value ~= nil and type(value) == 'table' then
-               local elemnum = 0
-               for _ in pairs(value) do
-                       elemnum = elemnum + 1
-               end
-               if elemnum == 0 then
-                       value = nil
-               end
-       end
-       return value
+    if type(value) == 'function' then
+        value = value(data)
+    end
+    if coerce then
+        value = coerce(value)
+    end
+    if value ~= nil and type(value) == 'string' and #value < 1 then
+        value = nil
+    elseif value ~= nil and type(value) == 'table' then
+        local elemnum = 0
+        for _ in pairs(value) do
+            elemnum = elemnum + 1
+        end
+        if elemnum == 0 then
+            value = nil
+        end
+    end
+    return value
 end
 
 -- validate configuration against description
 function validate_config(config_data, config_description)
-       local validation_errors = {}
-       -- check required options
-       for config_id, config_description_entry in pairs(config_description) do
-               local config_key = config_description_entry.name
-               if config_description_entry.required and config_data[config_key] == nil then
-                       table.insert(validation_errors,
-                                       string.format('Required configuration option missing or empty: %s', config_key))
-               end
-       end
+    local validation_errors = {}
+    -- check required options
+    for config_id, config_description_entry in pairs(config_description) do
+        local config_key = config_description_entry.name
+        if config_description_entry.required and config_data[config_key] == nil then
+            table.insert(validation_errors,
+                    string.format('Required configuration option missing or empty: %s', config_key))
+        end
+    end
 
-       -- validate options
-       for config_id, config_description_entry in pairs(config_description) do
-               local config_key = config_description_entry.name
-               if config_description_entry.validate and config_data[config_key] ~= nil then
-                       local is_valid, error_string = config_description_entry.validate(config_data[config_key])
-                       if not is_valid then
-                               table.insert(validation_errors,
-                                               string.format('Invalid configuration option "%s": %s', config_key, error_string))
-                       end
-               end
-       end
+    -- validate options
+    for config_id, config_description_entry in pairs(config_description) do
+        local config_key = config_description_entry.name
+        if config_description_entry.validate and config_data[config_key] ~= nil then
+            local is_valid, error_string = config_description_entry.validate(config_data[config_key])
+            if not is_valid then
+                table.insert(validation_errors,
+                        string.format('Invalid configuration option "%s": %s', config_key, error_string))
+            end
+        end
+    end
 
-       -- check unknown options
-       for config_key, config_value in pairs(config_data) do
-               local description_found = false
-               for config_id, config_description_entry in pairs(config_description) do
-                       if config_key == config_description_entry.name then
-                               description_found = true
-                               break
-                       end
-               end
-               if not description_found then
-                       table.insert(validation_errors,
-                               string.format('Unknown configuration option: %s', config_key))
-               end
-       end
+    -- check unknown options
+    for config_key, config_value in pairs(config_data) do
+        local description_found = false
+        for config_id, config_description_entry in pairs(config_description) do
+            if config_key == config_description_entry.name then
+                description_found = true
+                break
+            end
+        end
+        if not description_found then
+            table.insert(validation_errors,
+                string.format('Unknown configuration option: %s', config_key))
+        end
+    end
 
-       return #validation_errors > 0 and validation_errors
+    return #validation_errors > 0 and validation_errors
 end
 
 -- return a string from validation errors
 function format_validation_errors(validation_errors)
-       local error_string = ''
-       for error_index, error_entry in pairs(validation_errors) do
-               error_string = string.format('%s%s', error_string, error_entry)
-               if error_index < #validation_errors then
-                       error_string = string.format('%s\n', error_string)
-               end
-       end
-       return error_string
+    local error_string = ''
+    for error_index, error_entry in pairs(validation_errors) do
+        error_string = string.format('%s%s', error_string, error_entry)
+        if error_index < #validation_errors then
+            error_string = string.format('%s\n', error_string)
+        end
+    end
+    return error_string
 end
 
 -- configuration validators
 function config_string(value)
-       if type(value) ~= 'string' then
-               return false, 'must be a string'
-       end
-       return true
+    if type(value) ~= 'string' then
+        return false, 'must be a string'
+    end
+    return true
 end
 function config_table(value)
-       if type(value) ~= 'table' then
-               return false, 'must be a table'
-       end
-       return true
+    if type(value) ~= 'table' then
+        return false, 'must be a table'
+    end
+    return true
 end
 function config_int(value)
-       if type(value) ~= 'number' then
-               return false, 'must be an integer'
-       end
-       if not tostring(value):find('^-?%d+$') then
-               return false, 'must be an integer'
-       end
-       return true
+    if type(value) ~= 'number' then
+        return false, 'must be an integer'
+    end
+    if not tostring(value):find('^-?%d+$') then
+        return false, 'must be an integer'
+    end
+    return true
 end
 function config_number(value)
-       if type(value) ~= 'number' then
-               return false, 'must be a number'
-       end
-       if not tostring(value):find('^-?%d+\.?%d*$') then
-               return false, 'must be a number'
-       end
-       return true
+    if type(value) ~= 'number' then
+        return false, 'must be a number'
+    end
+    if not tostring(value):find('^-?%d+\.?%d*$') then
+        return false, 'must be a number'
+    end
+    return true
 end
 function config_boolean(value)
-       if type(value) ~= 'boolean' then
-               return false, 'must be true or false'
-       end
-       return true
+    if type(value) ~= 'boolean' then
+        return false, 'must be true or false'
+    end
+    return true
 end
 function config_file(value)
-       if type(value) ~= 'string' then
-               return false, 'invalid file name'
-       end
-       if not awful.util.file_readable(value) then
-               return false, 'file not readable'
-       end
-       return true
+    if type(value) ~= 'string' then
+        return false, 'invalid file name'
+    end
+    if not awful.util.file_readable(value) then
+        return false, 'file not readable'
+    end
+    return true
 end
 
 -- type coersions
 function coerce_table(value)
-       if value == nil or type(value) == 'table' then
-               return value
-       else
-               return { value }
-       end
+    if value == nil or type(value) == 'table' then
+        return value
+    else
+        return { value }
+    end
 end
 
 -- return and cache full path of an icon
 function find_icon_file(icon_description, icon_files, name)
-       -- return a cached entry
-       if icon_files and icon_files[name] then
-               return icon_files[name]
-       end
-       if not icon_description or not icon_files or not name then
-               return
-       end
-       local info = icon_description[name]
-       if not info then
-               return
-       end
-       -- try icon defined in the Awesome theme
-       if beautiful[info.beautiful_name] then
-               icon_files[name] = beautiful[info.beautiful_name]
-       end
-       if not icon_files[name] and info.default_icon then
-               local default_icon
-               if type(info.default_icon) == 'function' then
-                       default_icon = info.default_icon()
-               else
-                       default_icon = info.default_icon
-               end
-               icon_files[name] = freedesktop.utils.lookup_icon({ icon = default_icon })
-       end
-       return icon_files[name]
+    -- return a cached entry
+    if icon_files and icon_files[name] then
+        return icon_files[name]
+    end
+    if not icon_description or not icon_files or not name then
+        return
+    end
+    local info = icon_description[name]
+    if not info then
+        return
+    end
+    -- try icon defined in the Awesome theme
+    if beautiful[info.beautiful_name] then
+        icon_files[name] = beautiful[info.beautiful_name]
+    end
+    if not icon_files[name] and info.default_icon then
+        local default_icon
+        if type(info.default_icon) == 'function' then
+            default_icon = info.default_icon()
+        else
+            default_icon = info.default_icon
+        end
+        icon_files[name] = freedesktop.utils.lookup_icon({ icon = default_icon })
+    end
+    return icon_files[name]
 end
 
 -- return the full paths to icon files using the description
 function find_icon_files(icon_description)
-       if not icon_description then
-               return
-       end
-       -- Ensure system icon themes are available
-       if freedesktop.utils.icon_theme then
-               if type(freedesktop.utils.icon_theme) ~= 'table' then
-                       freedesktop.utils.icon_theme = { freedesktop.utils.icon_theme }
-               end
-       else
-               freedesktop.utils.icon_theme = {}
-       end
-       local system_icon_themes = { 'Adwaita', 'gnome' }
-       local has_system_theme = {}
-       for _, theme_name in pairs(freedesktop.utils.icon_theme) do
-               for _, system_theme_name in pairs(system_icon_themes) do
-                       if theme_name == system_theme_name then
-                               has_system_theme[theme_name] = true
-                       end
-               end
-       end
-       for _, system_theme_name in pairs(system_icon_themes) do
-               if not has_system_theme[system_theme_name] then
-                       table.insert(freedesktop.utils.icon_theme, system_theme_name)
-               end
-       end
-       -- Load icons
-       local icon_files = {}
-       for name, info in pairs(icon_description) do
-               find_icon_file(icon_description, icon_files, name)
-       end
-       return icon_files
+    if not icon_description then
+        return
+    end
+    -- Ensure system icon themes are available
+    if freedesktop.utils.icon_theme then
+        if type(freedesktop.utils.icon_theme) ~= 'table' then
+            freedesktop.utils.icon_theme = { freedesktop.utils.icon_theme }
+        end
+    else
+        freedesktop.utils.icon_theme = {}
+    end
+    local system_icon_themes = { 'Adwaita', 'gnome' }
+    local has_system_theme = {}
+    for _, theme_name in pairs(freedesktop.utils.icon_theme) do
+        for _, system_theme_name in pairs(system_icon_themes) do
+            if theme_name == system_theme_name then
+                has_system_theme[theme_name] = true
+            end
+        end
+    end
+    for _, system_theme_name in pairs(system_icon_themes) do
+        if not has_system_theme[system_theme_name] then
+            table.insert(freedesktop.utils.icon_theme, system_theme_name)
+        end
+    end
+    -- Load icons
+    local icon_files = {}
+    for name, info in pairs(icon_description) do
+        find_icon_file(icon_description, icon_files, name)
+    end
+    return icon_files
 end
 
 -- check if the given colors are defined in the Awesome theme
 function find_theme_color(colors)
-       if type(colors) ~= 'table' then
-               colors = { colors }
-       end
-       for _, theme_color_key in pairs(colors) do
-               if beautiful[theme_color_key] then
-                       return beautiful[theme_color_key]
-               end
-       end
-       return
+    if type(colors) ~= 'table' then
+        colors = { colors }
+    end
+    for _, theme_color_key in pairs(colors) do
+        if beautiful[theme_color_key] then
+            return beautiful[theme_color_key]
+        end
+    end
+    return
 end
 
 -- pad a string with spaces
 function pad_string_with_spaces(s, width)
-       if not width then
-               return s
-       end
-       if not s then
-               s = ''
-       end
-       local s_head = s:sub(1, width)
-       local space = ' '
-       return string.format('%s%s', awful.util.escape(s_head), space:rep(width - #s_head))
+    if not width then
+        return s
+    end
+    if not s then
+        s = ''
+    end
+    local s_head = s:sub(1, width)
+    local space = ' '
+    return string.format('%s%s', awful.util.escape(s_head), space:rep(width - #s_head))
 end
 
 -- print an error message
 function print_error(ident, error_string)
-       if not ident then
-               return
-       end
-       if not error_string then
-               error_string = ident
-               ident = nil
-       end
-       local text = ''
-       if ident then
-               text = string.format('[%s] ', ident)
-       end
-       print(string.format('%s%s', text, error_string))
+    if not ident then
+        return
+    end
+    if not error_string then
+        error_string = ident
+        ident = nil
+    end
+    local text = ''
+    if ident then
+        text = string.format('[%s] ', ident)
+    end
+    print(string.format('%s%s', text, error_string))
 end
 
 function fill_wibox_container(delightful_widgets, delightful_config, wibox_container)
-       if not delightful_widgets or not wibox_container then
-               return
-       end
-       local spacer = wibox.widget.textbox()
-       spacer:set_text(' ')
-       for i, delightful_widget in pairs(delightful_widgets) do
-               local config = delightful_config and delightful_config[delightful_widget]
-               local widgets, icons = delightful_widget:load(config)
-               if not widgets then
-                       widgets = {}
-               end
-               for widget_index, widget in pairs(widgets) do
-                       if i < #delightful_widgets then
-                               wibox_container:add(spacer)
-                       end
-                       if icons and icons[widget_index] then
-                               wibox_container:add(icons[widget_index])
-                       end
-                       wibox_container:add(widget)
-                       if i < #delightful_widgets then
-                               wibox_container:add(spacer)
-                       end
-               end
-       end
+    if not delightful_widgets or not wibox_container then
+        return
+    end
+    local spacer = wibox.widget.textbox()
+    spacer:set_text(' ')
+    for i, delightful_widget in pairs(delightful_widgets) do
+        local config = delightful_config and delightful_config[delightful_widget]
+        local widgets, icons = delightful_widget:load(config)
+        if not widgets then
+            widgets = {}
+        end
+        for widget_index, widget in pairs(widgets) do
+            if i < #delightful_widgets then
+                wibox_container:add(spacer)
+            end
+            if icons and icons[widget_index] then
+                wibox_container:add(icons[widget_index])
+            end
+            wibox_container:add(widget)
+            if i < #delightful_widgets then
+                wibox_container:add(spacer)
+            end
+        end
+    end
 end
index b24e97a..6590093 100644 (file)
@@ -80,177 +80,177 @@ local icon
 local prev_icon
 
 local config_description = {
-       {
-               name     = 'battery',
-               required = true,
-               default  = 'BAT0',
-               validate = function(value)
-                       local status, errors = delightful.utils.config_string(value)
-                       if not status then
-                               return status, errors
-                       end
-                       local battery_path = string.format('/sys/class/power_supply/%s/status', value)
-                       if not awful.util.file_readable(battery_path) then
-                               return false, string.format('Battery not found: %s', value)
-                       end
-                       return true
-               end
-       },
-       {
-               name     = 'command',
-               default  = 'gnome-power-preferences',
-               validate = function(value) return delightful.utils.config_string(value) end
-       },
-       {
-               name     = 'no_icon',
-               validate = function(value) return delightful.utils.config_boolean(value) end
-       },
-       {
-               name     = 'progressbar_height',
-               required = true,
-               default  = 19,
-               validate = function(value) return delightful.utils.config_int(value) end
-       },
-       {
-               name     = 'progressbar_width',
-               required = true,
-               default  = 12,
-               validate = function(value) return delightful.utils.config_int(value) end
-       },
-       {
-               name     = 'update_interval',
-               required = true,
-               default  = 20,
-               validate = function(value) return delightful.utils.config_int(value) end
-       },
+    {
+        name     = 'battery',
+        required = true,
+        default  = 'BAT0',
+        validate = function(value)
+            local status, errors = delightful.utils.config_string(value)
+            if not status then
+                return status, errors
+            end
+            local battery_path = string.format('/sys/class/power_supply/%s/status', value)
+            if not awful.util.file_readable(battery_path) then
+                return false, string.format('Battery not found: %s', value)
+            end
+            return true
+        end
+    },
+    {
+        name     = 'command',
+        default  = 'gnome-power-preferences',
+        validate = function(value) return delightful.utils.config_string(value) end
+    },
+    {
+        name     = 'no_icon',
+        validate = function(value) return delightful.utils.config_boolean(value) end
+    },
+    {
+        name     = 'progressbar_height',
+        required = true,
+        default  = 19,
+        validate = function(value) return delightful.utils.config_int(value) end
+    },
+    {
+        name     = 'progressbar_width',
+        required = true,
+        default  = 12,
+        validate = function(value) return delightful.utils.config_int(value) end
+    },
+    {
+        name     = 'update_interval',
+        required = true,
+        default  = 20,
+        validate = function(value) return delightful.utils.config_int(value) end
+    },
 }
 
 local icon_description = {
-       battery_ac     = { beautiful_name = 'delightful_battery_ac',     default_icon = 'battery-good-charging' },
-       battery_full   = { beautiful_name = 'delightful_battery_full',   default_icon = 'battery-good'          },
-       battery_medium = { beautiful_name = 'delightful_battery_medium', default_icon = 'battery-low'           },
-       battery_low    = { beautiful_name = 'delightful_battery_low',    default_icon = 'battery-caution'       },
-       not_found      = { beautiful_name = 'delightful_not_found',      default_icon = 'dialog-question'       },
-       error          = { beautiful_name = 'delightful_error',          default_icon = 'dialog-error'          },
+    battery_ac     = { beautiful_name = 'delightful_battery_ac',     default_icon = 'battery-good-charging' },
+    battery_full   = { beautiful_name = 'delightful_battery_full',   default_icon = 'battery-good'          },
+    battery_medium = { beautiful_name = 'delightful_battery_medium', default_icon = 'battery-low'           },
+    battery_low    = { beautiful_name = 'delightful_battery_low',    default_icon = 'battery-caution'       },
+    not_found      = { beautiful_name = 'delightful_not_found',      default_icon = 'dialog-question'       },
+    error          = { beautiful_name = 'delightful_error',          default_icon = 'dialog-error'          },
 }
 
 -- Configuration handler
 function handle_config(user_config)
-       local empty_config = delightful.utils.get_empty_config(config_description)
-       if not user_config then
-               user_config = empty_config
-       end
-       local config_data = delightful.utils.normalize_config(user_config, config_description)
-       local validation_errors = delightful.utils.validate_config(config_data, config_description)
-       if validation_errors then
-               fatal_error = 'Configuration errors: \n'
-               for error_index, error_entry in pairs(validation_errors) do
-                       fatal_error = string.format('%s %s', fatal_error, error_entry)
-                       if error_index < #validation_errors then
-                               fatal_error = string.format('%s \n', fatal_error)
-                       end
-               end
-               battery_config = empty_config
-               return
-       end
-       battery_config = config_data
+    local empty_config = delightful.utils.get_empty_config(config_description)
+    if not user_config then
+        user_config = empty_config
+    end
+    local config_data = delightful.utils.normalize_config(user_config, config_description)
+    local validation_errors = delightful.utils.validate_config(config_data, config_description)
+    if validation_errors then
+        fatal_error = 'Configuration errors: \n'
+        for error_index, error_entry in pairs(validation_errors) do
+            fatal_error = string.format('%s %s', fatal_error, error_entry)
+            if error_index < #validation_errors then
+                fatal_error = string.format('%s \n', fatal_error)
+            end
+        end
+        battery_config = empty_config
+        return
+    end
+    battery_config = config_data
 end
 
 -- Initalization
 function load(self, config)
-       handle_config(config)
-       if fatal_error then
-               delightful.utils.print_error('battery', fatal_error)
-               return nil, nil
-       end
-       if not battery_config.no_icon then
-               icon_files = delightful.utils.find_icon_files(icon_description)
-       end
-       if icon_files.battery_ac and icon_files.battery_full and icon_files.battery_medium and icon_files.battery_low and icon_files.not_found and icon_files.error then
-               local buttons = awful.button({}, 1, function()
-                               if not fatal_error and battery_config.command then
-                                       awful.util.spawn(battery_config.command, true)
-                               end
-               end)
-               icon = wibox.widget.imagebox()
-               icon:buttons(buttons)
-               icon_tooltip = awful.tooltip({ objects = { icon } })
-       end
+    handle_config(config)
+    if fatal_error then
+        delightful.utils.print_error('battery', fatal_error)
+        return nil, nil
+    end
+    if not battery_config.no_icon then
+        icon_files = delightful.utils.find_icon_files(icon_description)
+    end
+    if icon_files.battery_ac and icon_files.battery_full and icon_files.battery_medium and icon_files.battery_low and icon_files.not_found and icon_files.error then
+        local buttons = awful.button({}, 1, function()
+                if not fatal_error and battery_config.command then
+                    awful.util.spawn(battery_config.command, true)
+                end
+        end)
+        icon = wibox.widget.imagebox()
+        icon:buttons(buttons)
+        icon_tooltip = awful.tooltip({ objects = { icon } })
+    end
 
-       local bg_color        = delightful.utils.find_theme_color({ 'bg_widget', 'bg_normal'                     })
-       local fg_color        = delightful.utils.find_theme_color({ 'fg_widget', 'fg_normal'                     })
-       local fg_center_color = delightful.utils.find_theme_color({ 'fg_center_widget', 'fg_widget', 'fg_normal' })
-       local fg_end_color    = delightful.utils.find_theme_color({ 'fg_end_widget', 'fg_widget', 'fg_normal'    })
+    local bg_color        = delightful.utils.find_theme_color({ 'bg_widget', 'bg_normal'                     })
+    local fg_color        = delightful.utils.find_theme_color({ 'fg_widget', 'fg_normal'                     })
+    local fg_center_color = delightful.utils.find_theme_color({ 'fg_center_widget', 'fg_widget', 'fg_normal' })
+    local fg_end_color    = delightful.utils.find_theme_color({ 'fg_end_widget', 'fg_widget', 'fg_normal'    })
 
-       local battery_widget = awful.widget.progressbar()
-       if bg_color then
-               battery_widget:set_border_color(bg_color)
-               battery_widget:set_background_color(bg_color)
-       end
-       local color_args = fg_color
-       local height = beautiful.progressbar_height or battery_config.progressbar_height
-       local width  = beautiful.progressbar_width  or battery_config.progressbar_width
-       if fg_color and fg_center_color and fg_end_color then
-               color_args = {
-                       type = 'linear',
-                       from = { 0, 0 },
-                       to = { width, height },
-                       stops = {{ 0, fg_end_color }, { 0.5, fg_center_color }, { 1, fg_color }},
-               }
-       end
-       battery_widget:set_color(color_args)
-       battery_widget:set_width(width)
-       battery_widget:set_height(height)
-       battery_widget:set_vertical(true)
-       vicious.register(battery_widget, vicious.widgets.bat, vicious_formatter, battery_config.update_interval, battery_config.battery)
+    local battery_widget = awful.widget.progressbar()
+    if bg_color then
+        battery_widget:set_border_color(bg_color)
+        battery_widget:set_background_color(bg_color)
+    end
+    local color_args = fg_color
+    local height = beautiful.progressbar_height or battery_config.progressbar_height
+    local width  = beautiful.progressbar_width  or battery_config.progressbar_width
+    if fg_color and fg_center_color and fg_end_color then
+        color_args = {
+            type = 'linear',
+            from = { 0, 0 },
+            to = { width, height },
+            stops = {{ 0, fg_end_color }, { 0.5, fg_center_color }, { 1, fg_color }},
+        }
+    end
+    battery_widget:set_color(color_args)
+    battery_widget:set_width(width)
+    battery_widget:set_height(height)
+    battery_widget:set_vertical(true)
+    vicious.register(battery_widget, vicious.widgets.bat, vicious_formatter, battery_config.update_interval, battery_config.battery)
 
-       return { battery_widget }, { icon }
+    return { battery_widget }, { icon }
 end
 
 -- Vicious display formatter, also update widget tooltip and icon
 function vicious_formatter(widget, data)
-       -- update tooltip
-       local unknown = false
-       if icon_tooltip then
-               local tooltip_text
-               if fatal_error then
-                       tooltip_text = fatal_error
-               elseif data[1] == '↯' then
-                       tooltip_text = 'Battery is charged'
-               elseif data[1] == '+' then
-                       tooltip_text = string.format('Battery charge %d%% \n On AC power, %s until charged', data[2], data[3])
-               elseif data[1] == '−' then
-                       tooltip_text = string.format('Battery charge %d%% \n On battery power, %s left', data[2], data[3])
-               else
-                       tooltip_text = 'Battery status is unknown'
-                       unknown = true
-               end
-               icon_tooltip:set_text(string.format(' %s ', tooltip_text))
-       end
-       -- update icon
-       if icon then
-               local icon_file
-               if fatal_error then
-                       icon_file = icon_files.error
-               elseif unknown then
-                       icon_file = icon_files.not_found
-               elseif data[1] == '+' then
-                       icon_file = icon_files.battery_ac
-               elseif data[2] >= 50 and data[2] <= 100 then
-                       icon_file = icon_files.battery_full
-               elseif data[2] >= 15 and data[2] < 50   then
-                       icon_file = icon_files.battery_medium
-               elseif data[2] >= 0  and data[2] < 15   then
-                       icon_file = icon_files.battery_low
-               end
-               if icon_file and (not prev_icon or prev_icon ~= icon_file) then
-                       prev_icon = icon_file
-                       icon:set_image(icon_file)
-               end
-       end
-       if fatal_error then
-               return 0
-       else
-               return data[2]
-       end
+    -- update tooltip
+    local unknown = false
+    if icon_tooltip then
+        local tooltip_text
+        if fatal_error then
+            tooltip_text = fatal_error
+        elseif data[1] == '↯' then
+            tooltip_text = 'Battery is charged'
+        elseif data[1] == '+' then
+            tooltip_text = string.format('Battery charge %d%% \n On AC power, %s until charged', data[2], data[3])
+        elseif data[1] == '−' then
+            tooltip_text = string.format('Battery charge %d%% \n On battery power, %s left', data[2], data[3])
+        else
+            tooltip_text = 'Battery status is unknown'
+            unknown = true
+        end
+        icon_tooltip:set_text(string.format(' %s ', tooltip_text))
+    end
+    -- update icon
+    if icon then
+        local icon_file
+        if fatal_error then
+            icon_file = icon_files.error
+        elseif unknown then
+            icon_file = icon_files.not_found
+        elseif data[1] == '+' then
+            icon_file = icon_files.battery_ac
+        elseif data[2] >= 50 and data[2] <= 100 then
+            icon_file = icon_files.battery_full
+        elseif data[2] >= 15 and data[2] < 50   then
+            icon_file = icon_files.battery_medium
+        elseif data[2] >= 0  and data[2] < 15   then
+            icon_file = icon_files.battery_low
+        end
+        if icon_file and (not prev_icon or prev_icon ~= icon_file) then
+            prev_icon = icon_file
+            icon:set_image(icon_file)
+        end
+    end
+    if fatal_error then
+        return 0
+    else
+        return data[2]
+    end
 end
index 414f9a8..45c89e6 100644 (file)
@@ -68,121 +68,121 @@ local fatal_error
 local icon_tooltip
 
 local config_description = {
-       {
-               name     = 'command',
-               validate = function(value) return delightful.utils.config_string(value) end
-       },
-       {
-               name     = 'no_icon',
-               validate = function(value) return delightful.utils.config_boolean(value) end
-       },
-       {
-               name     = 'graph_height',
-               required = true,
-               default  = 19,
-               validate = function(value) return delightful.utils.config_int(value) end
-       },
-       {
-               name     = 'graph_width',
-               required = true,
-               default  = 30,
-               validate = function(value) return delightful.utils.config_int(value) end
-       },
-       {
-               name     = 'update_interval',
-               required = true,
-               default  = 1,
-               validate = function(value) return delightful.utils.config_int(value) end
-       },
+    {
+        name     = 'command',
+        validate = function(value) return delightful.utils.config_string(value) end
+    },
+    {
+        name     = 'no_icon',
+        validate = function(value) return delightful.utils.config_boolean(value) end
+    },
+    {
+        name     = 'graph_height',
+        required = true,
+        default  = 19,
+        validate = function(value) return delightful.utils.config_int(value) end
+    },
+    {
+        name     = 'graph_width',
+        required = true,
+        default  = 30,
+        validate = function(value) return delightful.utils.config_int(value) end
+    },
+    {
+        name     = 'update_interval',
+        required = true,
+        default  = 1,
+        validate = function(value) return delightful.utils.config_int(value) end
+    },
 }
 
 local icon_description = {
-       cpu = { beautiful_name = 'delightful_cpu', default_icon = 'mate-sensors-applet-cpu' },
+    cpu = { beautiful_name = 'delightful_cpu', default_icon = 'mate-sensors-applet-cpu' },
 }
 
 -- Configuration handler
 function handle_config(user_config)
-       local empty_config = delightful.utils.get_empty_config(config_description)
-       if not user_config then
-               user_config = empty_config
-       end
-       local config_data = delightful.utils.normalize_config(user_config, config_description)
-       local validation_errors = delightful.utils.validate_config(config_data, config_description)
-       if validation_errors then
-               fatal_error = 'Configuration errors: \n'
-               for error_index, error_entry in pairs(validation_errors) do
-                       fatal_error = string.format('%s %s', fatal_error, error_entry)
-                       if error_index < #validation_errors then
-                               fatal_error = string.format('%s \n', fatal_error)
-                       end
-               end
-               cpu_config = empty_config
-               return
-       end
-       cpu_config = config_data
+    local empty_config = delightful.utils.get_empty_config(config_description)
+    if not user_config then
+        user_config = empty_config
+    end
+    local config_data = delightful.utils.normalize_config(user_config, config_description)
+    local validation_errors = delightful.utils.validate_config(config_data, config_description)
+    if validation_errors then
+        fatal_error = 'Configuration errors: \n'
+        for error_index, error_entry in pairs(validation_errors) do
+            fatal_error = string.format('%s %s', fatal_error, error_entry)
+            if error_index < #validation_errors then
+                fatal_error = string.format('%s \n', fatal_error)
+            end
+        end
+        cpu_config = empty_config
+        return
+    end
+    cpu_config = config_data
 end
 
 -- Initalization
 function load(self, config)
-       handle_config(config)
-       if fatal_error then
-               delightful.utils.print_error('cpu', fatal_error)
-               return nil, nil
-       end
-       local icon
-       local icon_files
-       if not cpu_config.no_icon then
-               icon_files = delightful.utils.find_icon_files(icon_description)
-       end
-       local icon_file = icon_files and icon_files.cpu
-       if icon_file then
-               local buttons = awful.button({}, 1, function()
-                       if not fatal_error and cpu_config.command then
-                               awful.util.spawn(cpu_config.command, true)
-                       end
-               end)
-               icon = wibox.widget.imagebox()
-               icon:buttons(buttons)
-               icon:set_image(icon_file)
-               icon_tooltip = awful.tooltip({ objects = { icon } })
-       end
+    handle_config(config)
+    if fatal_error then
+        delightful.utils.print_error('cpu', fatal_error)
+        return nil, nil
+    end
+    local icon
+    local icon_files
+    if not cpu_config.no_icon then
+        icon_files = delightful.utils.find_icon_files(icon_description)
+    end
+    local icon_file = icon_files and icon_files.cpu
+    if icon_file then
+        local buttons = awful.button({}, 1, function()
+            if not fatal_error and cpu_config.command then
+                awful.util.spawn(cpu_config.command, true)
+            end
+        end)
+        icon = wibox.widget.imagebox()
+        icon:buttons(buttons)
+        icon:set_image(icon_file)
+        icon_tooltip = awful.tooltip({ objects = { icon } })
+    end
 
-       local bg_color        = delightful.utils.find_theme_color({ 'bg_widget', 'bg_normal'                     })
-       local fg_color        = delightful.utils.find_theme_color({ 'fg_widget', 'fg_normal'                     })
-       local fg_center_color = delightful.utils.find_theme_color({ 'fg_center_widget', 'fg_widget', 'fg_normal' })
-       local fg_end_color    = delightful.utils.find_theme_color({ 'fg_end_widget', 'fg_widget', 'fg_normal'    })
+    local bg_color        = delightful.utils.find_theme_color({ 'bg_widget', 'bg_normal'                     })
+    local fg_color        = delightful.utils.find_theme_color({ 'fg_widget', 'fg_normal'                     })
+    local fg_center_color = delightful.utils.find_theme_color({ 'fg_center_widget', 'fg_widget', 'fg_normal' })
+    local fg_end_color    = delightful.utils.find_theme_color({ 'fg_end_widget', 'fg_widget', 'fg_normal'    })
 
-       local cpu_widget = awful.widget.graph()
-       if bg_color then
-               cpu_widget:set_background_color(bg_color)
-               cpu_widget:set_border_color(bg_color)
-       end
-       local color_args = fg_color
-       local height = beautiful.graph_height or cpu_config.graph_height
-       local width  = beautiful.graph_width  or cpu_config.graph_width
-       if fg_color and fg_center_color and fg_end_color then
-               color_args = {
-                       type = 'linear',
-                       from = { width / 2, 0 },
-                       to = { width / 2, height },
-                       stops = {{ 0, fg_end_color }, { 0.5, fg_center_color }, { 1, fg_color }},
-               }
-       end
-       cpu_widget:set_color(color_args)
-       cpu_widget:set_width(width)
-       cpu_widget:set_height(height)
-       local w = wibox.layout.fixed.horizontal()
-       w:add(cpu_widget)
-       vicious.register(cpu_widget, vicious.widgets.cpu, vicious_formatter, cpu_config.update_interval)
+    local cpu_widget = awful.widget.graph()
+    if bg_color then
+        cpu_widget:set_background_color(bg_color)
+        cpu_widget:set_border_color(bg_color)
+    end
+    local color_args = fg_color
+    local height = beautiful.graph_height or cpu_config.graph_height
+    local width  = beautiful.graph_width  or cpu_config.graph_width
+    if fg_color and fg_center_color and fg_end_color then
+        color_args = {
+            type = 'linear',
+            from = { width / 2, 0 },
+            to = { width / 2, height },
+            stops = {{ 0, fg_end_color }, { 0.5, fg_center_color }, { 1, fg_color }},
+        }
+    end
+    cpu_widget:set_color(color_args)
+    cpu_widget:set_width(width)
+    cpu_widget:set_height(height)
+    local w = wibox.layout.fixed.horizontal()
+    w:add(cpu_widget)
+    vicious.register(cpu_widget, vicious.widgets.cpu, vicious_formatter, cpu_config.update_interval)
 
-       return { w }, { icon }
+    return { w }, { icon }
 end
 
 -- Vicious display formatter, also update widget tooltip
 function vicious_formatter(widget, data)
-       if icon_tooltip then
-               local tooltip_text = string.format(' CPU usage trend graph of all the CPUs in the system \n Current CPU usage: %d%% ', data[1])
-               icon_tooltip:set_text(tooltip_text)
-       end
-       return data[1]
+    if icon_tooltip then
+        local tooltip_text = string.format(' CPU usage trend graph of all the CPUs in the system \n Current CPU usage: %d%% ', data[1])
+        icon_tooltip:set_text(tooltip_text)
+    end
+    return data[1]
 end
index e918c4a..4f187ba 100644 (file)
@@ -34,12 +34,12 @@ local string = { format = string.format }
 module('delightful.widgets.datetime')
 
 function load()
-       local widget = awful.widget.textclock()
-       local calendar_format = '%s'
-       if beautiful.fg_focus and beautiful.bg_focus then
-               calendar_format = string.format('<span color="%s" background="%s">%%s</span>',
-                               beautiful.fg_focus, beautiful.bg_focus)
-       end
-       calendar2.addCalendarToWidget(widget, calendar_format)
-       return { widget } -- no icon
+    local widget = awful.widget.textclock()
+    local calendar_format = '%s'
+    if beautiful.fg_focus and beautiful.bg_focus then
+        calendar_format = string.format('<span color="%s" background="%s">%%s</span>',
+                beautiful.fg_focus, beautiful.bg_focus)
+    end
+    calendar2.addCalendarToWidget(widget, calendar_format)
+    return { widget } -- no icon
 end
index 8f00cb2..09622e8 100644 (file)
@@ -113,449 +113,449 @@ local imap_data         = {}
 local notification_data = {}
 
 local config_description = {
-       {
-               name     = 'user',
-               required = true,
-               validate = function(value) return delightful.utils.config_string(value) end
-       },
-       {
-               name     = 'password',
-               required = true,
-               validate = function(value) return delightful.utils.config_string(value) end
-       },
-       {
-               name     = 'host',
-               required = true,
-               validate = function(value) return delightful.utils.config_string(value) end
-       },
-       {
-               name     = 'ssl',
-               required = true,
-               default  = false,
-               validate = function(value) return delightful.utils.config_boolean(value) end
-       },
-       {
-               name     = 'ssl_string',
-               required = true,
-               default  = function(config_data) if config_data.ssl then return 'sslv3' else return 'none' end end,
-               validate = function(value) if not value or (value ~= 'sslv3' and value ~= 'none') then return false, 'needs to be either "sslv3" or "none"' else return true end end
-       },
-       {
-               name     = 'port',
-               required = true,
-               default  = function(config_data) if config_data.ssl then return 993 else return 143 end end,
-               validate = function(value) return delightful.utils.config_int(value) end
-       },
-       {
-               name     = 'mailboxes',
-               required = true,
-               default  = 'INBOX',
-               coerce   = function(value) return delightful.utils.coerce_table(value) end,
-               validate = function(value) return delightful.utils.config_table(value) end
-       },
-       {
-               name     = 'show_mail_count',
-               required = true,
-               default  = 5,
-               validate = function(value) return delightful.utils.config_int(value) end
-       },
-       {
-               name     = 'command',
-               default  = function(config_data) if mailer_cmd then return mailer_cmd end end,
-               validate = function(value) return delightful.utils.config_string(value) end
-       },
-       {
-               name     = 'no_icon',
-               validate = function(value) return delightful.utils.config_boolean(value) end
-       },
-       {
-               name     = 'update_interval',
-               required = true,
-               default  = 5 * 60,
-               validate = function(value) return delightful.utils.config_int(value) end
-       },
-       {
-               name     = 'notification_delay',
-               required = true,
-               default  = 10,
-               validate = function(value) return delightful.utils.config_int(value) end
-       },
-       -- User is not supposed to supply configuration of these settings
-       {
-               name     = 'font',
-               required = true,
-               default  = function(config_data) return beautiful.monospace_font or 'monospace' end,
-               validate = function(value) return delightful.utils.config_string(value) end,
-       },
+    {
+        name     = 'user',
+        required = true,
+        validate = function(value) return delightful.utils.config_string(value) end
+    },
+    {
+        name     = 'password',
+        required = true,
+        validate = function(value) return delightful.utils.config_string(value) end
+    },
+    {
+        name     = 'host',
+        required = true,
+        validate = function(value) return delightful.utils.config_string(value) end
+    },
+    {
+        name     = 'ssl',
+        required = true,
+        default  = false,
+        validate = function(value) return delightful.utils.config_boolean(value) end
+    },
+    {
+        name     = 'ssl_string',
+        required = true,
+        default  = function(config_data) if config_data.ssl then return 'sslv3' else return 'none' end end,
+        validate = function(value) if not value or (value ~= 'sslv3' and value ~= 'none') then return false, 'needs to be either "sslv3" or "none"' else return true end end
+    },
+    {
+        name     = 'port',
+        required = true,
+        default  = function(config_data) if config_data.ssl then return 993 else return 143 end end,
+        validate = function(value) return delightful.utils.config_int(value) end
+    },
+    {
+        name     = 'mailboxes',
+        required = true,
+        default  = 'INBOX',
+        coerce   = function(value) return delightful.utils.coerce_table(value) end,
+        validate = function(value) return delightful.utils.config_table(value) end
+    },
+    {
+        name     = 'show_mail_count',
+        required = true,
+        default  = 5,
+        validate = function(value) return delightful.utils.config_int(value) end
+    },
+    {
+        name     = 'command',
+        default  = function(config_data) if mailer_cmd then return mailer_cmd end end,
+        validate = function(value) return delightful.utils.config_string(value) end
+    },
+    {
+        name     = 'no_icon',
+        validate = function(value) return delightful.utils.config_boolean(value) end
+    },
+    {
+        name     = 'update_interval',
+        required = true,
+        default  = 5 * 60,
+        validate = function(value) return delightful.utils.config_int(value) end
+    },
+    {
+        name     = 'notification_delay',
+        required = true,
+        default  = 10,
+        validate = function(value) return delightful.utils.config_int(value) end
+    },
+    -- User is not supposed to supply configuration of these settings
+    {
+        name     = 'font',
+        required = true,
+        default  = function(config_data) return beautiful.monospace_font or 'monospace' end,
+        validate = function(value) return delightful.utils.config_string(value) end,
+    },
 }
 
 local icon_description = {
-       read   = { beautiful_name = 'delightful_imap_mail_read',   default_icon = 'mail-read'    },
-       unread = { beautiful_name = 'delightful_imap_mail_unread', default_icon = 'mail-unread'  },
-       error  = { beautiful_name = 'delightful_error',            default_icon = 'dialog-error' },
+    read   = { beautiful_name = 'delightful_imap_mail_read',   default_icon = 'mail-read'    },
+    unread = { beautiful_name = 'delightful_imap_mail_unread', default_icon = 'mail-unread'  },
+    error  = { beautiful_name = 'delightful_error',            default_icon = 'dialog-error' },
 }
 
 -- Poll the mailbox
 function update_data(imap_index)
-       if not imap_data[imap_index] or not imap_data[imap_index].connection or not imap_config[imap_index].mailboxes then
-               return
-       end
-       local connection = imap_data[imap_index].connection
-       imap_data[imap_index].unread_total  = 0
-       imap_data[imap_index].status_string = ' '
-       for mailbox_index, mailbox in pairs(imap_config[imap_index].mailboxes) do
-               local mailbox_url = string.format('%s/%s', imap_url(imap_index), mailbox)
-               imap_data[imap_index].mailboxes[mailbox_index].messages = nil
-               connection.mailbox = mailbox
+    if not imap_data[imap_index] or not imap_data[imap_index].connection or not imap_config[imap_index].mailboxes then
+        return
+    end
+    local connection = imap_data[imap_index].connection
+    imap_data[imap_index].unread_total  = 0
+    imap_data[imap_index].status_string = ' '
+    for mailbox_index, mailbox in pairs(imap_config[imap_index].mailboxes) do
+        local mailbox_url = string.format('%s/%s', imap_url(imap_index), mailbox)
+        imap_data[imap_index].mailboxes[mailbox_index].messages = nil
+        connection.mailbox = mailbox
 
-               local total, unread_num, imap_error
-               total, imap_error = connection:total()
-               if total then
-                       unread_num, imap_result = connection:unread()
-                       if unread_num then
-                               imap_data[imap_index].mailboxes[mailbox_index].total = total
-                               imap_data[imap_index].mailboxes[mailbox_index].unread = unread_num
-                               imap_data[imap_index].unread_total = imap_data[imap_index].unread_total + unread_num
-                               if unread_num > 0 then
-                                       local unread_messages
-                                       imap_error, unread_messages = connection:fetch(false, true, false)
-                                       if unread_messages then
-                                               local unread_message_ids = {}
-                                               for unread_message_id in pairs(unread_messages) do
-                                                       table.insert(unread_message_ids, unread_message_id)
-                                               end
-                                               table.sort(unread_message_ids)
-                                               imap_data[imap_index].mailboxes[mailbox_index].messages = {}
-                                               for unread_message_index, unread_message_id in pairs(awful.util.table.reverse(unread_message_ids)) do
-                                                       imap_data[imap_index].mailboxes[mailbox_index].messages[unread_message_index] =
-                                                                       unread_messages[unread_message_id]
-                                                       imap_data[imap_index].mailboxes[mailbox_index].messages[unread_message_index].uid =
-                                                                       unread_message_id
-                                               end
-                                               if not imap_data[imap_index].mailboxes[mailbox_index].latest_message
-                                                               or imap_data[imap_index].mailboxes[mailbox_index].latest_message ~= imap_data[imap_index].mailboxes[mailbox_index].messages[1].uid then
-                                                       local n = 1
-                                                       while imap_data[imap_index].mailboxes[mailbox_index].latest_message
-                                                                       and imap_data[imap_index].mailboxes[mailbox_index].messages[n]
-                                                                       and imap_data[imap_index].mailboxes[mailbox_index].messages[n].uid ~= imap_data[imap_index].mailboxes[mailbox_index].latest_message do
-                                                               if not notification_data[imap_index] then
-                                                                       notification_data[imap_index] = {}
-                                                               end
-                                                               if not notification_data[imap_index][mailbox_index] then
-                                                                       notification_data[imap_index][mailbox_index] = {}
-                                                               end
-                                                               table.insert(notification_data[imap_index][mailbox_index],
-                                                                               imap_data[imap_index].mailboxes[mailbox_index].messages[n])
-                                                               n = n + 1
-                                                       end
-                                                       imap_data[imap_index].mailboxes[mailbox_index].latest_message =
-                                                                       imap_data[imap_index].mailboxes[mailbox_index].messages[1].uid
-                                                       imap_data[imap_index].mailboxes[mailbox_index].error_string = nil
-                                               end
-                                       else
-                                               imap_data[imap_index].mailboxes[mailbox_index].error_string =
-                                                               string.format('Failed to fetch unread messages in %s: %s',
-                                                                               mailbox_url, imap_error)
-                                       end
-                               end
-                       else
-                               imap_data[imap_index].mailboxes[mailbox_index].error_string =
-                                               string.format('Failed to check the number of unread messages in %s: %s',
-                                                               mailbox_url, imap_error)
-                       end
-               else
-                       imap_data[imap_index].mailboxes[mailbox_index].error_string =
-                                       string.format('Failed to check the total number of messages in %s: %s',
-                                                       mailbox_url, imap_error)
-               end
-               local mailbox_status = '-'
-               if imap_data[imap_index].mailboxes[mailbox_index].error_string then
-                       mailbox_status = string.format('<span color="red">%s</span>', mailbox_status);
-               elseif unread_num then
-                       mailbox_status = tostring(unread_num)
-               end
-               imap_data[imap_index].status_string =
-                               string.format('%s%s', imap_data[imap_index].status_string,
-                                               mailbox_status);
-               if mailbox_index < #imap_config[imap_index].mailboxes then
-                       imap_data[imap_index].status_string =
-                                       string.format('%s ', imap_data[imap_index].status_string);
-               end
-       end
+        local total, unread_num, imap_error
+        total, imap_error = connection:total()
+        if total then
+            unread_num, imap_result = connection:unread()
+            if unread_num then
+                imap_data[imap_index].mailboxes[mailbox_index].total = total
+                imap_data[imap_index].mailboxes[mailbox_index].unread = unread_num
+                imap_data[imap_index].unread_total = imap_data[imap_index].unread_total + unread_num
+                if unread_num > 0 then
+                    local unread_messages
+                    imap_error, unread_messages = connection:fetch(false, true, false)
+                    if unread_messages then
+                        local unread_message_ids = {}
+                        for unread_message_id in pairs(unread_messages) do
+                            table.insert(unread_message_ids, unread_message_id)
+                        end
+                        table.sort(unread_message_ids)
+                        imap_data[imap_index].mailboxes[mailbox_index].messages = {}
+                        for unread_message_index, unread_message_id in pairs(awful.util.table.reverse(unread_message_ids)) do
+                            imap_data[imap_index].mailboxes[mailbox_index].messages[unread_message_index] =
+                                    unread_messages[unread_message_id]
+                            imap_data[imap_index].mailboxes[mailbox_index].messages[unread_message_index].uid =
+                                    unread_message_id
+                        end
+                        if not imap_data[imap_index].mailboxes[mailbox_index].latest_message
+                                or imap_data[imap_index].mailboxes[mailbox_index].latest_message ~= imap_data[imap_index].mailboxes[mailbox_index].messages[1].uid then
+                            local n = 1
+                            while imap_data[imap_index].mailboxes[mailbox_index].latest_message
+                                    and imap_data[imap_index].mailboxes[mailbox_index].messages[n]
+                                    and imap_data[imap_index].mailboxes[mailbox_index].messages[n].uid ~= imap_data[imap_index].mailboxes[mailbox_index].latest_message do
+                                if not notification_data[imap_index] then
+                                    notification_data[imap_index] = {}
+                                end
+                                if not notification_data[imap_index][mailbox_index] then
+                                    notification_data[imap_index][mailbox_index] = {}
+                                end
+                                table.insert(notification_data[imap_index][mailbox_index],
+                                        imap_data[imap_index].mailboxes[mailbox_index].messages[n])
+                                n = n + 1
+                            end
+                            imap_data[imap_index].mailboxes[mailbox_index].latest_message =
+                                    imap_data[imap_index].mailboxes[mailbox_index].messages[1].uid
+                            imap_data[imap_index].mailboxes[mailbox_index].error_string = nil
+                        end
+                    else
+                        imap_data[imap_index].mailboxes[mailbox_index].error_string =
+                                string.format('Failed to fetch unread messages in %s: %s',
+                                        mailbox_url, imap_error)
+                    end
+                end
+            else
+                imap_data[imap_index].mailboxes[mailbox_index].error_string =
+                        string.format('Failed to check the number of unread messages in %s: %s',
+                                mailbox_url, imap_error)
+            end
+        else
+            imap_data[imap_index].mailboxes[mailbox_index].error_string =
+                    string.format('Failed to check the total number of messages in %s: %s',
+                            mailbox_url, imap_error)
+        end
+        local mailbox_status = '-'
+        if imap_data[imap_index].mailboxes[mailbox_index].error_string then
+            mailbox_status = string.format('<span color="red">%s</span>', mailbox_status);
+        elseif unread_num then
+            mailbox_status = tostring(unread_num)
+        end
+        imap_data[imap_index].status_string =
+                string.format('%s%s', imap_data[imap_index].status_string,
+                        mailbox_status);
+        if mailbox_index < #imap_config[imap_index].mailboxes then
+            imap_data[imap_index].status_string =
+                    string.format('%s ', imap_data[imap_index].status_string);
+        end
+    end
 end
 
 -- Update widget icon based on the IMAP status data
 function update_icon(imap_index)
-       if not icon_files.read or not icon_files.unread or not icon_files.error then
-               return
-       end
-       if not imap_index or not icons[imap_index] or not imap_data[imap_index] then
-               return
-       end
-       if not imap_data[imap_index].unread_total and not imap_data[imap_index].error_string then
-               return
-       end
-       local icon_file
-       if imap_data[imap_index].unread_total then
-               icon_file = icon_files.read 
-               if imap_data[imap_index].unread_total > 0 then
-                       icon_file = icon_files.unread 
-               end
-       end
-       if imap_data[imap_index].error_string then
-               icon_file = icon_files.error 
-       end
-       if icon_file and
-                       (not prev_icons[imap_index] or prev_icons[imap_index] ~= icon_file) then
-               prev_icons[imap_index] = icon_file
-               icons[imap_index]:set_image(icon_file)
-       end
+    if not icon_files.read or not icon_files.unread or not icon_files.error then
+        return
+    end
+    if not imap_index or not icons[imap_index] or not imap_data[imap_index] then
+        return
+    end
+    if not imap_data[imap_index].unread_total and not imap_data[imap_index].error_string then
+        return
+    end
+    local icon_file
+    if imap_data[imap_index].unread_total then
+        icon_file = icon_files.read
+        if imap_data[imap_index].unread_total > 0 then
+            icon_file = icon_files.unread
+        end
+    end
+    if imap_data[imap_index].error_string then
+        icon_file = icon_files.error
+    end
+    if icon_file and
+            (not prev_icons[imap_index] or prev_icons[imap_index] ~= icon_file) then
+        prev_icons[imap_index] = icon_file
+        icons[imap_index]:set_image(icon_file)
+    end
 end
 
 -- Text for the hover notification
 function summary_text(imap_index)
-       local text = ''
-       if not imap_index or not imap_data[imap_index] then
-               return text
-       end
-       if imap_data[imap_index].error_string then
-               text = imap_data[imap_index].error_string
-       else
-               for mailbox_index, mailbox in pairs(imap_data[imap_index].mailboxes) do
-                       text = string.format('%s<span font_style="italic">%s</span>', text, mailbox.name)
-                       if mailbox.error_string then
-                               text = string.format('%s\n  <span color="red">%s</span>\n',
-                                               text, mailbox.error_string
-                               )
-                       else
-                               text = string.format('%s, <span font_weight="bold">%d</span> unread, <span font_weight="bold">%d</span> total\n',
-                                               text, mailbox.unread, mailbox.total
-                               )
-                               if mailbox.messages then
-                                       for message_count, message in pairs(mailbox.messages) do
-                                               text = string.format('%s  %s <span font_weight="bold">%s</span>\n',
-                                                               text,
-                                                               pad_message_detail(message.from),
-                                                               pad_message_detail(message.subject)
-                                               )
-                                               if message_count >= imap_config[imap_index].show_mail_count then
-                                                       total_message_count = #mailbox.messages
-                                                       if(total_message_count > message_count) then
-                                                               text = string.format('%s  ... and <span font_weight="bold">%d</span> more\n',
-                                                                               text,
-                                                                               total_message_count - message_count
-                                                               )
-                                                       end
-                                                       break
-                                               end
-                                       end
-                               end
-                       end
-                       text = string.format('%s\n', text)
-               end
-       end
-       return text:gsub('\n*$', '')
+    local text = ''
+    if not imap_index or not imap_data[imap_index] then
+        return text
+    end
+    if imap_data[imap_index].error_string then
+        text = imap_data[imap_index].error_string
+    else
+        for mailbox_index, mailbox in pairs(imap_data[imap_index].mailboxes) do
+            text = string.format('%s<span font_style="italic">%s</span>', text, mailbox.name)
+            if mailbox.error_string then
+                text = string.format('%s\n  <span color="red">%s</span>\n',
+                        text, mailbox.error_string
+                )
+            else
+                text = string.format('%s, <span font_weight="bold">%d</span> unread, <span font_weight="bold">%d</span> total\n',
+                        text, mailbox.unread, mailbox.total
+                )
+                if mailbox.messages then
+                    for message_count, message in pairs(mailbox.messages) do
+                        text = string.format('%s  %s <span font_weight="bold">%s</span>\n',
+                                text,
+                                pad_message_detail(message.from),
+                                pad_message_detail(message.subject)
+                        )
+                        if message_count >= imap_config[imap_index].show_mail_count then
+                            total_message_count = #mailbox.messages
+                            if(total_message_count > message_count) then
+                                text = string.format('%s  ... and <span font_weight="bold">%d</span> more\n',
+                                        text,
+                                        total_message_count - message_count
+                                )
+                            end
+                            break
+                        end
+                    end
+                end
+            end
+            text = string.format('%s\n', text)
+        end
+    end
+    return text:gsub('\n*$', '')
 end
 
 -- Notification of new messages
 function show_notifications(imap_index)
-       if not imap_index or
-                       not notification_data[imap_index] or
-                       not imap_data[imap_index] or
-                       not imap_data[imap_index].mailboxes then
-               return
-       end
-       local text = string.format('<span font_weight="bold">%s</span>\n', imap_url(imap_index))
-       for mailbox_index, notifications in pairs(notification_data[imap_index]) do
-               local mailbox_name = imap_data[imap_index].mailboxes[mailbox_index].name
-               text = string.format('%s<span font_style="italic">%s</span>, <span font_weight="bold">%d</span> new\n', text, mailbox_name, #notifications)
-               while notifications[1] do
-                       local message = table.remove(notifications, 1)
-                       text = string.format('%s  %s <span font_weight="bold">%s</span>\n',
-                                       text,
-                                       pad_message_detail(message.from),
-                                       pad_message_detail(message.subject)
-                       )
-               end
-               if mailbox_index < #notification_data[imap_index] then
-                       text = string.format('%s\n', text)
-               end
-       end
-       notification_data[imap_index] = nil
-       naughty.notify({
-                       text    = text,
-                       icon    = icon_files.unread,
-                       font    = imap_config[imap_index].font or 'monospace',
-                       timeout = imap_config[imap_index].notification_delay,
-                       screen  = capi.mouse.screen
-       })
+    if not imap_index or
+            not notification_data[imap_index] or
+            not imap_data[imap_index] or
+            not imap_data[imap_index].mailboxes then
+        return
+    end
+    local text = string.format('<span font_weight="bold">%s</span>\n', imap_url(imap_index))
+    for mailbox_index, notifications in pairs(notification_data[imap_index]) do
+        local mailbox_name = imap_data[imap_index].mailboxes[mailbox_index].name
+        text = string.format('%s<span font_style="italic">%s</span>, <span font_weight="bold">%d</span> new\n', text, mailbox_name, #notifications)
+        while notifications[1] do
+            local message = table.remove(notifications, 1)
+            text = string.format('%s  %s <span font_weight="bold">%s</span>\n',
+                    text,
+                    pad_message_detail(message.from),
+                    pad_message_detail(message.subject)
+            )
+        end
+        if mailbox_index < #notification_data[imap_index] then
+            text = string.format('%s\n', text)
+        end
+    end
+    notification_data[imap_index] = nil
+    naughty.notify({
+            text    = text,
+            icon    = icon_files.unread,
+            font    = imap_config[imap_index].font or 'monospace',
+            timeout = imap_config[imap_index].notification_delay,
+            screen  = capi.mouse.screen
+    })
 end
 
 -- Configuration handler
 function handle_config(user_config)
-       local empty_config = delightful.utils.get_empty_config(config_description)
-       if not user_config or #user_config == 0 then
-               table.insert(imap_data,   { error_string = 'No IMAP configuration' })
-               table.insert(imap_config, empty_config)
-               return
-       end
-       for imap_index, user_config_data in pairs(user_config) do
-               imap_data[imap_index] = {}
-               local config_data = delightful.utils.normalize_config(user_config_data, config_description)
-               local validation_errors = delightful.utils.validate_config(config_data, config_description)
-               if validation_errors then
-                       imap_data[imap_index].error_string =
-                                       string.format('Configuration errors:\n%s',
-                                                       delightful.utils.format_validation_errors(validation_errors))
-                       imap_config[imap_index] = empty_config
-                       return
-               end
-               imap_config[imap_index] = config_data
+    local empty_config = delightful.utils.get_empty_config(config_description)
+    if not user_config or #user_config == 0 then
+        table.insert(imap_data,   { error_string = 'No IMAP configuration' })
+        table.insert(imap_config, empty_config)
+        return
+    end
+    for imap_index, user_config_data in pairs(user_config) do
+        imap_data[imap_index] = {}
+        local config_data = delightful.utils.normalize_config(user_config_data, config_description)
+        local validation_errors = delightful.utils.validate_config(config_data, config_description)
+        if validation_errors then
+            imap_data[imap_index].error_string =
+                    string.format('Configuration errors:\n%s',
+                            delightful.utils.format_validation_errors(validation_errors))
+            imap_config[imap_index] = empty_config
+            return
+        end
+        imap_config[imap_index] = config_data
 
-               -- check that connection to the IMAP server works
-               local imap_error
-               local connection = imap.new(imap_config[imap_index].host,
-                               imap_config[imap_index].port,
-                               imap_config[imap_index].ssl_string
-               )
-               _, imap_error = connection:connect()
-               if imap_error then
-                       imap_data[imap_index].error_string =
-                                       string.format('Failed to connect to %s: %s',
-                                                       imap_url(imap_config[imap_index]), imap_error)
-                       return
-               end
-               _, imap_error = connection:login(imap_config[imap_index].user, imap_config[imap_index].password)
-               if imap_error then
-                       imap_data[imap_index].error_string =
-                                       string.format('Failed to login to %s as user %s: %s',
-                                                       imap_url(imap_config[imap_index]), imap_config[imap_index].user, imap_error)
-                       return
-               end
-               imap_data[imap_index].connection = connection
-               imap_data[imap_index].mailboxes  = {}
-               for mailbox_index, mailbox_name in pairs(imap_config[imap_index].mailboxes) do
-                       imap_data[imap_index].mailboxes[mailbox_index] = { name = mailbox_name }
-               end
-       end
+        -- check that connection to the IMAP server works
+        local imap_error
+        local connection = imap.new(imap_config[imap_index].host,
+                imap_config[imap_index].port,
+                imap_config[imap_index].ssl_string
+        )
+        _, imap_error = connection:connect()
+        if imap_error then
+            imap_data[imap_index].error_string =
+                    string.format('Failed to connect to %s: %s',
+                            imap_url(imap_config[imap_index]), imap_error)
+            return
+        end
+        _, imap_error = connection:login(imap_config[imap_index].user, imap_config[imap_index].password)
+        if imap_error then
+            imap_data[imap_index].error_string =
+                    string.format('Failed to login to %s as user %s: %s',
+                            imap_url(imap_config[imap_index]), imap_config[imap_index].user, imap_error)
+            return
+        end
+        imap_data[imap_index].connection = connection
+        imap_data[imap_index].mailboxes  = {}
+        for mailbox_index, mailbox_name in pairs(imap_config[imap_index].mailboxes) do
+            imap_data[imap_index].mailboxes[mailbox_index] = { name = mailbox_name }
+        end
+    end
 end
 
 -- Initalization
 function load(self, config)
-       handle_config(config)
-       icon_files = delightful.utils.find_icon_files(icon_description)
-       for imap_index, data in pairs(imap_data) do
-               local icon
-               if not imap_config[imap_index].no_icon and icon_files.read and icon_files.unread and icon_files.error then
-                       icon = wibox.widget.imagebox()
-               end
+    handle_config(config)
+    icon_files = delightful.utils.find_icon_files(icon_description)
+    for imap_index, data in pairs(imap_data) do
+        local icon
+        if not imap_config[imap_index].no_icon and icon_files.read and icon_files.unread and icon_files.error then
+            icon = wibox.widget.imagebox()
+        end
 
-               local popup_enter = function()
-                       local popup_title
-                       if data.error_string then
-                               popup_title = 'Error'
-                       else
-                               popup_title = imap_url(imap_index)
-                       end
-                       data.popup = naughty.notify({
-                                       title   = popup_title,
-                                       text    = summary_text(imap_index),
-                                       font    = imap_config[imap_index].font or 'monospace',
-                                       timeout = imap_config[imap_index].notification_delay,
-                                       screen  = capi.mouse.screen
-                       })
-               end
-               local popup_leave = function() naughty.destroy(data.popup) end
+        local popup_enter = function()
+            local popup_title
+            if data.error_string then
+                popup_title = 'Error'
+            else
+                popup_title = imap_url(imap_index)
+            end
+            data.popup = naughty.notify({
+                    title   = popup_title,
+                    text    = summary_text(imap_index),
+                    font    = imap_config[imap_index].font or 'monospace',
+                    timeout = imap_config[imap_index].notification_delay,
+                    screen  = capi.mouse.screen
+            })
+        end
+        local popup_leave = function() naughty.destroy(data.popup) end
 
-               local widget = wibox.widget.textbox()
+        local widget = wibox.widget.textbox()
 
-               widget:connect_signal('mouse::enter', popup_enter)
-               widget:connect_signal('mouse::leave', popup_leave)
-               if icon then
-                       icon:connect_signal('mouse::enter', popup_enter)
-                       icon:connect_signal('mouse::leave', popup_leave)
-               end
+        widget:connect_signal('mouse::enter', popup_enter)
+        widget:connect_signal('mouse::leave', popup_leave)
+        if icon then
+            icon:connect_signal('mouse::enter', popup_enter)
+            icon:connect_signal('mouse::leave', popup_leave)
+        end
 
-               if imap_config[imap_index].command then
-                       local buttons = awful.button({}, 1, function()
-                                       awful.util.spawn(imap_config[imap_index].command, true)
-                       end)
-                       widget:buttons(buttons)
-                       if icon then
-                               icon:buttons(buttons)
-                       end
-               end
+        if imap_config[imap_index].command then
+            local buttons = awful.button({}, 1, function()
+                    awful.util.spawn(imap_config[imap_index].command, true)
+            end)
+            widget:buttons(buttons)
+            if icon then
+                icon:buttons(buttons)
+            end
+        end
 
-               widgets[imap_index] = widget
-               icons[imap_index]   = icon
+        widgets[imap_index] = widget
+        icons[imap_index]   = icon
 
-               vicious.register(widget, self, '$1', imap_config[imap_index].update_interval, imap_index)
-       end
-       return widgets, icons
+        vicious.register(widget, self, '$1', imap_config[imap_index].update_interval, imap_index)
+    end
+    return widgets, icons
 end
 
 -- Vicious worker function
 function vicious_worker(format, imap_index)
-       update_data(imap_index)
-       update_icon(imap_index)
-       show_notifications(imap_index)
-       local status
-       local error_status = '<span color="red">'
-       if icons[imap_index] then
-               error_status = string.format('%s ', error_status);
-       end
-       error_status = string.format('%s!</span>', error_status);
-       if not imap_data[imap_index] then
-               status = error_status
-               delightful.utils.print_error('imap', string.format('No imap_data[%d]', imap_index))
-       else
-               if imap_data[imap_index].error_string then
-                       status = '<span color="red"> !</span>';
-                       delightful.utils.print_error('imap', imap_data[imap_index].error_string)
-               elseif imap_data[imap_index].status_string then
-                       status = imap_data[imap_index].status_string
-               else
-                       imap_data[imap_index].error_string = string.format('No imap_data[%s][status_string] or imap_data[%s][error_string]', imap_index, imap_index)
-                       status = '<span color="red"> !</span>';
-                       delightful.utils.print_error('imap', imap_data[imap_index].error_string)
-               end
-       end
-       if imap_data[imap_index].mailboxes then
-               for _, mailbox in pairs(imap_data[imap_index].mailboxes) do
-                       if mailbox.error_string then
-                               delightful.utils.print_error('imap', mailbox.error_string)
-                       end
-               end
-       end
-       return status
+    update_data(imap_index)
+    update_icon(imap_index)
+    show_notifications(imap_index)
+    local status
+    local error_status = '<span color="red">'
+    if icons[imap_index] then
+        error_status = string.format('%s ', error_status);
+    end
+    error_status = string.format('%s!</span>', error_status);
+    if not imap_data[imap_index] then
+        status = error_status
+        delightful.utils.print_error('imap', string.format('No imap_data[%d]', imap_index))
+    else
+        if imap_data[imap_index].error_string then
+            status = '<span color="red"> !</span>';
+            delightful.utils.print_error('imap', imap_data[imap_index].error_string)
+        elseif imap_data[imap_index].status_string then
+            status = imap_data[imap_index].status_string
+        else
+            imap_data[imap_index].error_string = string.format('No imap_data[%s][status_string] or imap_data[%s][error_string]', imap_index, imap_index)
+            status = '<span color="red"> !</span>';
+            delightful.utils.print_error('imap', imap_data[imap_index].error_string)
+        end
+    end
+    if imap_data[imap_index].mailboxes then
+        for _, mailbox in pairs(imap_data[imap_index].mailboxes) do
+            if mailbox.error_string then
+                delightful.utils.print_error('imap', mailbox.error_string)
+            end
+        end
+    end
+    return status
 end
 
 -- Helpers
 
 function imap_url(data)
-       if type(data) == 'number' then
-               data = imap_config[data]
-       end
-       if not data then
-               return
-       end
-       if not data.host or not data.port or not data.ssl then
-               return
-       end
-       local url = 'imap'
-       if data.ssl then
-               url = string.format('%ss', url)
-       end
-       url = string.format('%s://%s', url, data.host)
-       if (data.ssl and data.port ~= 993) or
-                       (not data.ssl and data.port ~= 143) then
-               url = string.format('%s:%s', url, data.port);
-       end
-       return url
+    if type(data) == 'number' then
+        data = imap_config[data]
+    end
+    if not data then
+        return
+    end
+    if not data.host or not data.port or not data.ssl then
+        return
+    end
+    local url = 'imap'
+    if data.ssl then
+        url = string.format('%ss', url)
+    end
+    url = string.format('%s://%s', url, data.host)
+    if (data.ssl and data.port ~= 993) or
+            (not data.ssl and data.port ~= 143) then
+        url = string.format('%s:%s', url, data.port);
+    end
+    return url
 end
 
 function pad_message_detail(line)
-       return delightful.utils.pad_string_with_spaces(line, 48)
+    return delightful.utils.pad_string_with_spaces(line, 48)
 end
 
 setmetatable(_M, { __call = function(_, ...) return vicious_worker(...) end })
index 29cf930..1f04cf4 100644 (file)
@@ -69,13 +69,13 @@ module('delightful.widgets.memory')
 
 -- Helper
 function is_swap_available()
-       for line in io.lines('/proc/meminfo') do
-               local total_swap = tonumber(line:match('^SwapTotal:%s+(%d+)%s+kB4'))
-               if total_swap and total_swap > 0 then
-                       return true
-               end
-       end
-       return false
+    for line in io.lines('/proc/meminfo') do
+        local total_swap = tonumber(line:match('^SwapTotal:%s+(%d+)%s+kB4'))
+        if total_swap and total_swap > 0 then
+            return true
+        end
+    end
+    return false
 end
 
 local memory_config
@@ -84,153 +84,153 @@ local icon_tooltip
 local has_swap = is_swap_available()
 
 local config_description = {
-       {
-               name     = 'command',
-               validate = function(value) return delightful.utils.config_string(value) end
-       },
-       {
-               name     = 'no_icon',
-               validate = function(value) return delightful.utils.config_boolean(value) end
-       },
-       {
-               name     = 'progressbar_height',
-               required = true,
-               default  = 19,
-               validate = function(value) return delightful.utils.config_int(value) end
-       },
-       {
-               name     = 'progressbar_width',
-               required = true,
-               default  = 12,
-               validate = function(value) return delightful.utils.config_int(value) end
-       },
-       {
-               name     = 'update_interval',
-               required = true,
-               default  = 10,
-               validate = function(value) return delightful.utils.config_int(value) end
-       },
+    {
+        name     = 'command',
+        validate = function(value) return delightful.utils.config_string(value) end
+    },
+    {
+        name     = 'no_icon',
+        validate = function(value) return delightful.utils.config_boolean(value) end
+    },
+    {
+        name     = 'progressbar_height',
+        required = true,
+        default  = 19,
+        validate = function(value) return delightful.utils.config_int(value) end
+    },
+    {
+        name     = 'progressbar_width',
+        required = true,
+        default  = 12,
+        validate = function(value) return delightful.utils.config_int(value) end
+    },
+    {
+        name     = 'update_interval',
+        required = true,
+        default  = 10,
+        validate = function(value) return delightful.utils.config_int(value) end
+    },
 }
 
 local icon_description = {
-       memory = { beautiful_name = 'delightful_mem', default_icon = 'mate-sensors-applet-memory' },
+    memory = { beautiful_name = 'delightful_mem', default_icon = 'mate-sensors-applet-memory' },
 }
 
 -- Configuration handler
 function handle_config(user_config)
-       local empty_config = delightful.utils.get_empty_config(config_description)
-       if not user_config then
-               user_config = empty_config
-       end
-       local config_data = delightful.utils.normalize_config(user_config, config_description)
-       local validation_errors = delightful.utils.validate_config(config_data, config_description)
-       if validation_errors then
-               fatal_error = 'Configuration errors: \n'
-               for error_index, error_entry in pairs(validation_errors) do
-                       fatal_error = string.format('%s %s', fatal_error, error_entry)
-                       if error_index < #validation_errors then
-                               fatal_error = string.format('%s \n', fatal_error)
-                       end
-               end
-               memory_config = empty_config
-               return
-       end
-       memory_config = config_data
+    local empty_config = delightful.utils.get_empty_config(config_description)
+    if not user_config then
+        user_config = empty_config
+    end
+    local config_data = delightful.utils.normalize_config(user_config, config_description)
+    local validation_errors = delightful.utils.validate_config(config_data, config_description)
+    if validation_errors then
+        fatal_error = 'Configuration errors: \n'
+        for error_index, error_entry in pairs(validation_errors) do
+            fatal_error = string.format('%s %s', fatal_error, error_entry)
+            if error_index < #validation_errors then
+                fatal_error = string.format('%s \n', fatal_error)
+            end
+        end
+        memory_config = empty_config
+        return
+    end
+    memory_config = config_data
 end
 
 -- Initalization
 function load(self, config)
-       handle_config(config)
-       if fatal_error then
-               delightful.utils.print_error('memory', fatal_error)
-               return nil, nil
-       end
-       local icon
-       local icon_files
-       if not memory_config.no_icon then
-               icon_files = delightful.utils.find_icon_files(icon_description)
-       end
-       local icon_file = icon_files and icon_files.memory
-       if icon_file then
-               local buttons = awful.button({}, 1, function()
-                       if not fatal_error and memory_config.command then
-                               awful.util.spawn(memory_config.command, true)
-                       end
-               end)
-               icon = wibox.widget.imagebox()
-               icon:buttons(buttons)
-               icon:set_image(icon_file)
-               icon_tooltip = awful.tooltip({ objects = { icon } })
-       end
-       local icons = { icon }
-
-       local bg_color        = delightful.utils.find_theme_color({ 'bg_widget', 'bg_normal'                     })
-       local fg_color        = delightful.utils.find_theme_color({ 'fg_widget', 'fg_normal'                     })
-       local fg_center_color = delightful.utils.find_theme_color({ 'fg_center_widget', 'fg_widget', 'fg_normal' })
-       local fg_end_color    = delightful.utils.find_theme_color({ 'fg_end_widget', 'fg_widget', 'fg_normal'    })
-
-       local memory_widget = awful.widget.progressbar()
-       if bg_color then
-               memory_widget:set_background_color(bg_color)
-               memory_widget:set_border_color(bg_color)
-       end
-       local color_args = fg_color
-       local height = beautiful.progressbar_height or memory_config.progressbar_height
-       local width  = beautiful.progressbar_width  or memory_config.progressbar_width
-       if fg_color and fg_center_color and fg_end_color then
-               color_args = {
-                       type = 'linear',
-                       from = { 0, 0 },
-                       to = { width, height },
-                       stops = {{ 0, fg_end_color }, { 0.5, fg_center_color }, { 1, fg_color }},
-               }
-       end
-       memory_widget:set_color(color_args)
-       memory_widget:set_width(width)
-       memory_widget:set_height(height)
-       memory_widget:set_vertical(true)
-       vicious.register(memory_widget, vicious.widgets.mem, vicious_formatter_memory, memory_config.update_interval)
-       local widgets = { memory_widget }
-
-
-       if has_swap then
-               local swap_widget = awful.widget.progressbar()
-               if bg_color then
-                       swap_widget:set_background_color(bg_color)
-                       swap_widget:set_border_color(bg_color)
-               end
-               swap_widget:set_color(color_args)
-               swap_widget:set_width(width)
-               swap_widget:set_height(height)
-               swap_widget:set_vertical(true)
-               vicious.register(swap_widget, vicious.widgets.mem, vicious_formatter_swap, memory_config.update_interval)
-               table.insert(widgets, swap_widget)
-       end
-
-       vicious.cache(vicious.widgets.mem)
-
-       return widgets, icons
+    handle_config(config)
+    if fatal_error then
+        delightful.utils.print_error('memory', fatal_error)
+        return nil, nil
+    end
+    local icon
+    local icon_files
+    if not memory_config.no_icon then
+        icon_files = delightful.utils.find_icon_files(icon_description)
+    end
+    local icon_file = icon_files and icon_files.memory
+    if icon_file then
+        local buttons = awful.button({}, 1, function()
+            if not fatal_error and memory_config.command then
+                awful.util.spawn(memory_config.command, true)
+            end
+        end)
+        icon = wibox.widget.imagebox()
+        icon:buttons(buttons)
+        icon:set_image(icon_file)
+        icon_tooltip = awful.tooltip({ objects = { icon } })
+    end
+    local icons = { icon }
+
+    local bg_color        = delightful.utils.find_theme_color({ 'bg_widget', 'bg_normal'                     })
+    local fg_color        = delightful.utils.find_theme_color({ 'fg_widget', 'fg_normal'                     })
+    local fg_center_color = delightful.utils.find_theme_color({ 'fg_center_widget', 'fg_widget', 'fg_normal' })
+    local fg_end_color    = delightful.utils.find_theme_color({ 'fg_end_widget', 'fg_widget', 'fg_normal'    })
+
+    local memory_widget = awful.widget.progressbar()
+    if bg_color then
+        memory_widget:set_background_color(bg_color)
+        memory_widget:set_border_color(bg_color)
+    end
+    local color_args = fg_color
+    local height = beautiful.progressbar_height or memory_config.progressbar_height
+    local width  = beautiful.progressbar_width  or memory_config.progressbar_width
+    if fg_color and fg_center_color and fg_end_color then
+        color_args = {
+            type = 'linear',
+            from = { 0, 0 },
+            to = { width, height },
+            stops = {{ 0, fg_end_color }, { 0.5, fg_center_color }, { 1, fg_color }},
+        }
+    end
+    memory_widget:set_color(color_args)
+    memory_widget:set_width(width)
+    memory_widget:set_height(height)
+    memory_widget:set_vertical(true)
+    vicious.register(memory_widget, vicious.widgets.mem, vicious_formatter_memory, memory_config.update_interval)
+    local widgets = { memory_widget }
+
+
+    if has_swap then
+        local swap_widget = awful.widget.progressbar()
+        if bg_color then
+            swap_widget:set_background_color(bg_color)
+            swap_widget:set_border_color(bg_color)
+        end
+        swap_widget:set_color(color_args)
+        swap_widget:set_width(width)
+        swap_widget:set_height(height)
+        swap_widget:set_vertical(true)
+        vicious.register(swap_widget, vicious.widgets.mem, vicious_formatter_swap, memory_config.update_interval)
+        table.insert(widgets, swap_widget)
+    end
+
+    vicious.cache(vicious.widgets.mem)
+
+    return widgets, icons
 end
 
 -- Vicious display formatter for memory graph, also update widget tooltip
 function vicious_formatter_memory(widget, data)
-       local memory_percentage = math.floor(((data[2] / data[3]) * 100) + 0.5)
-       if icon_tooltip then
-               local swap_string = ''
-               if has_swap then
-                       swap_string = 'and swap '
-               end
-               local tooltip_text = string.format(' Memory %susage statistics \n Current memory usage: %d%% (%dMB out of %dMB) ', swap_string, memory_percentage, data[2], data[3])
-               if has_swap then
-                       local swap_percentage = math.floor(((data[6] / data[7]) * 100) + 0.5)
-                       tooltip_text = string.format('%s\n Current swap usage: %d%% (%dMB out of %dMB) ', tooltip_text, swap_percentage, data[6], data[7])
-               end
-               icon_tooltip:set_text(tooltip_text)
-       end
-       return memory_percentage
+    local memory_percentage = math.floor(((data[2] / data[3]) * 100) + 0.5)
+    if icon_tooltip then
+        local swap_string = ''
+        if has_swap then
+            swap_string = 'and swap '
+        end
+        local tooltip_text = string.format(' Memory %susage statistics \n Current memory usage: %d%% (%dMB out of %dMB) ', swap_string, memory_percentage, data[2], data[3])
+        if has_swap then
+            local swap_percentage = math.floor(((data[6] / data[7]) * 100) + 0.5)
+            tooltip_text = string.format('%s\n Current swap usage: %d%% (%dMB out of %dMB) ', tooltip_text, swap_percentage, data[6], data[7])
+        end
+        icon_tooltip:set_text(tooltip_text)
+    end
+    return memory_percentage
 end
 
 -- Vicious display formatter for swap graph
 function vicious_formatter_swap(widget, data)
-       return (data[6] / data[7]) * 100
+    return (data[6] / data[7]) * 100
 end
index fc2cac0..348710f 100644 (file)
@@ -30,7 +30,7 @@
 -- -- is shown. Use this setting to exclude some local devices that
 -- -- you don't want to monitor and is not ingored by default by
 -- -- the next configure option. Empty list by default.
---           excluded_devices         = { '^somedev$' },
+--        excluded_devices         = { '^somedev$' },
 -- -- Most you likely don't want to change this option. It defines which
 -- -- network devices to exclude from monitoring by default. It uses
 -- -- the same format as previous option (a list of Lua patterns).
 --        dialup_devices           = { '^mydialupdev%d+$' },
 -- -- Command to execute when left-clicking the widget icon.
 -- -- Empty by default.
---           command                  = 'gnome-nettool',
+--        command                  = 'gnome-nettool',
 -- -- Don't try to display any icons. Default is false (i.e. display icons).
 --        no_icon                  = true,
 -- -- How often update the widget data. Default is 3 seconds.
---           update_interval          = 10
+--        update_interval          = 10
 -- }
 --
 --
@@ -91,176 +91,176 @@ local fatal_error
 local icon_files = {}
 
 local config_description = {
-       {
-               name     = 'excluded_devices',
-               require  = true,
-               default  = {},
-               coerce   = function(value) return delightful.utils.coerce_table(value) end,
-               validate = function(value) return delightful.utils.config_table(value) end
-       },
-       {
-               name     = 'excluded_devices_default',
-               required = true,
-               default  = { '^lo$', 'dummy%d*$', '^tun%d*$', '^tap%d*$', '^pan%d+$', '^br.+$', '^vm%d+$', '^vnet%d+$' },
-               coerce   = function(value) return delightful.utils.coerce_table(value) end,
-               validate = function(value) return delightful.utils.config_table(value) end
-       },
-       {
-               name     = 'wireless_devices',
-               required = true,
-               default  = { '^wlan%d*', '^wifi%d*', '^ath%d+$', '^ra%d+$' },
-               coerce   = function(value) return delightful.utils.coerce_table(value) end,
-               validate = function(value) return delightful.utils.config_table(value) end
-       },
-       {
-               name     = 'dialup_devices',
-               required = true,
-               default  = { '^ppp%d+' },
-               coerce   = function(value) return delightful.utils.coerce_table(value) end,
-               validate = function(value) return delightful.utils.config_table(value) end
-       },
-       {
-               name     = 'command',
-               validate = function(value) return delightful.utils.config_string(value) end
-       },
-       {
-               name     = 'no_icon',
-               validate = function(value) return delightful.utils.config_boolean(value) end
-       },
-       {
-               name     = 'update_interval',
-               required = true,
-               default  = 3,
-               validate = function(value) return delightful.utils.config_int(value) end
-       },
+    {
+        name     = 'excluded_devices',
+        require  = true,
+        default  = {},
+        coerce   = function(value) return delightful.utils.coerce_table(value) end,
+        validate = function(value) return delightful.utils.config_table(value) end
+    },
+    {
+        name     = 'excluded_devices_default',
+        required = true,
+        default  = { '^lo$', 'dummy%d*$', '^tun%d*$', '^tap%d*$', '^pan%d+$', '^br.+$', '^vm%d+$', '^vnet%d+$' },
+        coerce   = function(value) return delightful.utils.coerce_table(value) end,
+        validate = function(value) return delightful.utils.config_table(value) end
+    },
+    {
+        name     = 'wireless_devices',
+        required = true,
+        default  = { '^wlan%d*', '^wifi%d*', '^ath%d+$', '^ra%d+$' },
+        coerce   = function(value) return delightful.utils.coerce_table(value) end,
+        validate = function(value) return delightful.utils.config_table(value) end
+    },
+    {
+        name     = 'dialup_devices',
+        required = true,
+        default  = { '^ppp%d+' },
+        coerce   = function(value) return delightful.utils.coerce_table(value) end,
+        validate = function(value) return delightful.utils.config_table(value) end
+    },
+    {
+        name     = 'command',
+        validate = function(value) return delightful.utils.config_string(value) end
+    },
+    {
+        name     = 'no_icon',
+        validate = function(value) return delightful.utils.config_boolean(value) end
+    },
+    {
+        name     = 'update_interval',
+        required = true,
+        default  = 3,
+        validate = function(value) return delightful.utils.config_int(value) end
+    },
 }
 
 local icon_description = {
-       wired    = { beautiful_name = 'delightful_network_wired',    default_icon = 'network-wired'    },
-       wireless = { beautiful_name = 'delightful_network_wireless', default_icon = 'network-wireless' },
-       dialup   = { beautiful_name = 'delightful_network_dialup',   default_icon = 'modem'            },
+    wired    = { beautiful_name = 'delightful_network_wired',    default_icon = 'network-wired'    },
+    wireless = { beautiful_name = 'delightful_network_wireless', default_icon = 'network-wireless' },
+    dialup   = { beautiful_name = 'delightful_network_dialup',   default_icon = 'modem'            },
 }
 
 -- Configuration handler
 function handle_config(user_config)
-       local empty_config = delightful.utils.get_empty_config(config_description)
-       if not user_config then
-               user_config = empty_config
-       end
-       local config_data = delightful.utils.normalize_config(user_config, config_description)
-       local validation_errors = delightful.utils.validate_config(config_data, config_description)
-       if validation_errors then
-               fatal_error = 'Configuration errors: \n'
-               for error_index, error_entry in pairs(validation_errors) do
-                       fatal_error = string.format('%s %s', fatal_error, error_entry)
-                       if error_index < #validation_errors then
-                               fatal_error = string.format('%s \n', fatal_error)
-                       end
-               end
-               network_config = empty_config
-               return
-       end
-       network_config = config_data
+    local empty_config = delightful.utils.get_empty_config(config_description)
+    if not user_config then
+        user_config = empty_config
+    end
+    local config_data = delightful.utils.normalize_config(user_config, config_description)
+    local validation_errors = delightful.utils.validate_config(config_data, config_description)
+    if validation_errors then
+        fatal_error = 'Configuration errors: \n'
+        for error_index, error_entry in pairs(validation_errors) do
+            fatal_error = string.format('%s %s', fatal_error, error_entry)
+            if error_index < #validation_errors then
+                fatal_error = string.format('%s \n', fatal_error)
+            end
+        end
+        network_config = empty_config
+        return
+    end
+    network_config = config_data
 end
 
 -- Initalization
 function load(self, config)
-       handle_config(config)
-       if fatal_error then
-               delightful.utils.print_error('net', fatal_error)
-               return nil, nil
-       end
-       if not network_config.no_icon then
-               icon_files = delightful.utils.find_icon_files(icon_description)
-       end
-       local devices = {}
-       for line in io.lines('/proc/net/dev') do
-               local device = line:match('^[%s]?[%s]?[%s]?[%s]?([%w]+):')
-               if device then
-                       local exclude = false
-                       for _, excluded_device in pairs(awful.util.table.join(network_config.excluded_devices, network_config.excluded_devices_default)) do
-                               if device:find(excluded_device) then
-                                       exclude = true
-                                       break
-                               end
-                       end
-                       if not exclude then
-                               table.insert(devices, device)
-                       end
-               end
-       end
+    handle_config(config)
+    if fatal_error then
+        delightful.utils.print_error('net', fatal_error)
+        return nil, nil
+    end
+    if not network_config.no_icon then
+        icon_files = delightful.utils.find_icon_files(icon_description)
+    end
+    local devices = {}
+    for line in io.lines('/proc/net/dev') do
+        local device = line:match('^[%s]?[%s]?[%s]?[%s]?([%w]+):')
+        if device then
+            local exclude = false
+            for _, excluded_device in pairs(awful.util.table.join(network_config.excluded_devices, network_config.excluded_devices_default)) do
+                if device:find(excluded_device) then
+                    exclude = true
+                    break
+                end
+            end
+            if not exclude then
+                table.insert(devices, device)
+            end
+        end
+    end
 
-       local color_download = delightful.utils.find_theme_color({ 'fg_end_widget' })
-       local color_upload   = delightful.utils.find_theme_color({ 'fg_widget'     })
+    local color_download = delightful.utils.find_theme_color({ 'fg_end_widget' })
+    local color_upload   = delightful.utils.find_theme_color({ 'fg_widget'     })
 
-       local widgets
-       local icons
-       for _, device in pairs(devices) do
-               local icon
-               local icon_file
-               for _, wireless_device in pairs(network_config.wireless_devices) do
-                       if device:find(wireless_device) then
-                               icon_file = icon_files.wireless
-                       end
-               end
-               if not icon_file then
-                       for _, dialup_device in pairs(network_config.dialup_devices) do
-                               if device:find(dialup_device) then
-                                       icon_file = icon_files.dialup
-                               end
-                       end
-               end
-               if not icon_file then
-                       icon_file = icon_files.wired
-               end
+    local widgets
+    local icons
+    for _, device in pairs(devices) do
+        local icon
+        local icon_file
+        for _, wireless_device in pairs(network_config.wireless_devices) do
+            if device:find(wireless_device) then
+                icon_file = icon_files.wireless
+            end
+        end
+        if not icon_file then
+            for _, dialup_device in pairs(network_config.dialup_devices) do
+                if device:find(dialup_device) then
+                    icon_file = icon_files.dialup
+                end
+            end
+        end
+        if not icon_file then
+            icon_file = icon_files.wired
+        end
 
-               if icon_file then
-                       local buttons = awful.button({}, 1, function()
-                               if not fatal_error and network_config.command then
-                                       awful.util.spawn(network_config.command, true)
-                               end
-                       end)
-                       icon = wibox.widget.imagebox()
-                       icon:buttons(buttons)
-                       icon:set_image(icon_file)
-                       local tooltip = awful.tooltip({ objects = { icon } })
-                       tooltip:set_text(' Download and upload speed \n of the network device ' .. device .. ' \n in kilobytes per second ')
-                       if not icons then
-                               icons = {}
-                       end
-                       table.insert(icons, icon)
-               end
+        if icon_file then
+            local buttons = awful.button({}, 1, function()
+                if not fatal_error and network_config.command then
+                    awful.util.spawn(network_config.command, true)
+                end
+            end)
+            icon = wibox.widget.imagebox()
+            icon:buttons(buttons)
+            icon:set_image(icon_file)
+            local tooltip = awful.tooltip({ objects = { icon } })
+            tooltip:set_text(' Download and upload speed \n of the network device ' .. device .. ' \n in kilobytes per second ')
+            if not icons then
+                icons = {}
+            end
+            table.insert(icons, icon)
+        end
 
-               local net_widget = wibox.widget.textbox()
-               local widget_text = '↓'
-               local close_span = false
-               if color_download then
-                       widget_text = string.format('%s<span color="%s">', widget_text, color_download)
-                       close_span = true
-               end
-               widget_text = string.format('%s${%s down_kb}', widget_text, device)
-               if close_span then
-                       widget_text = string.format('%s%s', widget_text, '</span>')
-                       close_span = false
-               end
-               widget_text = string.format('%s%s', widget_text, ' ↑')
-               if color_upload then
-                       widget_text = string.format('%s<span color="%s">', widget_text, color_upload)
-                       close_span = true
-               end
-               widget_text = string.format('%s${%s up_kb}', widget_text, device)
-               if close_span then
-                       widget_text = string.format('%s%s', widget_text, '</span>')
-                       close_span = false
-               end
-               vicious.register(net_widget, vicious.widgets.net, widget_text, network_config.update_interval)
-               if not widgets then
-                       widgets = {}
-               end
-               table.insert(widgets, net_widget)
-       end
+        local net_widget = wibox.widget.textbox()
+        local widget_text = '↓'
+        local close_span = false
+        if color_download then
+            widget_text = string.format('%s<span color="%s">', widget_text, color_download)
+            close_span = true
+        end
+        widget_text = string.format('%s${%s down_kb}', widget_text, device)
+        if close_span then
+            widget_text = string.format('%s%s', widget_text, '</span>')
+            close_span = false
+        end
+        widget_text = string.format('%s%s', widget_text, ' ↑')
+        if color_upload then
+            widget_text = string.format('%s<span color="%s">', widget_text, color_upload)
+            close_span = true
+        end
+        widget_text = string.format('%s${%s up_kb}', widget_text, device)
+        if close_span then
+            widget_text = string.format('%s%s', widget_text, '</span>')
+            close_span = false
+        end
+        vicious.register(net_widget, vicious.widgets.net, widget_text, network_config.update_interval)
+        if not widgets then
+            widgets = {}
+        end
+        table.insert(widgets, net_widget)
+    end
 
-       vicious.cache(vicious.widgets.net)
+    vicious.cache(vicious.widgets.net)
 
-       return widgets, icons
+    return widgets, icons
 end
index b9eb977..9eb2b06 100644 (file)
@@ -112,418 +112,418 @@ local pacmd_timestamp
 local pacmd_force_update = false
 
 local config_description = {
-       {
-               name     = 'sink_nums',
-               coerce   = function(value) return delightful.utils.coerce_table(value) end,
-               validate = function(value) return delightful.utils.config_table(value) end
-       },
-       {
-               name     = 'pulseaudio_start',
-               required = true,
-               default  = true,
-               validate = function(value) return delightful.utils.config_boolean(value) end
-       },
-       {
-               name     = 'pulseaudio_command',
-               required = true,
-               default  = 'pulseaudio',
-               validate = function(value) return delightful.utils.config_string(value) end
-       },
-       {
-               name     = 'pacmd_command',
-               required = true,
-               default  = 'pacmd',
-               validate = function(value) return delightful.utils.config_string(value) end
-       },
-       {
-               name     = 'mixer_command',
-               default  = function(config_data) if mixer_cmd then return mixer_cmd end end,
-               validate = function(value) return delightful.utils.config_string(value) end
-       },
-       {
-               name     = 'no_icon',
-               validate = function(value) return delightful.utils.config_boolean(value) end
-       },
-       {
-               name     = 'progressbar_height',
-               required = true,
-               default  = 19,
-               validate = function(value) return delightful.utils.config_int(value) end
-       },
-       {
-               name     = 'progressbar_width',
-               required = true,
-               default  = 12,
-               validate = function(value) return delightful.utils.config_int(value) end
-       },
-       {
-               name     = 'update_interval',
-               required = true,
-               default  = 10,
-               validate = function(value) return delightful.utils.config_int(value) end
-       },
+    {
+        name     = 'sink_nums',
+        coerce   = function(value) return delightful.utils.coerce_table(value) end,
+        validate = function(value) return delightful.utils.config_table(value) end
+    },
+    {
+        name     = 'pulseaudio_start',
+        required = true,
+        default  = true,
+        validate = function(value) return delightful.utils.config_boolean(value) end
+    },
+    {
+        name     = 'pulseaudio_command',
+        required = true,
+        default  = 'pulseaudio',
+        validate = function(value) return delightful.utils.config_string(value) end
+    },
+    {
+        name     = 'pacmd_command',
+        required = true,
+        default  = 'pacmd',
+        validate = function(value) return delightful.utils.config_string(value) end
+    },
+    {
+        name     = 'mixer_command',
+        default  = function(config_data) if mixer_cmd then return mixer_cmd end end,
+        validate = function(value) return delightful.utils.config_string(value) end
+    },
+    {
+        name     = 'no_icon',
+        validate = function(value) return delightful.utils.config_boolean(value) end
+    },
+    {
+        name     = 'progressbar_height',
+        required = true,
+        default  = 19,
+        validate = function(value) return delightful.utils.config_int(value) end
+    },
+    {
+        name     = 'progressbar_width',
+        required = true,
+        default  = 12,
+        validate = function(value) return delightful.utils.config_int(value) end
+    },
+    {
+        name     = 'update_interval',
+        required = true,
+        default  = 10,
+        validate = function(value) return delightful.utils.config_int(value) end
+    },
 }
 
 local icon_description = {
-       vol   = { beautiful_name = 'delightful_vol',      default_icon = 'multimedia-volume-control' },
-       max   = { beautiful_name = 'delightful_vol_max',  default_icon = 'audio-volume-high'         },
-       med   = { beautiful_name = 'delightful_vol_med',  default_icon = 'audio-volume-medium'       },
-       min   = { beautiful_name = 'delightful_vol_min',  default_icon = 'audio-volume-low'          },
-       zero  = { beautiful_name = 'delightful_vol_zero', default_icon = 'audio-volume-low',         },
-       mute  = { beautiful_name = 'delightful_vol_mute', default_icon = 'audio-volume-muted'        },
-       error = { beautiful_name = 'delightful_error',    default_icon = 'dialog-error'              },
+    vol   = { beautiful_name = 'delightful_vol',      default_icon = 'multimedia-volume-control' },
+    max   = { beautiful_name = 'delightful_vol_max',  default_icon = 'audio-volume-high'         },
+    med   = { beautiful_name = 'delightful_vol_med',  default_icon = 'audio-volume-medium'       },
+    min   = { beautiful_name = 'delightful_vol_min',  default_icon = 'audio-volume-low'          },
+    zero  = { beautiful_name = 'delightful_vol_zero', default_icon = 'audio-volume-low',         },
+    mute  = { beautiful_name = 'delightful_vol_mute', default_icon = 'audio-volume-muted'        },
+    error = { beautiful_name = 'delightful_error',    default_icon = 'dialog-error'              },
 }
 
 -- Read sink info
 function update_data(force_update)
-       update_sink_string(force_update)
-       sink_data = {}
-       for i = 1, number_of_sinks do
-               sink_data[i] = {}
-       end
-       if not pacmd_string or fatal_error then
-               return
-       end
-       local sink_id = 0
-       local sink_num_ok
-       -- iterate all lines in "pacmd list-sinks" output
-       pacmd_string:gsub('(.-)\n', function(line)
-                       -- parse sink id
-                       line:gsub('^[%s\*]+index:%s(%d)$', function(match)
-                                       sink_num_ok = false
-                                       local sink_num = tonumber(match)
-                                       for accepted_sink_id, accepted_sink_num in pairs(pulseaudio_config.sink_nums) do
-                                               sink_num_ok = sink_num == accepted_sink_num
-                                               if sink_num_ok then
-                                                       sink_id = accepted_sink_id
-                                                       sink_data[sink_id].num = sink_num
-                                                       break
-                                               end
-                                       end
-                       end)
-                       -- parse mute status
-                       line:gsub('^%s+muted:%s(.+)$', function(match)
-                                       if not sink_num_ok then
-                                               return
-                                       end
-                                       sink_data[sink_id].muted = match == 'yes'
-                       end)
-                       -- parse volume
-                       line:gsub('^%s+volume:[%s%w-:/]+%s(%d+)%%', function(match)
-                                       if not sink_num_ok then
-                                               return
-                                       end
-                                       sink_data[sink_id].volperc = tonumber(match)
-                                       sink_data[sink_id].volnum  = math.floor(((maxvol / 100) * sink_data[sink_id].volperc) + 0.5)
-                       end)
-                       -- parse device name
-                       line:gsub('^%s+device\.description%s+=%s+[\'"]([^\'"]+)[\'"]$', function(match)
-                                       if not sink_num_ok then
-                                               return
-                                       end
-                                       sink_data[sink_id].name = match
-                       end)
-       end)
-       -- ensure all required info was found
-       for found_sink_id, found_sink_data in pairs(sink_data) do
-               if not (found_sink_data.name and found_sink_data.muted ~= nil and found_sink_data.volperc) then
-                       sink_data[found_sink_id] = {
-                               error_string = 'Failed to get required info about PulseAudio sink'
-                       }
-               end
-       end
+    update_sink_string(force_update)
+    sink_data = {}
+    for i = 1, number_of_sinks do
+        sink_data[i] = {}
+    end
+    if not pacmd_string or fatal_error then
+        return
+    end
+    local sink_id = 0
+    local sink_num_ok
+    -- iterate all lines in "pacmd list-sinks" output
+    pacmd_string:gsub('(.-)\n', function(line)
+            -- parse sink id
+            line:gsub('^[%s\*]+index:%s(%d)$', function(match)
+                    sink_num_ok = false
+                    local sink_num = tonumber(match)
+                    for accepted_sink_id, accepted_sink_num in pairs(pulseaudio_config.sink_nums) do
+                        sink_num_ok = sink_num == accepted_sink_num
+                        if sink_num_ok then
+                            sink_id = accepted_sink_id
+                            sink_data[sink_id].num = sink_num
+                            break
+                        end
+                    end
+            end)
+            -- parse mute status
+            line:gsub('^%s+muted:%s(.+)$', function(match)
+                    if not sink_num_ok then
+                        return
+                    end
+                    sink_data[sink_id].muted = match == 'yes'
+            end)
+            -- parse volume
+            line:gsub('^%s+volume:[%s%w-:/]+%s(%d+)%%', function(match)
+                    if not sink_num_ok then
+                        return
+                    end
+                    sink_data[sink_id].volperc = tonumber(match)
+                    sink_data[sink_id].volnum  = math.floor(((maxvol / 100) * sink_data[sink_id].volperc) + 0.5)
+            end)
+            -- parse device name
+            line:gsub('^%s+device\.description%s+=%s+[\'"]([^\'"]+)[\'"]$', function(match)
+                    if not sink_num_ok then
+                        return
+                    end
+                    sink_data[sink_id].name = match
+            end)
+    end)
+    -- ensure all required info was found
+    for found_sink_id, found_sink_data in pairs(sink_data) do
+        if not (found_sink_data.name and found_sink_data.muted ~= nil and found_sink_data.volperc) then
+            sink_data[found_sink_id] = {
+                error_string = 'Failed to get required info about PulseAudio sink'
+            }
+        end
+    end
 end
 
 -- Update widget icon based on the volume
 function update_icon(sink_id)
-       if not icons[sink_id] or not icon_files.error then
-               return
-       end
-       local icon_file
-       if (fatal_error or (sink_data[sink_id] and sink_data[sink_id].error_string)) and icon_files.error then
-               icon_file = icon_files.error
-       elseif sink_data[sink_id] and icon_files.vol then
-               icon_file = icon_files.vol
-               if sink_data[sink_id].muted then
-                       icon_file = icon_files.mute
-               elseif sink_data[sink_id].volperc then
-                       if     sink_data[sink_id].volperc > 100 * 0.7 then
-                               icon_file = icon_files.max
-                       elseif sink_data[sink_id].volperc > 100 * 0.3 then
-                               icon_file = icon_files.med
-                       elseif sink_data[sink_id].volperc > 0 then
-                               icon_file = icon_files.min
-                       elseif sink_data[sink_id].volperc == 0 then
-                               icon_file = icon_files.zero
-                       end
-               end
-       end
-       if icon_file and (not prev_icons[sink_id] or prev_icons[sink_id] ~= icon_file) then
-               prev_icons[sink_id] = icon_file
-               icons[sink_id]:set_image(icon_file)
-       end
+    if not icons[sink_id] or not icon_files.error then
+        return
+    end
+    local icon_file
+    if (fatal_error or (sink_data[sink_id] and sink_data[sink_id].error_string)) and icon_files.error then
+        icon_file = icon_files.error
+    elseif sink_data[sink_id] and icon_files.vol then
+        icon_file = icon_files.vol
+        if sink_data[sink_id].muted then
+            icon_file = icon_files.mute
+        elseif sink_data[sink_id].volperc then
+            if     sink_data[sink_id].volperc > 100 * 0.7 then
+                icon_file = icon_files.max
+            elseif sink_data[sink_id].volperc > 100 * 0.3 then
+                icon_file = icon_files.med
+            elseif sink_data[sink_id].volperc > 0 then
+                icon_file = icon_files.min
+            elseif sink_data[sink_id].volperc == 0 then
+                icon_file = icon_files.zero
+            end
+        end
+    end
+    if icon_file and (not prev_icons[sink_id] or prev_icons[sink_id] ~= icon_file) then
+        prev_icons[sink_id] = icon_file
+        icons[sink_id]:set_image(icon_file)
+    end
 end
 
 -- Update the mixer tooltip text
 function update_tooltip(sink_id)
-       if not tooltips[sink_id] or not sink_data[sink_id]then
-               return
-       end
-       local text
-       if fatal_error then
-               text = string.format(' %s ', fatal_error)
-       elseif sink_data[sink_id].error_string then
-               text = string.format(' %s ', sink_data[sink_id].error_string)
-       else
-               local volume_text = 'Unknown'
-               if sink_data[sink_id].muted then
-                       volume_text = 'muted'
-               elseif sink_data[sink_id].volperc then
-                       volume_text = string.format('%d%%', sink_data[sink_id].volperc)
-               end
-               text = string.format(' Audio device %s \n Volume level: %s \n Volume controls: \n Left mouse button: toggle mute on and off \n Right mouse button: launch mixer \n Scrollwheel up and down: rise and lower volume ', sink_data[sink_id].name, volume_text)
+    if not tooltips[sink_id] or not sink_data[sink_id]then
+        return
+    end
+    local text
+    if fatal_error then
+        text = string.format(' %s ', fatal_error)
+    elseif sink_data[sink_id].error_string then
+        text = string.format(' %s ', sink_data[sink_id].error_string)
+    else
+        local volume_text = 'Unknown'
+        if sink_data[sink_id].muted then
+            volume_text = 'muted'
+        elseif sink_data[sink_id].volperc then
+            volume_text = string.format('%d%%', sink_data[sink_id].volperc)
+        end
+        text = string.format(' Audio device %s \n Volume level: %s \n Volume controls: \n Left mouse button: toggle mute on and off \n Right mouse button: launch mixer \n Scrollwheel up and down: rise and lower volume ', sink_data[sink_id].name, volume_text)
 
-       end
-       tooltips[sink_id]:set_text(text)
+    end
+    tooltips[sink_id]:set_text(text)
 end
 
 -- Configuration handler
 function handle_config(user_config)
-       local empty_config = delightful.utils.get_empty_config(config_description)
-       if not user_config then
-               user_config = empty_config
-       end
-       local config_data = delightful.utils.normalize_config(user_config, config_description)
-       local validation_errors = delightful.utils.validate_config(config_data, config_description)
-       if validation_errors then
-               fatal_error = 'Configuration errors: \n'
-               for error_index, error_entry in pairs(validation_errors) do
-                       fatal_error = string.format('%s %s', fatal_error, error_entry)
-                       if error_index < #validation_errors then
-                               fatal_error = string.format('%s \n', fatal_error)
-                       end
-               end
-               retry_fatal_error = false
-               pulseaudio_config = empty_config
-               return
-       end
-       pulseaudio_config = config_data
+    local empty_config = delightful.utils.get_empty_config(config_description)
+    if not user_config then
+        user_config = empty_config
+    end
+    local config_data = delightful.utils.normalize_config(user_config, config_description)
+    local validation_errors = delightful.utils.validate_config(config_data, config_description)
+    if validation_errors then
+        fatal_error = 'Configuration errors: \n'
+        for error_index, error_entry in pairs(validation_errors) do
+            fatal_error = string.format('%s %s', fatal_error, error_entry)
+            if error_index < #validation_errors then
+                fatal_error = string.format('%s \n', fatal_error)
+            end
+        end
+        retry_fatal_error = false
+        pulseaudio_config = empty_config
+        return
+    end
+    pulseaudio_config = config_data
 end
 
 -- Initalization
 function load(self, config)
-       handle_config(config)
-       if not pulseaudio_config.no_icon then
-               icon_files = delightful.utils.find_icon_files(icon_description)
-       end
-       update_sink_string()
-       update_number_of_sinks()
-       if not pulseaudio_config.sink_nums then
-               pulseaudio_config.sink_nums = {}
-               for sink_id = 1, number_of_sinks do
-                       table.insert(pulseaudio_config.sink_nums, sink_id - 1)
-               end
-       end
+    handle_config(config)
+    if not pulseaudio_config.no_icon then
+        icon_files = delightful.utils.find_icon_files(icon_description)
+    end
+    update_sink_string()
+    update_number_of_sinks()
+    if not pulseaudio_config.sink_nums then
+        pulseaudio_config.sink_nums = {}
+        for sink_id = 1, number_of_sinks do
+            table.insert(pulseaudio_config.sink_nums, sink_id - 1)
+        end
+    end
 
-       local bg_color        = delightful.utils.find_theme_color({ 'bg_widget', 'bg_normal'                     })
-       local fg_color        = delightful.utils.find_theme_color({ 'fg_widget', 'fg_normal'                     })
-       local fg_center_color = delightful.utils.find_theme_color({ 'fg_center_widget', 'fg_widget', 'fg_normal' })
-       local fg_end_color    = delightful.utils.find_theme_color({ 'fg_end_widget', 'fg_widget', 'fg_normal'    })
+    local bg_color        = delightful.utils.find_theme_color({ 'bg_widget', 'bg_normal'                     })
+    local fg_color        = delightful.utils.find_theme_color({ 'fg_widget', 'fg_normal'                     })
+    local fg_center_color = delightful.utils.find_theme_color({ 'fg_center_widget', 'fg_widget', 'fg_normal' })
+    local fg_end_color    = delightful.utils.find_theme_color({ 'fg_end_widget', 'fg_widget', 'fg_normal'    })
 
-       for sink_id = 1, number_of_sinks do
-               if icon_files.vol and icon_files.error then
-                       local buttons = awful.util.table.join(
-                                       awful.button({}, 1, function()
-                                                       if sink_data[sink_id] and not fatal_error and not sink_data[sink_id].error_string then
-                                                               pulseaudio_control('toggle', sink_id)
-                                                       end
-                                       end),
-                                       awful.button({}, 3, function()
-                                                       if sink_data[sink_id] and not fatal_error and not sink_data[sink_id].error_string then
-                                                               if pulseaudio_config.mixer_command then
-                                                                       awful.util.spawn(pulseaudio_config.mixer_command, true)
-                                                               end
-                                                       end
-                                       end),
-                                       awful.button({}, 4, function()
-                                                       if sink_data[sink_id] and not fatal_error and not sink_data[sink_id].error_string then
-                                                               pulseaudio_control('up', sink_id)
-                                                       end
-                                       end),
-                                       awful.button({}, 5, function()
-                                                       if sink_data[sink_id] and not fatal_error and not sink_data[sink_id].error_string then
-                                                               pulseaudio_control('down', sink_id)
-                                                       end
-                                       end)
-                       )
-                       icons[sink_id] = wibox.widget.imagebox()
-                       icons[sink_id]:buttons(buttons)
-                       tooltips[sink_id] = awful.tooltip( { objects = { icons[sink_id] } })
-                       update_icon(sink_id)
-                       update_tooltip(sink_id)
-               end
-               local widget = awful.widget.progressbar()
-               if bg_color then
-                       widget:set_border_color(bg_color)
-                       widget:set_background_color(bg_color)
-               end
-               local color_args = fg_color
-               local height = beautiful.progressbar_height or pulseaudio_config.progressbar_height
-               local width  = beautiful.progressbar_width  or pulseaudio_config.progressbar_width
-               if fg_color and fg_center_color and fg_end_color then
-                       color_args = {
-                               type = 'linear',
-                               from = { 0, 0 },
-                               to = { width, height },
-                               stops = {{ 0, fg_end_color }, { 0.5, fg_center_color }, { 1, fg_color }},
-                       }
-               end
-               widget:set_color(color_args)
-               widget:set_width(width)
-               widget:set_height(height)
-               widget:set_vertical(true)
-               widgets[sink_id] = widget
-               vicious.register(widget, self, '$1', pulseaudio_config.update_interval, sink_id)
-       end
-       return widgets, icons
+    for sink_id = 1, number_of_sinks do
+        if icon_files.vol and icon_files.error then
+            local buttons = awful.util.table.join(
+                    awful.button({}, 1, function()
+                            if sink_data[sink_id] and not fatal_error and not sink_data[sink_id].error_string then
+                                pulseaudio_control('toggle', sink_id)
+                            end
+                    end),
+                    awful.button({}, 3, function()
+                            if sink_data[sink_id] and not fatal_error and not sink_data[sink_id].error_string then
+                                if pulseaudio_config.mixer_command then
+                                    awful.util.spawn(pulseaudio_config.mixer_command, true)
+                                end
+                            end
+                    end),
+                    awful.button({}, 4, function()
+                            if sink_data[sink_id] and not fatal_error and not sink_data[sink_id].error_string then
+                                pulseaudio_control('up', sink_id)
+                            end
+                    end),
+                    awful.button({}, 5, function()
+                            if sink_data[sink_id] and not fatal_error and not sink_data[sink_id].error_string then
+                                pulseaudio_control('down', sink_id)
+                            end
+                    end)
+            )
+            icons[sink_id] = wibox.widget.imagebox()
+            icons[sink_id]:buttons(buttons)
+            tooltips[sink_id] = awful.tooltip( { objects = { icons[sink_id] } })
+            update_icon(sink_id)
+            update_tooltip(sink_id)
+        end
+        local widget = awful.widget.progressbar()
+        if bg_color then
+            widget:set_border_color(bg_color)
+            widget:set_background_color(bg_color)
+        end
+        local color_args = fg_color
+        local height = beautiful.progressbar_height or pulseaudio_config.progressbar_height
+        local width  = beautiful.progressbar_width  or pulseaudio_config.progressbar_width
+        if fg_color and fg_center_color and fg_end_color then
+            color_args = {
+                type = 'linear',
+                from = { 0, 0 },
+                to = { width, height },
+                stops = {{ 0, fg_end_color }, { 0.5, fg_center_color }, { 1, fg_color }},
+            }
+        end
+        widget:set_color(color_args)
+        widget:set_width(width)
+        widget:set_height(height)
+        widget:set_vertical(true)
+        widgets[sink_id] = widget
+        vicious.register(widget, self, '$1', pulseaudio_config.update_interval, sink_id)
+    end
+    return widgets, icons
 end
 
 -- Vicious worker function
 function vicious_worker(format, sink_id)
-       update_data(pacmd_force_update)
-       update_icon(sink_id)
-       update_tooltip(sink_id)
-       pacmd_force_update = false
-       if fatal_error then
-               delightful.utils.print_error('pulseaudio', fatal_error)
-               return 0
-       end
-       if not sink_data[sink_id] then
-               return 0
-       end
-       if sink_data[sink_id].error_string then
-               delightful.utils.print_error('pulseaudio', sink_data[sink_id].error_string)
-               return 0
-       end
-       return sink_data[sink_id].volperc
+    update_data(pacmd_force_update)
+    update_icon(sink_id)
+    update_tooltip(sink_id)
+    pacmd_force_update = false
+    if fatal_error then
+        delightful.utils.print_error('pulseaudio', fatal_error)
+        return 0
+    end
+    if not sink_data[sink_id] then
+        return 0
+    end
+    if sink_data[sink_id].error_string then
+        delightful.utils.print_error('pulseaudio', sink_data[sink_id].error_string)
+        return 0
+    end
+    return sink_data[sink_id].volperc
 end
 
 -- Sink helpers
 
 function update_sink_string(force_update)
-       if not retry_fatal_error then
-               return
-       end
-       local now = os.time()
-       local pacmd_command = pulseaudio_config.pacmd_command .. ' list-sinks'
-       if force_update or not pacmd_string or (pacmd_timestamp and now - pacmd_timestamp >= pulseaudio_config.update_interval) then
-               pacmd_string = awful.util.pread(pacmd_command)
-               pacmd_timestamp = now
-       end
-       if not pacmd_string or #pacmd_string == 0 then
-               pacmd_string = nil
-               -- try starting PulseAudio
-               if pulseaudio_config.pulseaudio_start then
-                       awful.util.spawn(pulseaudio_config.pulseaudio_command, false)
-                       os.execute('sleep 1')
-                       pacmd_string = awful.util.pread(pacmd_command)
-                       if not pacmd_string or #pacmd_string == 0 then
-                               pacmd_string = nil
-                               fatal_error = 'Tried to start PulseAudio, but failed list PulseAudio sinks. Is PulseAudio installed and properly configured?'
-                               return
-                       end
-               else
-                       fatal_error = 'Failed to list PulseAudio sinks. Is PulseAudio installed and running?'
-                       return
-               end
-       end
+    if not retry_fatal_error then
+        return
+    end
+    local now = os.time()
+    local pacmd_command = pulseaudio_config.pacmd_command .. ' list-sinks'
+    if force_update or not pacmd_string or (pacmd_timestamp and now - pacmd_timestamp >= pulseaudio_config.update_interval) then
+        pacmd_string = awful.util.pread(pacmd_command)
+        pacmd_timestamp = now
+    end
+    if not pacmd_string or #pacmd_string == 0 then
+        pacmd_string = nil
+        -- try starting PulseAudio
+        if pulseaudio_config.pulseaudio_start then
+            awful.util.spawn(pulseaudio_config.pulseaudio_command, false)
+            os.execute('sleep 1')
+            pacmd_string = awful.util.pread(pacmd_command)
+            if not pacmd_string or #pacmd_string == 0 then
+                pacmd_string = nil
+                fatal_error = 'Tried to start PulseAudio, but failed list PulseAudio sinks. Is PulseAudio installed and properly configured?'
+                return
+            end
+        else
+            fatal_error = 'Failed to list PulseAudio sinks. Is PulseAudio installed and running?'
+            return
+        end
+    end
 end
 
 function update_number_of_sinks()
-       if number_of_sinks then
-               return
-       end
-       number_of_sinks = 0
-       if pulseaudio_config.sink_nums then
-               number_of_sinks = #pulseaudio_config.sink_nums
-       elseif pacmd_string then
-               pacmd_string:gsub('(.-)\n', function(line)
-                               line:gsub('^[%s\*]+index:%s%d$', function(match)
-                                               number_of_sinks = number_of_sinks + 1
-                               end)
-               end)
-       end
-       if number_of_sinks == 0 then
-               number_of_sinks = 1
-               local error_string = 'Failed to detect PulseAudio sinks'
-               if fatal_error then
-                       error_string = string.format('%s: %s', error_string, fatal_error)
-               end
-               fatal_error = error_string
-       end
+    if number_of_sinks then
+        return
+    end
+    number_of_sinks = 0
+    if pulseaudio_config.sink_nums then
+        number_of_sinks = #pulseaudio_config.sink_nums
+    elseif pacmd_string then
+        pacmd_string:gsub('(.-)\n', function(line)
+                line:gsub('^[%s\*]+index:%s%d$', function(match)
+                        number_of_sinks = number_of_sinks + 1
+                end)
+        end)
+    end
+    if number_of_sinks == 0 then
+        number_of_sinks = 1
+        local error_string = 'Failed to detect PulseAudio sinks'
+        if fatal_error then
+            error_string = string.format('%s: %s', error_string, fatal_error)
+        end
+        fatal_error = error_string
+    end
 end
 
 -- PulseAudio volume control functions
 
 function pulseaudio_control(command, sink_id)
-       if sink_data[sink_id] and not fatal_error and not sink_data[sink_id].error_string then
-               if command == 'toggle' then
-                       pulseaudio_toggle(sink_id)
-               elseif command == 'up' then
-                       pulseaudio_set_volume(sink_id, volstep)
-               elseif command == 'down' then
-                       pulseaudio_set_volume(sink_id, -volstep)
-               end
-       end
-       pacmd_force_update = true
-       vicious.force({ widgets[sink_id] })
-       update_icon(sink_id)
+    if sink_data[sink_id] and not fatal_error and not sink_data[sink_id].error_string then
+        if command == 'toggle' then
+            pulseaudio_toggle(sink_id)
+        elseif command == 'up' then
+            pulseaudio_set_volume(sink_id, volstep)
+        elseif command == 'down' then
+            pulseaudio_set_volume(sink_id, -volstep)
+        end
+    end
+    pacmd_force_update = true
+    vicious.force({ widgets[sink_id] })
+    update_icon(sink_id)
 end
 
 function pulseaudio_set_volume(sink_id, step)
-       if not sink_data[sink_id] or fatal_error or sink_data[sink_id].error_string or not sink_data[sink_id].volperc then
-               return
-       end
-       local volperc_new = sink_data[sink_id].volperc + step
-       if volperc_new > 100 then
-               volperc_new = 100
-       elseif volperc_new < 0 then
-               volperc_new = 0
-       end
-       local volnum_new = math.floor(((maxvol / 100) * volperc_new) + 0.5)
-       if volnum_new ~= sink_data[sink_id].volnum then
-               awful.util.spawn('pacmd set-sink-volume ' .. sink_data[sink_id].num .. ' ' .. volnum_new, false)
-               sink_data[sink_id].volperc = volperc_new
-               sink_data[sink_id].volnum = volnum_new
-       end
+    if not sink_data[sink_id] or fatal_error or sink_data[sink_id].error_string or not sink_data[sink_id].volperc then
+        return
+    end
+    local volperc_new = sink_data[sink_id].volperc + step
+    if volperc_new > 100 then
+        volperc_new = 100
+    elseif volperc_new < 0 then
+        volperc_new = 0
+    end
+    local volnum_new = math.floor(((maxvol / 100) * volperc_new) + 0.5)
+    if volnum_new ~= sink_data[sink_id].volnum then
+        awful.util.spawn('pacmd set-sink-volume ' .. sink_data[sink_id].num .. ' ' .. volnum_new, false)
+        sink_data[sink_id].volperc = volperc_new
+        sink_data[sink_id].volnum = volnum_new
+    end
 end
 
 function pulseaudio_toggle(sink_id)
-       if not sink_data[sink_id] or fatal_error or sink_data[sink_id].error_string then
-               return
-       elseif sink_data[sink_id].muted ~= nil then
-               if sink_data[sink_id].muted then
-                       pulseaudio_unmute(sink_id)
-               else
-                       pulseaudio_mute(sink_id)
-               end
-       end
+    if not sink_data[sink_id] or fatal_error or sink_data[sink_id].error_string then
+        return
+    elseif sink_data[sink_id].muted ~= nil then
+        if sink_data[sink_id].muted then
+            pulseaudio_unmute(sink_id)
+        else
+            pulseaudio_mute(sink_id)
+        end
+    end
 end
 
 function pulseaudio_mute(sink_id)
-       if not sink_data[sink_id] or fatal_error or sink_data[sink_id].error_string then
-               return
-       end
-       awful.util.spawn('pacmd set-sink-mute ' .. sink_data[sink_id].num .. ' 1', false)
-       sink_data[sink_id].muted = true
+    if not sink_data[sink_id] or fatal_error or sink_data[sink_id].error_string then
+        return
+    end
+    awful.util.spawn('pacmd set-sink-mute ' .. sink_data[sink_id].num .. ' 1', false)
+    sink_data[sink_id].muted = true
 end
 
 function pulseaudio_unmute(sink_id)
-       if not sink_data[sink_id] or fatal_error or sink_data[sink_id].error_string then
-               return
-       end
-       awful.util.spawn('pacmd set-sink-mute ' .. sink_data[sink_id].num .. ' 0', false)
-       sink_data[sink_id].muted = false
+    if not sink_data[sink_id] or fatal_error or sink_data[sink_id].error_string then
+        return
+    end
+    awful.util.spawn('pacmd set-sink-mute ' .. sink_data[sink_id].num .. ' 0', false)
+    sink_data[sink_id].muted = false
 end
 
 setmetatable(_M, { __call = function(_, ...) return vicious_worker(...) end })
index fa60a22..222211e 100644 (file)
@@ -159,126 +159,126 @@ local weather_config = {}
 local weather_data   = {}
 
 local icon_description = {
-       weather_clear             = { beautiful_name = 'delightful_weather_clear',             default_icon = 'weather-clear'             },
-       weather_clear_night       = { beautiful_name = 'delightful_weather_clear_night',       default_icon = 'weather-clear-night'       },
-       weather_few_clouds        = { beautiful_name = 'delightful_weather_few_clouds',        default_icon = 'weather-few-clouds'        },
-       weather_few_clouds_night  = { beautiful_name = 'delightful_weather_few_clouds_night',  default_icon = 'weather-few-clouds-night'  },
-       weather_overcast          = { beautiful_name = 'delightful_weather_overcast',          default_icon = 'weather-overcast'          },
-       weather_alert             = { beautiful_name = 'delightful_weather_alert',             default_icon = 'weather-severe-alert'      },
-       weather_storm             = { beautiful_name = 'delightful_weather_strom',             default_icon = 'weather-storm'             },
-       weather_snow              = { beautiful_name = 'delightful_weather_snow',              default_icon = 'weather-snow'              },
-       weather_scattered_showers = { beautiful_name = 'delightful_weather_scattered_showers', default_icon = 'weather-showers-scattered' },
-       weather_showers           = { beautiful_name = 'delightful_weather_showers',           default_icon = 'weather-showers'           },
-       weather_fog               = { beautiful_name = 'delightful_weather_fog',               default_icon = 'weather-fog'               },
-       not_found                 = { beautiful_name = 'delightful_not_found',                 default_icon = 'dialog-question'           },
-       error                     = { beautiful_name = 'delightful_error',                     default_icon = 'dialog-error'              },
+    weather_clear             = { beautiful_name = 'delightful_weather_clear',             default_icon = 'weather-clear'             },
+    weather_clear_night       = { beautiful_name = 'delightful_weather_clear_night',       default_icon = 'weather-clear-night'       },
+    weather_few_clouds        = { beautiful_name = 'delightful_weather_few_clouds',        default_icon = 'weather-few-clouds'        },
+    weather_few_clouds_night  = { beautiful_name = 'delightful_weather_few_clouds_night',  default_icon = 'weather-few-clouds-night'  },
+    weather_overcast          = { beautiful_name = 'delightful_weather_overcast',          default_icon = 'weather-overcast'          },
+    weather_alert             = { beautiful_name = 'delightful_weather_alert',             default_icon = 'weather-severe-alert'      },
+    weather_storm             = { beautiful_name = 'delightful_weather_strom',             default_icon = 'weather-storm'             },
+    weather_snow              = { beautiful_name = 'delightful_weather_snow',              default_icon = 'weather-snow'              },
+    weather_scattered_showers = { beautiful_name = 'delightful_weather_scattered_showers', default_icon = 'weather-showers-scattered' },
+    weather_showers           = { beautiful_name = 'delightful_weather_showers',           default_icon = 'weather-showers'           },
+    weather_fog               = { beautiful_name = 'delightful_weather_fog',               default_icon = 'weather-fog'               },
+    not_found                 = { beautiful_name = 'delightful_not_found',                 default_icon = 'dialog-question'           },
+    error                     = { beautiful_name = 'delightful_error',                     default_icon = 'dialog-error'              },
 }
 -- dynamically generate entries for the moon phase icons
 local night_icons = { 'weather_clear', 'weather_few_clouds' }
 for n = 0, 35 do
-       if n ~= 18 then
-               for _, day_icon in pairs(night_icons) do
-                       local night_icon = string.format('%s_night_%03d', day_icon, n * 10)
-                       icon_description[night_icon] = { beautiful_name = string.format('delightful_%s', night_icon), default_icon = function() return night_icon:gsub('_', '-') end }
-               end
-       end
+    if n ~= 18 then
+        for _, day_icon in pairs(night_icons) do
+            local night_icon = string.format('%s_night_%03d', day_icon, n * 10)
+            icon_description[night_icon] = { beautiful_name = string.format('delightful_%s', night_icon), default_icon = function() return night_icon:gsub('_', '-') end }
+        end
+    end
 end
 
 -- icon mappings for METAR data
 local metar_icons = {
-       sky = {
-               not_found = {
-                       metar.SKY_STATUS.UNKNOWN,
-               },
-               weather_fog = {
-                       metar.SKY_STATUS.OBSCURE,
-               },
-               weather_clear = {
-                       metar.SKY_STATUS.CLEAR,
-                       metar.SKY_STATUS.NO_SIGNIFICANT_CLOUDS,
-                       metar.SKY_STATUS.NO_CLOUDS_DETECTED,
-               },
-       },
-       clouds = {
-               weather_clear = {
-                       metar.CLOUD_COVERAGE.CLEAR,
-               },
-               weather_few_clouds = {
-                       metar.CLOUD_COVERAGE.FEW,
-                       metar.CLOUD_COVERAGE.SCATTERED,
-                       metar.CLOUD_COVERAGE.BROKEN_SKY,
-               },
-               weather_overcast = {
-                       metar.CLOUD_COVERAGE.OVERCAST,
-               },
-       },
-       phenomena = {
-               weather_alert = {
-                       metar.WEATHER_PHENOMENA.DUST_WHIRLS,
-                       metar.WEATHER_PHENOMENA.DUST_STORM,
-                       metar.WEATHER_PHENOMENA.SAND_STORM,
-                       metar.WEATHER_PHENOMENA.FUNNEL_CLOUD,
-                       metar.WEATHER_PHENOMENA.WIDESPREAD_DUST,
-                       metar.WEATHER_PHENOMENA.VOLCANIC_ASH,
-                       metar.WEATHER_PHENOMENA.SQUALLS,
-               },
-               weather_snow = {
-                       metar.WEATHER_PHENOMENA.ICE_CRYSTALS,
-                       metar.WEATHER_PHENOMENA.ICE_PELLETS,
-                       metar.WEATHER_PHENOMENA.SNOW,
-                       metar.WEATHER_PHENOMENA.SNOW_GRAINS,
-               },
-               weather_scattered_showers = {
-                       metar.WEATHER_PHENOMENA.DRIZZLE,
-                       metar.WEATHER_PHENOMENA.SMALL_HAIL,
-                       metar.WEATHER_PHENOMENA.HAIL,
-               },
-               weather_showers = {
-                       metar.WEATHER_PHENOMENA.RAIN,
-               },
-               weather_fog = {
-                       metar.WEATHER_PHENOMENA.MIST,
-                       metar.WEATHER_PHENOMENA.FOG,
-                       metar.WEATHER_PHENOMENA.SMOKE,
-                       metar.WEATHER_PHENOMENA.HAZE,
-                       metar.WEATHER_PHENOMENA.SAND,
-                       metar.WEATHER_PHENOMENA.SPRAY,
-                       metar.WEATHER_PHENOMENA.WIDESPREAD_DUST,
-               },
-               not_found = {
-                       metar.WEATHER_PHENOMENA.UNKNOWN,
-               },
-       },
-       descriptor = {
-               weather_showers = {
-                       metar.WEATHER_DESCRIPTOR.SHOWERS,
-               },
-               weather_storm = {
-                       metar.WEATHER_DESCRIPTOR.THUNDERSTORM,
-               },
-               weather_snow = {
-                       metar.WEATHER_DESCRIPTOR.FREEZING,
-               },
-       }
+    sky = {
+        not_found = {
+            metar.SKY_STATUS.UNKNOWN,
+        },
+        weather_fog = {
+            metar.SKY_STATUS.OBSCURE,
+        },
+        weather_clear = {
+            metar.SKY_STATUS.CLEAR,
+            metar.SKY_STATUS.NO_SIGNIFICANT_CLOUDS,
+            metar.SKY_STATUS.NO_CLOUDS_DETECTED,
+        },
+    },
+    clouds = {
+        weather_clear = {
+            metar.CLOUD_COVERAGE.CLEAR,
+        },
+        weather_few_clouds = {
+            metar.CLOUD_COVERAGE.FEW,
+            metar.CLOUD_COVERAGE.SCATTERED,
+            metar.CLOUD_COVERAGE.BROKEN_SKY,
+        },
+        weather_overcast = {
+            metar.CLOUD_COVERAGE.OVERCAST,
+        },
+    },
+    phenomena = {
+        weather_alert = {
+            metar.WEATHER_PHENOMENA.DUST_WHIRLS,
+            metar.WEATHER_PHENOMENA.DUST_STORM,
+            metar.WEATHER_PHENOMENA.SAND_STORM,
+            metar.WEATHER_PHENOMENA.FUNNEL_CLOUD,
+            metar.WEATHER_PHENOMENA.WIDESPREAD_DUST,
+            metar.WEATHER_PHENOMENA.VOLCANIC_ASH,
+            metar.WEATHER_PHENOMENA.SQUALLS,
+        },
+        weather_snow = {
+            metar.WEATHER_PHENOMENA.ICE_CRYSTALS,
+            metar.WEATHER_PHENOMENA.ICE_PELLETS,
+            metar.WEATHER_PHENOMENA.SNOW,
+            metar.WEATHER_PHENOMENA.SNOW_GRAINS,
+        },
+        weather_scattered_showers = {
+            metar.WEATHER_PHENOMENA.DRIZZLE,
+            metar.WEATHER_PHENOMENA.SMALL_HAIL,
+            metar.WEATHER_PHENOMENA.HAIL,
+        },
+        weather_showers = {
+            metar.WEATHER_PHENOMENA.RAIN,
+        },
+        weather_fog = {
+            metar.WEATHER_PHENOMENA.MIST,
+            metar.WEATHER_PHENOMENA.FOG,
+            metar.WEATHER_PHENOMENA.SMOKE,
+            metar.WEATHER_PHENOMENA.HAZE,
+            metar.WEATHER_PHENOMENA.SAND,
+            metar.WEATHER_PHENOMENA.SPRAY,
+            metar.WEATHER_PHENOMENA.WIDESPREAD_DUST,
+        },
+        not_found = {
+            metar.WEATHER_PHENOMENA.UNKNOWN,
+        },
+    },
+    descriptor = {
+        weather_showers = {
+            metar.WEATHER_DESCRIPTOR.SHOWERS,
+        },
+        weather_storm = {
+            metar.WEATHER_DESCRIPTOR.THUNDERSTORM,
+        },
+        weather_snow = {
+            metar.WEATHER_DESCRIPTOR.FREEZING,
+        },
+    }
 }
 for metar_icon_type, weather_values in pairs(metar_icons) do
-       for icon_name, weather_types in pairs(weather_values) do
-               for _, weather_type in pairs(weather_types) do
-                       local metar_icon_key = string.format('weather_%s_%d', metar_icon_type, weather_type)
-                       icon_description[metar_icon_key] = icon_description[icon_name]
-               end
-       end
+    for icon_name, weather_types in pairs(weather_values) do
+        for _, weather_type in pairs(weather_types) do
+            local metar_icon_key = string.format('weather_%s_%d', metar_icon_type, weather_type)
+            icon_description[metar_icon_key] = icon_description[icon_name]
+        end
+    end
 end
 
 -- mappings how to display METAR data in text
 local metar_strings = {
-       wind_direction     = {},
-       cloud_coverage     = {},
-       cloud_type         = {},
-       sky                = {},
-       weather_intensity  = {},
-       weather_descriptor = {},
-       weather_phenomena  = {},
+    wind_direction     = {},
+    cloud_coverage     = {},
+    cloud_type         = {},
+    sky                = {},
+    weather_intensity  = {},
+    weather_descriptor = {},
+    weather_phenomena  = {},
 }
 metar_strings.wind_direction[metar.WIND_DIRECTION.VRB]                   = 'Variable direction'
 metar_strings.wind_direction[metar.WIND_DIRECTION.N]                     = 'North'
@@ -348,985 +348,985 @@ metar_strings.weather_phenomena[metar.WEATHER_PHENOMENA.DUST_STORM]      = 'Dust
 -- lists supported units, their mapping with configuration options
 -- and how to display the units in text
 local unit_data = {
-       temperature = {
-               units = {
-                       weatherlib.TEMPERATURE_UNITS.CELCIUS,
-                       weatherlib.TEMPERATURE_UNITS.FAHRENHEIT,
-               },
-               config_options = {
-                       'c',
-                       'f',
-               },
-               display_texts = {
-                       'Celcius',
-                       'Fahrenheit',
-               },
-               display_units = {
-                       ' °C',
-                       ' °F',
-               },
-       },
-       speed = {
-               units = {
-                       weatherlib.SPEED_UNITS.KNOT,
-                       weatherlib.SPEED_UNITS.MS,
-                       weatherlib.SPEED_UNITS.KMH,
-                       weatherlib.SPEED_UNITS.MPH,
-               },
-               config_options = {
-                       'kn',
-                       'ms',
-                       'kmh',
-                       'mph',
-               },
-               display_texts = {
-                       'knots',
-                       'meters per second',
-                       'kilometers per hour',
-                       'miles per hour',
-               },
-               display_units = {
-                       ' kn',
-                       ' m/s',
-                       ' km/h',
-                       ' mph',
-               },
-       },
-       pressure = {
-               units = {
-                       weatherlib.PRESSURE_UNITS.HPA,
-                       weatherlib.PRESSURE_UNITS.ATM,
-                       weatherlib.PRESSURE_UNITS.INHG,
-               },
-               config_options = {
-                       'hpa',
-                       'atm',
-                       'inhg',
-               },
-               display_texts = {
-                       'hectopascal',
-                       'Standard atmosphere',
-                       'Inches of Merucry',
-               },
-               display_units = {
-                       ' hPa',
-                       ' atm',
-                       ' inHg',
-               },
-       },
-       length = {
-               units = {
-                       weatherlib.LENGTH_UNITS.METER,
-                       weatherlib.LENGTH_UNITS.KILOMETER,
-                       weatherlib.LENGTH_UNITS.FOOT,
-                       weatherlib.LENGTH_UNITS.YARD,
-                       weatherlib.LENGTH_UNITS.MILE,
-               },
-               config_options = {
-                       'm',
-                       'km',
-                       'ft',
-                       'yd',
-                       'mi',
-               },
-               display_texts = {
-                       'meters',
-                       'kilometers',
-                       'feet',
-                       'yards',
-                       'miles',
-               },
-               display_units = {
-                       'm',
-                       'km',
-                       'ft',
-                       'yd',
-                       'mi',
-               },
-       },
+    temperature = {
+        units = {
+            weatherlib.TEMPERATURE_UNITS.CELCIUS,
+            weatherlib.TEMPERATURE_UNITS.FAHRENHEIT,
+        },
+        config_options = {
+            'c',
+            'f',
+        },
+        display_texts = {
+            'Celcius',
+            'Fahrenheit',
+        },
+        display_units = {
+            ' °C',
+            ' °F',
+        },
+    },
+    speed = {
+        units = {
+            weatherlib.SPEED_UNITS.KNOT,
+            weatherlib.SPEED_UNITS.MS,
+            weatherlib.SPEED_UNITS.KMH,
+            weatherlib.SPEED_UNITS.MPH,
+        },
+        config_options = {
+            'kn',
+            'ms',
+            'kmh',
+            'mph',
+        },
+        display_texts = {
+            'knots',
+            'meters per second',
+            'kilometers per hour',
+            'miles per hour',
+        },
+        display_units = {
+            ' kn',
+            ' m/s',
+            ' km/h',
+            ' mph',
+        },
+    },
+    pressure = {
+        units = {
+            weatherlib.PRESSURE_UNITS.HPA,
+            weatherlib.PRESSURE_UNITS.ATM,
+            weatherlib.PRESSURE_UNITS.INHG,
+        },
+        config_options = {
+            'hpa',
+            'atm',
+            'inhg',
+        },
+        display_texts = {
+            'hectopascal',
+            'Standard atmosphere',
+            'Inches of Merucry',
+        },
+        display_units = {
+            ' hPa',
+            ' atm',
+            ' inHg',
+        },
+    },
+    length = {
+        units = {
+            weatherlib.LENGTH_UNITS.METER,
+            weatherlib.LENGTH_UNITS.KILOMETER,
+            weatherlib.LENGTH_UNITS.FOOT,
+            weatherlib.LENGTH_UNITS.YARD,
+            weatherlib.LENGTH_UNITS.MILE,
+        },
+        config_options = {
+            'm',
+            'km',
+            'ft',
+            'yd',
+            'mi',
+        },
+        display_texts = {
+            'meters',
+            'kilometers',
+            'feet',
+            'yards',
+            'miles',
+        },
+        display_units = {
+            'm',
+            'km',
+            'ft',
+            'yd',
+            'mi',
+        },
+    },
 }
 
 -- define fields in the summary text
 local summary_field_data = {
-       {
-               title    = 'Location',
-               data_key = 'location',
-       },
-       {
-               title    = 'Updated',
-               data_key = 'updated',
-       },
-       {
-               title    = 'Conditions',
-               data_key = 'weather',
-       },
-       {
-               title    = 'Sky',
-               data_key = 'sky',
-       },
-       {
-               title    = 'Temperature',
-               data_key = 'temperature',
-       },
-       {
-               title    = 'Dew point',
-               data_key = 'dewpoint',
-       },
-       {
-               title    = 'Relative humidity',
-               data_key = 'humidity',
-       },
-       {
-               title    = 'Wind',
-               data_key = 'wind',
-       },
-       {
-               title    = 'Pressure',
-               data_key = 'pressure',
-       },
-       {
-               title    = 'Visibility',
-               data_key = 'visibility',
-       },
-       {
-               title    = 'Sunrise',
-               data_key = 'sunrise',
-       },
-       {
-               title    = 'Sunset',
-               data_key = 'sunset',
-       },
+    {
+        title    = 'Location',
+        data_key = 'location',
+    },
+    {
+        title    = 'Updated',
+        data_key = 'updated',
+    },
+    {
+        title    = 'Conditions',
+        data_key = 'weather',
+    },
+    {
+        title    = 'Sky',
+        data_key = 'sky',
+    },
+    {
+        title    = 'Temperature',
+        data_key = 'temperature',
+    },
+    {
+        title    = 'Dew point',
+        data_key = 'dewpoint',
+    },
+    {
+        title    = 'Relative humidity',
+        data_key = 'humidity',
+    },
+    {
+        title    = 'Wind',
+        data_key = 'wind',
+    },
+    {
+        title    = 'Pressure',
+        data_key = 'pressure',
+    },
+    {
+        title    = 'Visibility',
+        data_key = 'visibility',
+    },
+    {
+        title    = 'Sunrise',
+        data_key = 'sunrise',
+    },
+    {
+        title    = 'Sunset',
+        data_key = 'sunset',
+    },
 }
 
 -- Configuration handler
 function check_config_unit(data, config_unit)
-       if not data then
-               return false, 'internal error, data is nil'
-       end
-       if not config_unit then
-               return false, 'internal error, config unit is nil'
-       end
-       local found = false
-       for _, check_config_unit in pairs(data.units) do
-               if config_unit == data.config_options[check_config_unit] then
-                       found = true
-                       break
-               end
-       end
-       if found then
-               return true
-       end
-       local error_string = 'needs to be one of the following: '
-       for _, unit in pairs(data.units) do
-               error_string = string.format('%s"%s" (%s), ', error_string, data.config_options[unit], data.display_texts[unit])
-       end
-       return false, error_string:gsub(',%s*$', '')
+    if not data then
+        return false, 'internal error, data is nil'
+    end
+    if not config_unit then
+        return false, 'internal error, config unit is nil'
+    end
+    local found = false
+    for _, check_config_unit in pairs(data.units) do
+        if config_unit == data.config_options[check_config_unit] then
+            found = true
+            break
+        end
+    end
+    if found then
+        return true
+    end
+    local error_string = 'needs to be one of the following: '
+    for _, unit in pairs(data.units) do
+        error_string = string.format('%s"%s" (%s), ', error_string, data.config_options[unit], data.display_texts[unit])
+    end
+    return false, error_string:gsub(',%s*$', '')
 end
 
 local config_description = {
-       {
-               name     = 'temperature_unit',
-               required = true,
-               default  = 'c',
-               validate = function(value) if not value then return true end return check_config_unit(unit_data.temperature, value) end
-       },
-       {
-               name     = 'wind_speed_unit',
-               required = true,
-               default  = 'ms',
-               validate = function(value) if not value then return true end return check_config_unit(unit_data.speed, value) end
-       },
-       {
-               name     = 'pressure_unit',
-               required = true,
-               default  = 'hpa',
-               validate = function(value) if not value then return true end return check_config_unit(unit_data.pressure, value) end
-       },
-       {
-               name     = 'visibility_unit',
-               required = true,
-               default  = 'm',
-               validate = function(value) if not value then return true end return check_config_unit(unit_data.length, value) end
-       },
-       {
-               name     = 'city',
-               validate = function(value) return delightful.utils.config_string(value) end
-       },
-       {
-               name     = 'timezone_offset_local',
-               required = true,
-               default  = function(config_data) return weatherlib.calc_timezone_offset() end,
-               validate = function(value) return delightful.utils.config_int(value) end
-       },
-       {
-               name     = 'command',
-               validate = function(value) return delightful.utils.config_string(value) end
-       },
-       {
-               name     = 'no_icon',
-               validate = function(value) return delightful.utils.config_boolean(value) end
-       },
-       {
-               name     = 'update_interval',
-               required = true,
-               default  = 20 * 60,
-               validate = function(value) return delightful.utils.config_int(value) end
-       },
-       {
-               name     = 'notification_delay',
-               required = true,
-               default  = 10,
-               validate = function(value) return delightful.utils.config_int(value) end
-       },
-       {
-               name     = 'location_file',
-               default  = function(config_data) if config_data.city then return '/usr/share/libmateweather/Locations.xml.gz' end return nil end,
-               validate = function(value) return delightful.utils.config_file(value) end
-       },
-       -- User is not supposed to supply configuration of this setting
-       {
-               name     = 'location_data',
-               default  = function(config_data)
-                       if not config_data or not config_data.location_file then
-                               return nil
-                       end
-                       local country, city, station_code
-                       if config_data.city then
-                               country, city = config_data.city:match('^([^/]+)/(.+)$')
-                               if not country then
-                                       city = config_data.city
-                               end
-                       elseif config_data.station_code then
-                               station_code = config_data.station_code
-                       else
-                               return nil
-                       end
-                       -- parse station code, station name, coordinates, city, country,
-                       -- and timezone from GWeather XML location datafile
-                       local check_data = { country = country, city = city, station_code = station_code }
-                       local location_data = {}
-                       local open = { country = false, city = false, location = false }
-                       local skip = { country = false, city = false }
-                       local session_data = {}
-                       local data_gatherers = {
-                               country = function(p, v)
-                                       session_data.country = v
-                               end,
-                               city = function(p, v)
-                                       session_data.city = v
-                               end,
-                               coordinates = function(p, v)
-                                       if check_data.station_code then
-                                               session_data.coordinates = v
-                                       else
-                                               if not location_data.coordinates then
-                                                       location_data.coordinates = v
-                                               end
-                                       end
-                               end,
-                               location_name = function(p, v)
-                                       if check_data.station_code then
-                                               session_data.station_name = v
-                                       else
-                                               if not location_data.station_name then
-                                                       location_data.station_name = v
-                                               end
-                                       end
-                               end,
-                               location_code = function(p, v)
-                                       if check_data.station_code then
-                                               session_data.station_code = v
-                                       else
-                                               if not location_data.station_code then
-                                                       location_data.station_code = v
-                                               end
-                                       end
-                               end,
-                               tz = function(p, v)
-                                       if not location_data.station_code then
-                                               location_data.timezone = v
-                                       end
-                               end,
-                       }
-                       local elements = { 'country', 'city', 'location' }
-                       local location_elements = { 'name', 'code' }
-                       local callbacks
-                       callbacks = {
-                               StartElement = function(parser, name)
-                                       if location_data.station_code then
-                                               return
-                                       end
-                                       for _, element in pairs(elements) do
-                                               if name == element then
-                                                       open[element] = true
-                                                       return
-                                               end
-                                               if open[element] and name == 'name' and data_gatherers[element] then
-                                                       callbacks.CharacterData = data_gatherers[element]
-                                                       open[element] = false
-                                                       return
-                                               end
-                                               if element == 'location' and open[element] then
-                                                       for _, location_element in pairs(location_elements) do
-                                                               if name == location_element then
-                                                                       local data_gatherer_key = 'location_' .. location_element
-                                                                       callbacks.CharacterData = data_gatherers[data_gatherer_key]
-                                                                       break
-                                                               end
-                                                       end
-                                               end
-                                               if skip[element] then
-                                                       return
-                                               end
-                                       end
-                                       if not location_data.coordinates and name == 'coordinates' then
-                                               callbacks.CharacterData = data_gatherers.coordinates
-                                               return
-                                       end
-                                       if name == 'tz-hint' then
-                                               callbacks.CharacterData = data_gatherers.tz
-                                               return
-                                       end
-                               end,
-                               EndElement = function(parser, name)
-                                       callbacks.CharacterData = false
-                                       if location_data.station_code then
-                                               return
-                                       end
-                                       if check_data.station_code and open.location and name == 'code' and session_data.station_code:lower() == check_data.station_code:lower() then
-                                               for element in pairs(session_data) do
-                                                       location_data[element] = session_data[element]
-                                               end
-                                               return
-                                       else
-                                               for _, element in pairs(elements) do
-                                                       if session_data[element] then
-                                                               if check_data[element] then
-                                                                       skip[element] = session_data[element]:lower() ~= check_data[element]:lower()
-                                                               end
-                                                               if not skip[element] and element ~= 'location' then
-                                                                       location_data[element] = session_data[element]
-                                                               end
-                                                               session_data[element] = nil
-                                                               return
-                                                       end
-                                                       if element == name then
-                                                               open[element] = false
-                                                               if skip[element] ~= nil then
-                                                                       skip[element] = false
-                                                               end
-                                                               return
-                                                       end
-                                               end
-                                       end
-                               end,
-                               CharacterData = false,
-                       }
-                       local lxp = require('lxp')
-                       local parser = lxp.new(callbacks)
-                       if string.sub(config_data.location_file, -3) == '.gz' then
-                               local zlib = require('zlib')
-                               local inflate = zlib.inflate()
-                               local fh = assert(io.open(config_data.location_file, 'rb'))
-                               local content, eof = inflate(fh:read('*all'))
-                               fh:close()
-                               if eof then
-                                       parser:parse(content)
-                               end
-                       else
-                               for line in io.lines(config_data.location_file) do
-                                       parser:parse(line)
-                               end
-                       end
-                       parser:parse()
-                       parser:close()
-                       local required_entries = { 'station_code', 'station_name', 'coordinates', 'city', 'country', 'timezone' }
-                       for _, entry in pairs(required_entries) do
-                               if not location_data[entry] then
-                                       return nil
-                               end
-                       end
-                       return location_data
-               end
-       },
-       {
-               name     = 'station_code',
-               default  = function(config_data) if config_data.location_data then return config_data.location_data.station_code end return nil end,
-               validate = function(value) return delightful.utils.config_string(value) end
-       },
-       -- User is not supposed to supply configuration of these settings
-       {
-               name     = 'station_name',
-               default  = function(config_data) if config_data.location_data and config_data.location_data.station_name:lower() ~= config_data.city:lower() then return config_data.location_data.station_name end return nil end,
-               validate = function(value) return delightful.utils.config_string(value) end
-       },
-       {
-               name     = 'latitude',
-               default  = function(config_data) if config_data.location_data then local l = config_data.location_data.coordinates:match('^([+-]?%d+\.%d+)%s+[+-]?%d+\.%d+$') if l then return tonumber(l) else return nil end end return nil end,
-               validate = function(value) return delightful.utils.config_number(value) end
-       },
-       {
-               name     = 'longitude',
-               default  = function(config_data) if config_data.location_data then local l = config_data.location_data.coordinates:match('^[+-]?%d+\.%d+%s+([+-]?%d+\.%d+)$') if l then return tonumber(l) else return nil end end return nil end,
-               validate = function(value) return delightful.utils.config_number(value) end
-       },
-       {
-               name     = 'country',
-               default  = function(config_data) if config_data.location_data then return config_data.location_data.country end return nil end,
-               validate = function(value) return delightful.utils.config_string(value) end
-       },
-       {
-               name     = 'timezone',
-               default  = function(config_data) if config_data.location_data then return config_data.location_data.timezone end return nil end,
-               validate = function(value) return delightful.utils.config_string(value) end
-       },
-       {
-               name     = 'timezone_offset',
-               default  = function(config_data) if config_data.timezone then return weatherlib.calc_timezone_offset(config_data.timezone) end return nil end,
-               validate = function(value) return delightful.utils.config_int(value) end
-       },
-       {
-               name     = 'font',
-               required = true,
-               default  = function(config_data) return beautiful.monospace_font or 'monospace' end,
-               validate = function(value) return delightful.utils.config_string(value) end
-       },
+    {
+        name     = 'temperature_unit',
+        required = true,
+        default  = 'c',
+        validate = function(value) if not value then return true end return check_config_unit(unit_data.temperature, value) end
+    },
+    {
+        name     = 'wind_speed_unit',
+        required = true,
+        default  = 'ms',
+        validate = function(value) if not value then return true end return check_config_unit(unit_data.speed, value) end
+    },
+    {
+        name     = 'pressure_unit',
+        required = true,
+        default  = 'hpa',
+        validate = function(value) if not value then return true end return check_config_unit(unit_data.pressure, value) end
+    },
+    {
+        name     = 'visibility_unit',
+        required = true,
+        default  = 'm',
+        validate = function(value) if not value then return true end return check_config_unit(unit_data.length, value) end
+    },
+    {
+        name     = 'city',
+        validate = function(value) return delightful.utils.config_string(value) end
+    },
+    {
+        name     = 'timezone_offset_local',
+        required = true,
+        default  = function(config_data) return weatherlib.calc_timezone_offset() end,
+        validate = function(value) return delightful.utils.config_int(value) end
+    },
+    {
+        name     = 'command',
+        validate = function(value) return delightful.utils.config_string(value) end
+    },
+    {
+        name     = 'no_icon',
+        validate = function(value) return delightful.utils.config_boolean(value) end
+    },
+    {
+        name     = 'update_interval',
+        required = true,
+        default  = 20 * 60,
+        validate = function(value) return delightful.utils.config_int(value) end
+    },
+    {
+        name     = 'notification_delay',
+        required = true,
+        default  = 10,
+        validate = function(value) return delightful.utils.config_int(value) end
+    },
+    {
+        name     = 'location_file',
+        default  = function(config_data) if config_data.city then return '/usr/share/libmateweather/Locations.xml.gz' end return nil end,
+        validate = function(value) return delightful.utils.config_file(value) end
+    },
+    -- User is not supposed to supply configuration of this setting
+    {
+        name     = 'location_data',
+        default  = function(config_data)
+            if not config_data or not config_data.location_file then
+                return nil
+            end
+            local country, city, station_code
+            if config_data.city then
+                country, city = config_data.city:match('^([^/]+)/(.+)$')
+                if not country then
+                    city = config_data.city
+                end
+            elseif config_data.station_code then
+                station_code = config_data.station_code
+            else
+                return nil
+            end
+            -- parse station code, station name, coordinates, city, country,
+            -- and timezone from GWeather XML location datafile
+            local check_data = { country = country, city = city, station_code = station_code }
+            local location_data = {}
+            local open = { country = false, city = false, location = false }
+            local skip = { country = false, city = false }
+            local session_data = {}
+            local data_gatherers = {
+                country = function(p, v)
+                    session_data.country = v
+                end,
+                city = function(p, v)
+                    session_data.city = v
+                end,
+                coordinates = function(p, v)
+                    if check_data.station_code then
+                        session_data.coordinates = v
+                    else
+                        if not location_data.coordinates then
+                            location_data.coordinates = v
+                        end
+                    end
+                end,
+                location_name = function(p, v)
+                    if check_data.station_code then
+                        session_data.station_name = v
+                    else
+                        if not location_data.station_name then
+                            location_data.station_name = v
+                        end
+                    end
+                end,
+                location_code = function(p, v)
+                    if check_data.station_code then
+                        session_data.station_code = v
+                    else
+                        if not location_data.station_code then
+                            location_data.station_code = v
+                        end
+                    end
+                end,
+                tz = function(p, v)
+                    if not location_data.station_code then
+                        location_data.timezone = v
+                    end
+                end,
+            }
+            local elements = { 'country', 'city', 'location' }
+            local location_elements = { 'name', 'code' }
+            local callbacks
+            callbacks = {
+                StartElement = function(parser, name)
+                    if location_data.station_code then
+                        return
+                    end
+                    for _, element in pairs(elements) do
+                        if name == element then
+                            open[element] = true
+                            return
+                        end
+                        if open[element] and name == 'name' and data_gatherers[element] then
+                            callbacks.CharacterData = data_gatherers[element]
+                            open[element] = false
+                            return
+                        end
+                        if element == 'location' and open[element] then
+                            for _, location_element in pairs(location_elements) do
+                                if name == location_element then
+                                    local data_gatherer_key = 'location_' .. location_element
+                                    callbacks.CharacterData = data_gatherers[data_gatherer_key]
+                                    break
+                                end
+                            end
+                        end
+                        if skip[element] then
+                            return
+                        end
+                    end
+                    if not location_data.coordinates and name == 'coordinates' then
+                        callbacks.CharacterData = data_gatherers.coordinates
+                        return
+                    end
+                    if name == 'tz-hint' then
+                        callbacks.CharacterData = data_gatherers.tz
+                        return
+                    end
+                end,
+                EndElement = function(parser, name)
+                    callbacks.CharacterData = false
+                    if location_data.station_code then
+                        return
+                    end
+                    if check_data.station_code and open.location and name == 'code' and session_data.station_code:lower() == check_data.station_code:lower() then
+                        for element in pairs(session_data) do
+                            location_data[element] = session_data[element]
+                        end
+                        return
+                    else
+                        for _, element in pairs(elements) do
+                            if session_data[element] then
+                                if check_data[element] then
+                                    skip[element] = session_data[element]:lower() ~= check_data[element]:lower()
+                                end
+                                if not skip[element] and element ~= 'location' then
+                                    location_data[element] = session_data[element]
+                                end
+                                session_data[element] = nil
+                                return
+                            end
+                            if element == name then
+                                open[element] = false
+                                if skip[element] ~= nil then
+                                    skip[element] = false
+                                end
+                                return
+                            end
+                        end
+                    end
+                end,
+                CharacterData = false,
+            }
+            local lxp = require('lxp')
+            local parser = lxp.new(callbacks)
+            if string.sub(config_data.location_file, -3) == '.gz' then
+                local zlib = require('zlib')
+                local inflate = zlib.inflate()
+                local fh = assert(io.open(config_data.location_file, 'rb'))
+                local content, eof = inflate(fh:read('*all'))
+                fh:close()
+                if eof then
+                    parser:parse(content)
+                end
+            else
+                for line in io.lines(config_data.location_file) do
+                    parser:parse(line)
+                end
+            end
+            parser:parse()
+            parser:close()
+            local required_entries = { 'station_code', 'station_name', 'coordinates', 'city', 'country', 'timezone' }
+            for _, entry in pairs(required_entries) do
+                if not location_data[entry] then
+                    return nil
+                end
+            end
+            return location_data
+        end
+    },
+    {
+        name     = 'station_code',
+        default  = function(config_data) if config_data.location_data then return config_data.location_data.station_code end return nil end,
+        validate = function(value) return delightful.utils.config_string(value) end
+    },
+    -- User is not supposed to supply configuration of these settings
+    {
+        name     = 'station_name',
+        default  = function(config_data) if config_data.location_data and config_data.location_data.station_name:lower() ~= config_data.city:lower() then return config_data.location_data.station_name end return nil end,
+        validate = function(value) return delightful.utils.config_string(value) end
+    },
+    {
+        name     = 'latitude',
+        default  = function(config_data) if config_data.location_data then local l = config_data.location_data.coordinates:match('^([+-]?%d+\.%d+)%s+[+-]?%d+\.%d+$') if l then return tonumber(l) else return nil end end return nil end,
+        validate = function(value) return delightful.utils.config_number(value) end
+    },
+    {
+        name     = 'longitude',
+        default  = function(config_data) if config_data.location_data then local l = config_data.location_data.coordinates:match('^[+-]?%d+\.%d+%s+([+-]?%d+\.%d+)$') if l then return tonumber(l) else return nil end end return nil end,
+        validate = function(value) return delightful.utils.config_number(value) end
+    },
+    {
+        name     = 'country',
+        default  = function(config_data) if config_data.location_data then return config_data.location_data.country end return nil end,
+        validate = function(value) return delightful.utils.config_string(value) end
+    },
+    {
+        name     = 'timezone',
+        default  = function(config_data) if config_data.location_data then return config_data.location_data.timezone end return nil end,
+        validate = function(value) return delightful.utils.config_string(value) end
+    },
+    {
+        name     = 'timezone_offset',
+        default  = function(config_data) if config_data.timezone then return weatherlib.calc_timezone_offset(config_data.timezone) end return nil end,
+        validate = function(value) return delightful.utils.config_int(value) end
+    },
+    {
+        name     = 'font',
+        required = true,
+        default  = function(config_data) return beautiful.monospace_font or 'monospace' end,
+        validate = function(value) return delightful.utils.config_string(value) end
+    },
 }
 
 -- get current METAR data and use populate weather data
 function update_data(station_index)
-       if not weather_data[station_index].metar then
-               return
-       end
-       weather_data[station_index].status_string = nil
-       weather_data[station_index].error_string  = nil
-       weather_data[station_index].summary       = nil
-       weather_data[station_index].metar_data    = nil
-       -- download METAR data
-       local metar_data, metar_error = weather_data[station_index].metar:get_metar_data()
-       if not metar_data then
-               if not metar_error then
-                       metar_error = 'Unknown error'
-               end
-               weather_data[station_index].error_string = string.format('Failed to read weather data: %s', metar_error)
-               return
-       end
-       weather_data[station_index].metar_data = metar_data
+    if not weather_data[station_index].metar then
+        return
+    end
+    weather_data[station_index].status_string = nil
+    weather_data[station_index].error_string  = nil
+    weather_data[station_index].summary       = nil
+    weather_data[station_index].metar_data    = nil
+    -- download METAR data
+    local metar_data, metar_error = weather_data[station_index].metar:get_metar_data()
+    if not metar_data then
+        if not metar_error then
+            metar_error = 'Unknown error'
+        end
+        weather_data[station_index].error_string = string.format('Failed to read weather data: %s', metar_error)
+        return
+    end
+    weather_data[station_index].metar_data = metar_data
 
-       -- update summary data...
-       weather_data[station_index].summary= {}
-       -- ...location
-       weather_data[station_index].summary.location = string.format('Weather station %s', weather_config[station_index].station_code)
-       if weather_config[station_index].city then
-               weather_data[station_index].summary.location = weather_config[station_index].city
-               if weather_config[station_index].country then
-                       weather_data[station_index].summary.location = string.format('%s, %s', weather_data[station_index].summary.location, weather_config[station_index].country)
-               end
-               if weather_config[station_index].station_name then
-                       weather_data[station_index].summary.location = string.format('%s (weather station %s)', weather_data[station_index].summary.location, weather_config[station_index].station_name)
-               end
-       end
-       -- ...time stamp
-       if metar_data.timestamp then
-               -- print in local time of timezone available, otherwise display in UTC
-               if weather_config[station_index].timezone_offset_local and
-                       weather_config[station_index].timezone_offset_local > 0
-               then
-                       weather_data[station_index].summary.updated = os.date('%c', metar_data.timestamp)
-               else
-                       weather_data[station_index].summary.updated = os.date('!%c UTC', metar_data.timestamp)
-               end
-       else
-               weather_data[station_index].summary.updated = 'N/A'
-               delightful.utils.print_error('weather', 'No timestamp in METAR data')
-       end
-       -- ..weather
-       if metar_data.weather then
-               local weather_words = {}
-               if metar_data.weather.intensity then
-                       if #metar_strings.weather_intensity[metar_data.weather.intensity] > 0 then
-                               table.insert(weather_words, metar_strings.weather_intensity[metar_data.weather.intensity])
-                       end
-                       if metar_data.weather.phenomena then
-                               table.insert(weather_words, metar_strings.weather_phenomena[metar_data.weather.phenomena])
-                               if metar_data.weather.descriptor then
-                                       table.insert(weather_words, string.format('/ %s', metar_strings.weather_descriptor[metar_data.weather.descriptor]))
-                               end
-                               local weather_string = ''
-                               for weather_word_index, weather_word in pairs(weather_words) do
-                                       if weather_word_index > 1 then
-                                               weather_word = weather_word:lower()
-                                       end
-                                       weather_string = string.format('%s%s ', weather_string, weather_word)
-                               end
-                               weather_data[station_index].summary.weather = weather_string:gsub('%s*$', '')
-                       else
-                               weather_data[station_index].summary.weather = 'N/A'
-                               delightful.utils.print_error('weather', 'No weather.phenomena in METAR data')
-                       end
-               else
-                       weather_data[station_index].summary.weather = 'N/A'
-                       delightful.utils.print_error('weather', 'No weather.intensity in METAR data')
-               end
-       else
-               weather_data[station_index].summary.weather = 'N/A'
-       end
-       -- ...sky
-       if metar_data.sky then
-               local sky = metar_strings.sky[metar_data.sky]
-               if metar_data.sky == metar.SKY_STATUS.CLOUDS then
-                       if metar_data.clouds and type(metar_data.clouds) == 'table' and metar_data.clouds[1] then
-                               if metar_data.clouds[1].coverage then
-                                       sky = metar_strings.cloud_coverage[metar_data.clouds[1].coverage]
-                                       if metar_data.clouds[1].type then
-                                               sky = string.format('%s, %s', sky, metar_strings.cloud_type[metar_data.clouds[1].type])
-                                       end
-                               else
-                                       weather_data[station_index].summary.sky = 'N/A'
-                                       delightful.utils.print_error('weather', 'No clouds[1].coverage in METAR data')
-                               end
-                       else
-                               weather_data[station_index].summary.sky = 'N/A'
-                               delightful.utils.print_error('weather', 'No clouds in METAR data')
-                       end
-               end
-               weather_data[station_index].summary.sky = sky
-       else
-               weather_data[station_index].summary.sky = 'N/A'
-               delightful.utils.print_error('weather', 'No sky in METAR data')
-       end
-       -- ...feels like temperature
-       if metar_data.temperature
-                       and metar_data.dewpoint
-                       and metar_data.wind
-                       and metar_data.wind.speed then
-               weather_data[station_index].summary.feels_like = string.format('%g%s',
-                               weatherlib.convert_temperature(weatherlib.TEMPERATURE_UNITS.FAHRENHEIT, weather_data[station_index].units.temperature,
-                                               weatherlib.calc_apparent_temperature(
-                                                               weatherlib.convert_temperature(weatherlib.TEMPERATURE_UNITS.CELCIUS, weatherlib.TEMPERATURE_UNITS.FAHRENHEIT, metar_data.temperature),
-                                                               weatherlib.convert_speed(weatherlib.SPEED_UNITS.KNOT, weatherlib.SPEED_UNITS.MPH, metar_data.wind.speed),
-                                                               weatherlib.calc_humidity(metar_data.temperature, metar_data.dewpoint)
-                                               )
-                               ),
-                               weather_data[station_index].display_units.temperature)
-       end
-       -- ...temperature
-       if metar_data.temperature then
-               local temperature = string.format('%g%s',
-                               weatherlib.convert_temperature(weatherlib.TEMPERATURE_UNITS.CELCIUS, weather_data[station_index].units.temperature, metar_data.temperature),
-                               weather_data[station_index].display_units.temperature)
-               if weather_data[station_index].summary.feels_like and weather_data[station_index].summary.feels_like ~= temperature then
-                       temperature = string.format('%s, feels like %s', temperature, weather_data[station_index].summary.feels_like)
-               end
-               weather_data[station_index].summary.temperature = temperature
-       else
-               weather_data[station_index].summary.temperature = 'N/A'
-               delightful.utils.print_error('weather', 'No temperature in METAR data')
-       end
-       -- ...dew point
-       if metar_data.dewpoint then
-               weather_data[station_index].summary.dewpoint = string.format('%g%s',
-                               weatherlib.convert_temperature(weatherlib.TEMPERATURE_UNITS.CELCIUS, weather_data[station_index].units.temperature, metar_data.dewpoint),
-                               weather_data[station_index].display_units.temperature)
-       else
-               weather_data[station_index].summary.dewpoint = 'N/A'
-               delightful.utils.print_error('weather', 'No dewpoint in METAR data')
-       end
-       -- ...humidity
-       if metar_data.temperature and metar_data.dewpoint then
-               weather_data[station_index].summary.humidity = string.format('%s%%', weatherlib.calc_humidity(metar_data.temperature, metar_data.dewpoint))
-       else
-               weather_data[station_index].summary.humidity = 'N/A'
-       end
-       -- ..‥wind
-       if metar_data.wind and type(metar_data.wind) == 'table' then
-               if metar_data.wind.direction then
-                       if metar_data.wind.speed then
-                               local gust = ''
-                               if metar_data.wind.gust then
-                                       gust = string.format(', gust %g%s',
-                                                       metar_data.wind.gust,
-                                                       weather_data[station_index].display_units.speed)
+    -- update summary data...
+    weather_data[station_index].summary= {}
+    -- ...location
+    weather_data[station_index].summary.location = string.format('Weather station %s', weather_config[station_index].station_code)
+    if weather_config[station_index].city then
+        weather_data[station_index].summary.location = weather_config[station_index].city
+        if weather_config[station_index].country then
+            weather_data[station_index].summary.location = string.format('%s, %s', weather_data[station_index].summary.location, weather_config[station_index].country)
+        end
+        if weather_config[station_index].station_name then
+            weather_data[station_index].summary.location = string.format('%s (weather station %s)', weather_data[station_index].summary.location, weather_config[station_index].station_name)
+        end
+    end
+    -- ...time stamp
+    if metar_data.timestamp then
+        -- print in local time of timezone available, otherwise display in UTC
+        if weather_config[station_index].timezone_offset_local and
+            weather_config[station_index].timezone_offset_local > 0
+        then
+            weather_data[station_index].summary.updated = os.date('%c', metar_data.timestamp)
+        else
+            weather_data[station_index].summary.updated = os.date('!%c UTC', metar_data.timestamp)
+        end
+    else
+        weather_data[station_index].summary.updated = 'N/A'
+        delightful.utils.print_error('weather', 'No timestamp in METAR data')
+    end
+    -- ..weather
+    if metar_data.weather then
+        local weather_words = {}
+        if metar_data.weather.intensity then
+            if #metar_strings.weather_intensity[metar_data.weather.intensity] > 0 then
+                table.insert(weather_words, metar_strings.weather_intensity[metar_data.weather.intensity])
+            end
+            if metar_data.weather.phenomena then
+                table.insert(weather_words, metar_strings.weather_phenomena[metar_data.weather.phenomena])
+                if metar_data.weather.descriptor then
+                    table.insert(weather_words, string.format('/ %s', metar_strings.weather_descriptor[metar_data.weather.descriptor]))
+                end
+                local weather_string = ''
+                for weather_word_index, weather_word in pairs(weather_words) do
+                    if weather_word_index > 1 then
+                        weather_word = weather_word:lower()
+                    end
+                    weather_string = string.format('%s%s ', weather_string, weather_word)
+                end
+                weather_data[station_index].summary.weather = weather_string:gsub('%s*$', '')
+            else
+                weather_data[station_index].summary.weather = 'N/A'
+                delightful.utils.print_error('weather', 'No weather.phenomena in METAR data')
+            end
+        else
+            weather_data[station_index].summary.weather = 'N/A'
+            delightful.utils.print_error('weather', 'No weather.intensity in METAR data')
+        end
+    else
+        weather_data[station_index].summary.weather = 'N/A'
+    end
+    -- ...sky
+    if metar_data.sky then
+        local sky = metar_strings.sky[metar_data.sky]
+        if metar_data.sky == metar.SKY_STATUS.CLOUDS then
+            if metar_data.clouds and type(metar_data.clouds) == 'table' and metar_data.clouds[1] then
+                if metar_data.clouds[1].coverage then
+                    sky = metar_strings.cloud_coverage[metar_data.clouds[1].coverage]
+                    if metar_data.clouds[1].type then
+                        sky = string.format('%s, %s', sky, metar_strings.cloud_type[metar_data.clouds[1].type])
+                    end
+                else
+                    weather_data[station_index].summary.sky = 'N/A'
+                    delightful.utils.print_error('weather', 'No clouds[1].coverage in METAR data')
+                end
+            else
+                weather_data[station_index].summary.sky = 'N/A'
+                delightful.utils.print_error('weather', 'No clouds in METAR data')
+            end
+        end
+        weather_data[station_index].summary.sky = sky
+    else
+        weather_data[station_index].summary.sky = 'N/A'
+        delightful.utils.print_error('weather', 'No sky in METAR data')
+    end
+    -- ...feels like temperature
+    if metar_data.temperature
+            and metar_data.dewpoint
+            and metar_data.wind
+            and metar_data.wind.speed then
+        weather_data[station_index].summary.feels_like = string.format('%g%s',
+                weatherlib.convert_temperature(weatherlib.TEMPERATURE_UNITS.FAHRENHEIT, weather_data[station_index].units.temperature,
+                        weatherlib.calc_apparent_temperature(
+                                weatherlib.convert_temperature(weatherlib.TEMPERATURE_UNITS.CELCIUS, weatherlib.TEMPERATURE_UNITS.FAHRENHEIT, metar_data.temperature),
+                                weatherlib.convert_speed(weatherlib.SPEED_UNITS.KNOT, weatherlib.SPEED_UNITS.MPH, metar_data.wind.speed),
+                                weatherlib.calc_humidity(metar_data.temperature, metar_data.dewpoint)
+                        )
+                ),
+                weather_data[station_index].display_units.temperature)
+    end
+    -- ...temperature
+    if metar_data.temperature then
+        local temperature = string.format('%g%s',
+                weatherlib.convert_temperature(weatherlib.TEMPERATURE_UNITS.CELCIUS, weather_data[station_index].units.temperature, metar_data.temperature),
+                weather_data[station_index].display_units.temperature)
+        if weather_data[station_index].summary.feels_like and weather_data[station_index].summary.feels_like ~= temperature then
+            temperature = string.format('%s, feels like %s', temperature, weather_data[station_index].summary.feels_like)
+        end
+        weather_data[station_index].summary.temperature = temperature
+    else
+        weather_data[station_index].summary.temperature = 'N/A'
+        delightful.utils.print_error('weather', 'No temperature in METAR data')
+    end
+    -- ...dew point
+    if metar_data.dewpoint then
+        weather_data[station_index].summary.dewpoint = string.format('%g%s',
+                weatherlib.convert_temperature(weatherlib.TEMPERATURE_UNITS.CELCIUS, weather_data[station_index].units.temperature, metar_data.dewpoint),
+                weather_data[station_index].display_units.temperature)
+    else
+        weather_data[station_index].summary.dewpoint = 'N/A'
+        delightful.utils.print_error('weather', 'No dewpoint in METAR data')
+    end
+    -- ...humidity
+    if metar_data.temperature and metar_data.dewpoint then
+        weather_data[station_index].summary.humidity = string.format('%s%%', weatherlib.calc_humidity(metar_data.temperature, metar_data.dewpoint))
+    else
+        weather_data[station_index].summary.humidity = 'N/A'
+    end
+    -- ..‥wind
+    if metar_data.wind and type(metar_data.wind) == 'table' then
+        if metar_data.wind.direction then
+            if metar_data.wind.speed then
+                local gust = ''
+                if metar_data.wind.gust then
+                    gust = string.format(', gust %g%s',
+                            metar_data.wind.gust,
+                            weather_data[station_index].display_units.speed)
 
-                               end
-                               weather_data[station_index].summary.wind = string.format('%s, %g%s%s',
-                                               metar_strings.wind_direction[metar_data.wind.direction],
-                                               weatherlib.convert_speed(weatherlib.SPEED_UNITS.KNOT, weather_data[station_index].units.speed, metar_data.wind.speed),
-                                               weather_data[station_index].display_units.speed,
-                                               gust)
-                       else
-                               weather_data[station_index].summary.wind = 'N/A'
-                               delightful.utils.print_error('weather', 'No wind.speed in METAR data')
-                       end
-               else
-                       weather_data[station_index].summary.wind = 'N/A'
-                       delightful.utils.print_error('weather', 'No wind.direction in METAR data')
-               end
-       else
-               weather_data[station_index].summary.wind = 'N/A'
-               delightful.utils.print_error('weather', 'No wind in METAR data')
-       end
-       -- ...pressure
-       if metar_data.pressure then
-               weather_data[station_index].summary.pressure = string.format('%g%s',
-                               weatherlib.convert_pressure(weatherlib.PRESSURE_UNITS.HPA, weather_data[station_index].units.pressure, metar_data.pressure),
-                               weather_data[station_index].display_units.pressure)
-       else
-               weather_data[station_index].summary.pressure = 'N/A'
-               delightful.utils.print_error('weather', 'No pressure in METAR data')
-       end
-       -- ...visibility
-       if metar_data.visibility and type(metar_data.visibility) == 'table' and metar_data.visibility[1] then
-               local visibility_value = metar_data.visibility[1].distance
-               local visibility_prefix = ''
-               if visibility_value== 9999 then
-                       visibility_value = 10000
-                       visibility_prefix = 'More than '
-               end
-               weather_data[station_index].summary.visibility = string.format('%s%g%s',
-                               visibility_prefix,
-                               weatherlib.convert_length(weatherlib.LENGTH_UNITS.METER, weather_data[station_index].units.length, visibility_value),
-                               weather_data[station_index].display_units.length)
-       else
-               weather_data[station_index].summary.visibility = 'N/A'
-               delightful.utils.print_error('weather', 'No visibility in METAR data')
-       end
+                end
+                weather_data[station_index].summary.wind = string.format('%s, %g%s%s',
+                        metar_strings.wind_direction[metar_data.wind.direction],
+                        weatherlib.convert_speed(weatherlib.SPEED_UNITS.KNOT, weather_data[station_index].units.speed, metar_data.wind.speed),
+                        weather_data[station_index].display_units.speed,
+                        gust)
+            else
+                weather_data[station_index].summary.wind = 'N/A'
+                delightful.utils.print_error('weather', 'No wind.speed in METAR data')
+            end
+        else
+            weather_data[station_index].summary.wind = 'N/A'
+            delightful.utils.print_error('weather', 'No wind.direction in METAR data')
+        end
+    else
+        weather_data[station_index].summary.wind = 'N/A'
+        delightful.utils.print_error('weather', 'No wind in METAR data')
+    end
+    -- ...pressure
+    if metar_data.pressure then
+        weather_data[station_index].summary.pressure = string.format('%g%s',
+                weatherlib.convert_pressure(weatherlib.PRESSURE_UNITS.HPA, weather_data[station_index].units.pressure, metar_data.pressure),
+                weather_data[station_index].display_units.pressure)
+    else
+        weather_data[station_index].summary.pressure = 'N/A'
+        delightful.utils.print_error('weather', 'No pressure in METAR data')
+    end
+    -- ...visibility
+    if metar_data.visibility and type(metar_data.visibility) == 'table' and metar_data.visibility[1] then
+        local visibility_value = metar_data.visibility[1].distance
+        local visibility_prefix = ''
+        if visibility_value== 9999 then
+            visibility_value = 10000
+            visibility_prefix = 'More than '
+        end
+        weather_data[station_index].summary.visibility = string.format('%s%g%s',
+                visibility_prefix,
+                weatherlib.convert_length(weatherlib.LENGTH_UNITS.METER, weather_data[station_index].units.length, visibility_value),
+                weather_data[station_index].display_units.length)
+    else
+        weather_data[station_index].summary.visibility = 'N/A'
+        delightful.utils.print_error('weather', 'No visibility in METAR data')
+    end
 
-       if weather_config[station_index].timezone_offset then
-               local now = os.date('!*t')
-               weather_data[station_index].now = os.date('*t', os.time(now) + weather_config[station_index].timezone_offset)
-       end
+    if weather_config[station_index].timezone_offset then
+        local now = os.date('!*t')
+        weather_data[station_index].now = os.date('*t', os.time(now) + weather_config[station_index].timezone_offset)
+    end
 
-       -- ...sunrise and sunset using weatherlib
-       if weather_data[station_index].now and weather_config[station_index].timezone_offset and weather_config[station_index].latitude and weather_config[station_index].longitude then
-               local timezone = weatherlib.convert_time(weatherlib.TIME_UNITS.S, weatherlib.TIME_UNITS.H, weather_config[station_index].timezone_offset)
-               local sunrise = weatherlib.calc_sunrise(weather_data[station_index].now, weather_config[station_index].latitude, weather_config[station_index].longitude, timezone)
-               local sunset  = weatherlib.calc_sunset(weather_data[station_index].now, weather_config[station_index].latitude, weather_config[station_index].longitude, timezone)
-               if sunrise and sunset then
-                       local sky_text
-                       weather_data[station_index].summary.sunrise = os.date('%H:%M', os.time(sunrise))
-                       weather_data[station_index].summary.sunset  = os.date('%H:%M', os.time(sunset))
-                       weather_data[station_index].sunrise = sunrise
-                       weather_data[station_index].sunset  = sunset
-                       if weather_data[station_index].now.year == sunrise.year and weather_data[station_index].now.month == sunrise.month and weather_data[station_index].now.day == sunrise.day and weather_data[station_index].now.year == sunset.year and weather_data[station_index].now.month == sunset.month and weather_data[station_index].now.day == sunset.day then
-                               weather_data[station_index].daytime = os.time(weather_data[station_index].now) >= os.time(sunrise) and os.time(weather_data[station_index].now) < os.time(sunset)
-                       elseif os.time(weather_data[station_index].now) >= os.time(sunset) and os.time(weather_data[station_index].now) < os.time(sunrise) then
-                               -- polar night
-                               weather_data[station_index].daytime = false
-                               weather_data[station_index].summary.sunrise = os.date('Next sunrise %a %b %d %Y', os.time(sunrise))
-                               weather_data[station_index].summary.sunset  = os.date('Last sunset %a %b %d %Y', os.time(sunset))
-                               sky_text = 'Polar night'
-                       elseif os.time(weather_data[station_index].now) >= os.time(sunrise) and os.time(weather_data[station_index].now) < os.time(sunset) then
-                               -- midnight sun
-                               weather_data[station_index].daytime = true
-                               weather_data[station_index].summary.sunrise = os.date('Last sunrise %a %b %d %Y', os.time(sunrise))
-                               weather_data[station_index].summary.sunset  = os.date('Next sunset %a %b %d %Y', os.time(sunset))
-                               sky_text = 'Midnight sun'
-                       end
-                       -- update sky summary if required
-                       if sky_text then
-                               if weather_data[station_index].summary.sky then
-                                       weather_data[station_index].summary.sky = string.format('%s, %s', weather_data[station_index].summary.sky, sky_text)
-                               else
-                                       weather_data[station_index].summary.sky = sky_text
-                               end
-                       end
-               else
-                       delightful.utils.print_error('weather', 'Failed to calculate sunrise and sunset')
-               end
-       end
+    -- ...sunrise and sunset using weatherlib
+    if weather_data[station_index].now and weather_config[station_index].timezone_offset and weather_config[station_index].latitude and weather_config[station_index].longitude then
+        local timezone = weatherlib.convert_time(weatherlib.TIME_UNITS.S, weatherlib.TIME_UNITS.H, weather_config[station_index].timezone_offset)
+        local sunrise = weatherlib.calc_sunrise(weather_data[station_index].now, weather_config[station_index].latitude, weather_config[station_index].longitude, timezone)
+        local sunset  = weatherlib.calc_sunset(weather_data[station_index].now, weather_config[station_index].latitude, weather_config[station_index].longitude, timezone)
+        if sunrise and sunset then
+            local sky_text
+            weather_data[station_index].summary.sunrise = os.date('%H:%M', os.time(sunrise))
+            weather_data[station_index].summary.sunset  = os.date('%H:%M', os.time(sunset))
+            weather_data[station_index].sunrise = sunrise
+            weather_data[station_index].sunset  = sunset
+            if weather_data[station_index].now.year == sunrise.year and weather_data[station_index].now.month == sunrise.month and weather_data[station_index].now.day == sunrise.day and weather_data[station_index].now.year == sunset.year and weather_data[station_index].now.month == sunset.month and weather_data[station_index].now.day == sunset.day then
+                weather_data[station_index].daytime = os.time(weather_data[station_index].now) >= os.time(sunrise) and os.time(weather_data[station_index].now) < os.time(sunset)
+            elseif os.time(weather_data[station_index].now) >= os.time(sunset) and os.time(weather_data[station_index].now) < os.time(sunrise) then
+                -- polar night
+                weather_data[station_index].daytime = false
+                weather_data[station_index].summary.sunrise = os.date('Next sunrise %a %b %d %Y', os.time(sunrise))
+                weather_data[station_index].summary.sunset  = os.date('Last sunset %a %b %d %Y', os.time(sunset))
+                sky_text = 'Polar night'
+            elseif os.time(weather_data[station_index].now) >= os.time(sunrise) and os.time(weather_data[station_index].now) < os.time(sunset) then
+                -- midnight sun
+                weather_data[station_index].daytime = true
+                weather_data[station_index].summary.sunrise = os.date('Last sunrise %a %b %d %Y', os.time(sunrise))
+                weather_data[station_index].summary.sunset  = os.date('Next sunset %a %b %d %Y', os.time(sunset))
+                sky_text = 'Midnight sun'
+            end
+            -- update sky summary if required
+            if sky_text then
+                if weather_data[station_index].summary.sky then
+                    weather_data[station_index].summary.sky = string.format('%s, %s', weather_data[station_index].summary.sky, sky_text)
+                else
+                    weather_data[station_index].summary.sky = sky_text
+                end
+            end
+        else
+            delightful.utils.print_error('weather', 'Failed to calculate sunrise and sunset')
+        end
+    end
 
-       -- ...moonphase using weatherlib
-       if weather_data[station_index].now and weather_config[station_index].latitude and weather_config[station_index].longitude then
-               weather_data[station_index].moonphase = calc_moon_phase(weather_data[station_index].now, weather_config[station_index].latitude)
-       end
+    -- ...moonphase using weatherlib
+    if weather_data[station_index].now and weather_config[station_index].latitude and weather_config[station_index].longitude then
+        weather_data[station_index].moonphase = calc_moon_phase(weather_data[station_index].now, weather_config[station_index].latitude)
+    end
 
-       -- and finally set status string to the temperature
-       if weather_data[station_index].summary.temperature then
-               weather_data[station_index].status_string = weather_data[station_index].summary.temperature:gsub(', .+$', '')
-       else
-               weather_data[station_index].status_string = 'N/A'
-       end
+    -- and finally set status string to the temperature
+    if weather_data[station_index].summary.temperature then
+        weather_data[station_index].status_string = weather_data[station_index].summary.temperature:gsub(', .+$', '')
+    else
+        weather_data[station_index].status_string = 'N/A'
+    end
 end
 
 -- Update widget icon based on the weather
 function update_icon(station_index)
-       if not icon_files.not_found or not icon_files.error then
-               return
-       end
-       if not station_index or not icons[station_index] or not weather_data[station_index] then
-               return
-       end
-       local icon_file
-       -- 1st try: use sky icon
-       if weather_data[station_index].metar_data and weather_data[station_index].metar_data.sky then
-               local sky_key = string.format('weather_sky_%d', weather_data[station_index].metar_data.sky)
-               icon_file = delightful.utils.find_icon_file(icon_description, icon_files, sky_key)
-       end
-       -- 2nd try: use clouds icon
-       if weather_data[station_index].metar_data and weather_data[station_index].metar_data.clouds and weather_data[station_index].metar_data.clouds[1] then
-               local clouds_key = string.format('weather_clouds_%d', weather_data[station_index].metar_data.clouds[1].coverage)
-               icon_file = delightful.utils.find_icon_file(icon_description, icon_files, clouds_key)
-       end
-       -- 3rd try: search weather for the icon
-       if weather_data[station_index].metar_data and weather_data[station_index].metar_data.weather then
-               local weather_key, weather_icon_file
-               if weather_data[station_index].metar_data.weather.descriptor then
-                       weather_key = string.format('weather_descriptor_%d', weather_data[station_index].metar_data.weather.descriptor)
-                       weather_icon_file = delightful.utils.find_icon_file(icon_description, icon_files, weather_key)
-               end
-               if not weather_icon_file or (weather_data[station_index].metar_data.weather.descriptor ~= metar.WEATHER_PHENOMENA.THUNDERSTORM) then
-                       if weather_data[station_index].metar_data.weather.intensity == metar.WEATHER_INTENSITY.LIGHT and
-                                       weather_data[station_index].metar_data.weather.phenomena == metar.WEATHER_PHENOMENA.RAIN then
-                               weather_key = 'weather_scattered_showers'
-                       else
-                               weather_key = string.format('weather_phenomena_%d', weather_data[station_index].metar_data.weather.phenomena)
-                       end
-                       weather_icon_file = delightful.utils.find_icon_file(icon_description, icon_files, weather_key)
-               end
-               if weather_icon_file then
-                       icon_file = weather_icon_file
-               end
-       end
-       -- 4th try: errors override the weather icon
-       if weather_data[station_index].error_string then
-               icon_file = icon_files.error
-       end
-       -- 5th try: still no icon, use the not found icon
-       if not icon_file then
-               icon_file = icon_files.not_found
-               delightful.utils.print_error('weather', 'Weather icon not found')
-       end
-       -- 6th try: replace clear and few clouds icons with night versions if sun is set
-       if icon_file and weather_data[station_index].daytime ~= nil and not weather_data[station_index].daytime then
-               for _, day_icon in pairs(night_icons) do
-                       local night_icon = string.format('%s_night', day_icon)
-                       delightful.utils.find_icon_file(icon_description, icon_files, day_icon)
-                       delightful.utils.find_icon_file(icon_description, icon_files, night_icon)
-                       if icon_file == icon_files[day_icon] and icon_files[night_icon] then
-                               icon_file = icon_files[night_icon]
-                               break
-                       end
-               end
-       end
-       -- 7th try: add moon phase icons if it's night
-       if icon_file and weather_data[station_index].moonphase then
-               for _, day_icon in pairs(night_icons) do
-                       local night_icon = string.format('%s_night', day_icon)
-                       delightful.utils.find_icon_file(icon_description, icon_files, night_icon)
-                       if icon_file == icon_files[night_icon] then
-                               local night_icon_moon = string.format('%s_%03d', night_icon, weather_data[station_index].moonphase)
-                               if weather_data[station_index].moonphase == 180 then
-                                       night_icon_moon = night_icon_moon:gsub('_180$', '')
-                               end
-                               delightful.utils.find_icon_file(icon_description, icon_files, night_icon_moon)
-                               if icon_files[night_icon_moon] then
-                                       icon_file = icon_files[night_icon_moon]
-                                       break
-                               end
-                       end
-               end
-       end
-       -- apply the icon
-       if icon_file and
-                       (not prev_icons[station_index] or prev_icons[station_index] ~= icon_file) then
-               prev_icons[station_index] = icon_file
-               icons[station_index]:set_image(icon_file)
-       end
+    if not icon_files.not_found or not icon_files.error then
+        return
+    end
+    if not station_index or not icons[station_index] or not weather_data[station_index] then
+        return
+    end
+    local icon_file
+    -- 1st try: use sky icon
+    if weather_data[station_index].metar_data and weather_data[station_index].metar_data.sky then
+        local sky_key = string.format('weather_sky_%d', weather_data[station_index].metar_data.sky)
+        icon_file = delightful.utils.find_icon_file(icon_description, icon_files, sky_key)
+    end
+    -- 2nd try: use clouds icon
+    if weather_data[station_index].metar_data and weather_data[station_index].metar_data.clouds and weather_data[station_index].metar_data.clouds[1] then
+        local clouds_key = string.format('weather_clouds_%d', weather_data[station_index].metar_data.clouds[1].coverage)
+        icon_file = delightful.utils.find_icon_file(icon_description, icon_files, clouds_key)
+    end
+    -- 3rd try: search weather for the icon
+    if weather_data[station_index].metar_data and weather_data[station_index].metar_data.weather then
+        local weather_key, weather_icon_file
+        if weather_data[station_index].metar_data.weather.descriptor then
+            weather_key = string.format('weather_descriptor_%d', weather_data[station_index].metar_data.weather.descriptor)
+            weather_icon_file = delightful.utils.find_icon_file(icon_description, icon_files, weather_key)
+        end
+        if not weather_icon_file or (weather_data[station_index].metar_data.weather.descriptor ~= metar.WEATHER_PHENOMENA.THUNDERSTORM) then
+            if weather_data[station_index].metar_data.weather.intensity == metar.WEATHER_INTENSITY.LIGHT and
+                    weather_data[station_index].metar_data.weather.phenomena == metar.WEATHER_PHENOMENA.RAIN then
+                weather_key = 'weather_scattered_showers'
+            else
+                weather_key = string.format('weather_phenomena_%d', weather_data[station_index].metar_data.weather.phenomena)
+            end
+            weather_icon_file = delightful.utils.find_icon_file(icon_description, icon_files, weather_key)
+        end
+        if weather_icon_file then
+            icon_file = weather_icon_file
+        end
+    end
+    -- 4th try: errors override the weather icon
+    if weather_data[station_index].error_string then
+        icon_file = icon_files.error
+    end
+    -- 5th try: still no icon, use the not found icon
+    if not icon_file then
+        icon_file = icon_files.not_found
+        delightful.utils.print_error('weather', 'Weather icon not found')
+    end
+    -- 6th try: replace clear and few clouds icons with night versions if sun is set
+    if icon_file and weather_data[station_index].daytime ~= nil and not weather_data[station_index].daytime then
+        for _, day_icon in pairs(night_icons) do
+            local night_icon = string.format('%s_night', day_icon)
+            delightful.utils.find_icon_file(icon_description, icon_files, day_icon)
+            delightful.utils.find_icon_file(icon_description, icon_files, night_icon)
+            if icon_file == icon_files[day_icon] and icon_files[night_icon] then
+                icon_file = icon_files[night_icon]
+                break
+            end
+        end
+    end
+    -- 7th try: add moon phase icons if it's night
+    if icon_file and weather_data[station_index].moonphase then
+        for _, day_icon in pairs(night_icons) do
+            local night_icon = string.format('%s_night', day_icon)
+            delightful.utils.find_icon_file(icon_description, icon_files, night_icon)
+            if icon_file == icon_files[night_icon] then
+                local night_icon_moon = string.format('%s_%03d', night_icon, weather_data[station_index].moonphase)
+                if weather_data[station_index].moonphase == 180 then
+                    night_icon_moon = night_icon_moon:gsub('_180$', '')
+                end
+                delightful.utils.find_icon_file(icon_description, icon_files, night_icon_moon)
+                if icon_files[night_icon_moon] then
+                    icon_file = icon_files[night_icon_moon]
+                    break
+                end
+            end
+        end
+    end
+    -- apply the icon
+    if icon_file and
+            (not prev_icons[station_index] or prev_icons[station_index] ~= icon_file) then
+        prev_icons[station_index] = icon_file
+        icons[station_index]:set_image(icon_file)
+    end
 end
 
 -- Text for the hover notification
 function summary_text(station_index)
-       local text = ''
-       if not station_index or not weather_data[station_index] then
-               return text
-       end
-       if weather_data[station_index].error_string then
-               text = weather_data[station_index].error_string;
-       else
-               for _, summary_field_entry in pairs(summary_field_data) do
-                       local value = weather_data[station_index].summary[summary_field_entry.data_key]
-                       if value then
-                               local formatted_value
-                               if summary_field_entry.format then
-                                       formatted_value = summary_field_entry.format(value, station_index)
-                               else
-                                       formatted_value = value
-                               end
-                               text = string.format('%s<span font_weight="bold">%s</span>: %s\n', text, pad_summary(summary_field_entry.title), formatted_value)
-                       end
-               end
-       end
-       return text:gsub('\n*$', '')
+    local text = ''
+    if not station_index or not weather_data[station_index] then
+        return text
+    end
+    if weather_data[station_index].error_string then
+        text = weather_data[station_index].error_string;
+    else
+        for _, summary_field_entry in pairs(summary_field_data) do
+            local value = weather_data[station_index].summary[summary_field_entry.data_key]
+            if value then
+                local formatted_value
+                if summary_field_entry.format then
+                    formatted_value = summary_field_entry.format(value, station_index)
+                else
+                    formatted_value = value
+                end
+                text = string.format('%s<span font_weight="bold">%s</span>: %s\n', text, pad_summary(summary_field_entry.title), formatted_value)
+            end
+        end
+    end
+    return text:gsub('\n*$', '')
 end
 
 -- Configuration handler
 function handle_config(user_config)
-       local empty_config = delightful.utils.get_empty_config(config_description)
-       if not user_config or #user_config == 0 then
-               table.insert(weather_data,   { error_string = 'No weather locations configured' })
-               table.insert(weather_config, empty_config)
-               return
-       end
-       for station_index, user_config_data in pairs(user_config) do
-               weather_data[station_index] = {}
-               local config_data = delightful.utils.normalize_config(user_config_data, config_description)
-               local validation_errors = delightful.utils.validate_config(config_data, config_description)
-               if not config_data.station_code then
-                       if config_data.city then
-                               if not config_data.location_data then
-                                       if not validation_errors then
-                                               validation_errors = {}
-                                       end
-                                       table.insert(validation_errors, string.format('City "%s" not found in GWeather XML location datafile', config_data.city))
-                               end
-                       else
-                               if not validation_errors then
-                                       validation_errors = {}
-                               end
-                               table.insert(validation_errors, 'Must specify city name if no weather station code given given')
-                       end
-               end
-               if validation_errors then
-                       weather_data[station_index].error_string =
-                                       string.format('Configuration errors:\n%s',
-                                                       delightful.utils.format_validation_errors(validation_errors))
-                       weather_config[station_index] = empty_config
-                       return
-               end
-               if not config_data.city and config_data.location_data then
-                       config_data.city = config_data.location_data.city
-               end
-               weather_config[station_index] = config_data
-       end
+    local empty_config = delightful.utils.get_empty_config(config_description)
+    if not user_config or #user_config == 0 then
+        table.insert(weather_data,   { error_string = 'No weather locations configured' })
+        table.insert(weather_config, empty_config)
+        return
+    end
+    for station_index, user_config_data in pairs(user_config) do
+        weather_data[station_index] = {}
+        local config_data = delightful.utils.normalize_config(user_config_data, config_description)
+        local validation_errors = delightful.utils.validate_config(config_data, config_description)
+        if not config_data.station_code then
+            if config_data.city then
+                if not config_data.location_data then
+                    if not validation_errors then
+                        validation_errors = {}
+                    end
+                    table.insert(validation_errors, string.format('City "%s" not found in GWeather XML location datafile', config_data.city))
+                end
+            else
+                if not validation_errors then
+                    validation_errors = {}
+                end
+                table.insert(validation_errors, 'Must specify city name if no weather station code given given')
+            end
+        end
+        if validation_errors then
+            weather_data[station_index].error_string =
+                    string.format('Configuration errors:\n%s',
+                            delightful.utils.format_validation_errors(validation_errors))
+            weather_config[station_index] = empty_config
+            return
+        end
+        if not config_data.city and config_data.location_data then
+            config_data.city = config_data.location_data.city
+        end
+        weather_config[station_index] = config_data
+    end
 end
 
 -- Initalization
 function load(self, config)
-       handle_config(config)
-       icon_files = {}
-       delightful.utils.find_icon_file(icon_description, icon_files, 'error')
-       delightful.utils.find_icon_file(icon_description, icon_files, 'not_found')
-       local units_config_mapping = {
-               temperature_unit = 'temperature',
-               wind_speed_unit  = 'speed',
-               pressure_unit    = 'pressure',
-               visibility_unit  = 'length'
-       }
-       for station_index, data in pairs(weather_data) do
-               -- init data
-               if weather_config[station_index].station_code then
-                       weather_data[station_index].metar = metar.new(weather_config[station_index].station_code)
-               end
-               weather_data[station_index].units         = {}
-               weather_data[station_index].display_units = {}
-               for config_unit_key, weather_unit_key in pairs(units_config_mapping) do
-                       local config_unit = weather_config[station_index][config_unit_key]
-                       if config_unit then
-                               for unit_index, unit_config_value in pairs(unit_data[weather_unit_key].config_options) do
-                                       if config_unit == unit_config_value then
-                                               weather_data[station_index].units[weather_unit_key]         = unit_data[weather_unit_key].units[unit_index]
-                                               weather_data[station_index].display_units[weather_unit_key] = unit_data[weather_unit_key].display_units[unit_index]
-                                       end
-                               end
-                       end
-               end
+    handle_config(config)
+    icon_files = {}
+    delightful.utils.find_icon_file(icon_description, icon_files, 'error')
+    delightful.utils.find_icon_file(icon_description, icon_files, 'not_found')
+    local units_config_mapping = {
+        temperature_unit = 'temperature',
+        wind_speed_unit  = 'speed',
+        pressure_unit    = 'pressure',
+        visibility_unit  = 'length'
+    }
+    for station_index, data in pairs(weather_data) do
+        -- init data
+        if weather_config[station_index].station_code then
+            weather_data[station_index].metar = metar.new(weather_config[station_index].station_code)
+        end
+        weather_data[station_index].units         = {}
+        weather_data[station_index].display_units = {}
+        for config_unit_key, weather_unit_key in pairs(units_config_mapping) do
+            local config_unit = weather_config[station_index][config_unit_key]
+            if config_unit then
+                for unit_index, unit_config_value in pairs(unit_data[weather_unit_key].config_options) do
+                    if config_unit == unit_config_value then
+                        weather_data[station_index].units[weather_unit_key]         = unit_data[weather_unit_key].units[unit_index]
+                        weather_data[station_index].display_units[weather_unit_key] = unit_data[weather_unit_key].display_units[unit_index]
+                    end
+                end
+            end
+        end
 
-               local icon
-               if not weather_config[station_index].no_icon and icon_files.not_found and icon_files.error then
-                       icon = wibox.widget.imagebox()
-               end
+        local icon
+        if not weather_config[station_index].no_icon and icon_files.not_found and icon_files.error then
+            icon = wibox.widget.imagebox()
+        end
 
-               local popup_enter = function()
-                       local popup_title
-                       if data.error_string then
-                               popup_title = 'Error'
-                       end
-                       data.popup = naughty.notify({
-                                       title   = popup_title,
-                                       text    = summary_text(station_index),
-                                       font    = weather_config[station_index].font or 'monospace',
-                                       timeout = weather_config[station_index].notification_delay,
-                                       screen  = capi.mouse.screen
-                       })
-               end
-               local popup_leave = function() naughty.destroy(data.popup) end
+        local popup_enter = function()
+            local popup_title
+            if data.error_string then
+                popup_title = 'Error'
+            end
+            data.popup = naughty.notify({
+                    title   = popup_title,
+                    text    = summary_text(station_index),
+                    font    = weather_config[station_index].font or 'monospace',
+                    timeout = weather_config[station_index].notification_delay,
+                    screen  = capi.mouse.screen
+            })
+        end
+        local popup_leave = function() naughty.destroy(data.popup) end
 
-               local widget = wibox.widget.textbox()
+        local widget = wibox.widget.textbox()
 
-               widget:connect_signal('mouse::enter', popup_enter)
-               widget:connect_signal('mouse::leave', popup_leave)
-               if icon then
-                       icon:connect_signal('mouse::enter', popup_enter)
-                       icon:connect_signal('mouse::leave', popup_leave)
-               end
-       
-               if weather_config[station_index].command then
-                       local buttons = awful.button({}, 1, function()
-                                       awful.util.spawn(weather_config[station_index].command, true)
-                       end)
-                       widget:buttons(buttons)
-                       if icon then
-                               icon:buttons(buttons)
-                       end
-               end
+        widget:connect_signal('mouse::enter', popup_enter)
+        widget:connect_signal('mouse::leave', popup_leave)
+        if icon then
+            icon:connect_signal('mouse::enter', popup_enter)
+            icon:connect_signal('mouse::leave', popup_leave)
+        end
 
-               widgets[station_index] = widget
-               icons[station_index]   = icon
+        if weather_config[station_index].command then
+            local buttons = awful.button({}, 1, function()
+                    awful.util.spawn(weather_config[station_index].command, true)
+            end)
+            widget:buttons(buttons)
+            if icon then
+                icon:buttons(buttons)
+            end
+        end
 
-               vicious.register(widget, self, '$1', weather_config[station_index].update_interval, station_index)
-       end
-       return widgets, icons
+        widgets[station_index] = widget
+        icons[station_index]   = icon
+
+        vicious.register(widget, self, '$1', weather_config[station_index].update_interval, station_index)
+    end
+    return widgets, icons
 end
 
 -- Vicious worker function
 local function vicious_worker(format, station_index)
-       update_data(station_index)
-       update_icon(station_index)
-       local status
-       local error_status = '<span color="red">'
-       if icons[station_index] then
-               error_status = string.format('%s ', error_status);
-       end
-       error_status = string.format('%s!</span>', error_status);
-       if not weather_data[station_index] then
-               status = error_status
-               delightful.utils.print_error('weather', string.format('No weather_data[%d]', station_index))
-       else
-               if weather_data[station_index].error_string then
-                       status = '<span color="red"> !</span>';
-                       delightful.utils.print_error('weather', weather_data[station_index].error_string)
-               elseif weather_data[station_index].status_string then
-                       status = weather_data[station_index].status_string
-               else
-                       weather_data[station_index].error_string = string.format('No weather_data[%s][status_string] or weather_data[%s][error_string]', station_index, station_index)
-                       status = '<span color="red"> !</span>';
-                       delightful.utils.print_error('weather', weather_data[station_index].error_string)
-               end
-       end
-       return status
+    update_data(station_index)
+    update_icon(station_index)
+    local status
+    local error_status = '<span color="red">'
+    if icons[station_index] then
+        error_status = string.format('%s ', error_status);
+    end
+    error_status = string.format('%s!</span>', error_status);
+    if not weather_data[station_index] then
+        status = error_status
+        delightful.utils.print_error('weather', string.format('No weather_data[%d]', station_index))
+    else
+        if weather_data[station_index].error_string then
+            status = '<span color="red"> !</span>';
+            delightful.utils.print_error('weather', weather_data[station_index].error_string)
+        elseif weather_data[station_index].status_string then
+            status = weather_data[station_index].status_string
+        else
+            weather_data[station_index].error_string = string.format('No weather_data[%s][status_string] or weather_data[%s][error_string]', station_index, station_index)
+            status = '<span color="red"> !</span>';
+            delightful.utils.print_error('weather', weather_data[station_index].error_string)
+        end
+    end
+    return status
 end
 
 -- Helpers
 
 function pad_summary(line)
-       return delightful.utils.pad_string_with_spaces(line, 20)
+    return delightful.utils.pad_string_with_spaces(line, 20)
 end
 
 function calc_moon_phase(time, latitude)
-       local moon_phases = 36
-       local moon_phase, moon_latitude = weatherlib.calc_moon(time)
-       local phase = math.floor((moon_phase * moon_phases / 360) + 0.5)
+    local moon_phases = 36
+    local moon_phase, moon_latitude = weatherlib.calc_moon(time)
+    local phase = math.floor((moon_phase * moon_phases / 360) + 0.5)
 
-       if phase == moon_phases then
-               phase = 0
-       elseif phase > 0 and weatherlib.convert_angle(weatherlib.ANGLE_UNITS.RAD, weatherlib.ANGLE_UNITS.DEG, latitude) < moon_latitude then
-               -- Locations south of the moon's latitude will see the moon in the
-               -- northern sky. The moon waxes and wanes from left to right
-               -- so we reference an icon running in the opposite direction.
-               phase = moon_phases - phase
-       end
-       return math.floor(phase * 360 / moon_phases)
+    if phase == moon_phases then
+        phase = 0
+    elseif phase > 0 and weatherlib.convert_angle(weatherlib.ANGLE_UNITS.RAD, weatherlib.ANGLE_UNITS.DEG, latitude) < moon_latitude then
+        -- Locations south of the moon's latitude will see the moon in the
+        -- northern sky. The moon waxes and wanes from left to right
+        -- so we reference an icon running in the opposite direction.
+        phase = moon_phases - phase
+    end
+    return math.floor(phase * 360 / moon_phases)
 end
 
 setmetatable(_M, { __call = function(_, ...) return vicious_worker(...) end })