--------------------------------------------------------------------------- --- Apply properties to a new `naughty.notification` based on pre-determined rules. -- --@DOC_wibox_nwidget_rules_urgency_EXAMPLE@ -- -- In this example, we setup a different widget template for some music -- notifications: -- --@DOC_wibox_nwidget_rules_widget_template_EXAMPLE@ -- -- In this example, we add action to a notification that originally lacked -- them: -- --@DOC_wibox_nwidget_rules_add_actions_EXAMPLE@ -- -- Here is a list of all properties available in the `properties` section of -- a rule: -- --@DOC_notification_rules_index_COMMON@ -- -- @author Emmanuel Lepage Vallee <elv1313@gmail.com> -- @copyright 2017-2019 Emmanuel Lepage Vallee -- @ruleslib ruled.notifications --------------------------------------------------------------------------- local capi = {screen = screen, client = client, awesome = awesome} local matcher = require("gears.matcher") local gtable = require("gears.table") local gobject = require("gears.object") --- The notification is attached to the focused client. -- -- This is useful, along with other matching properties and the `ignore` -- notification property, to prevent focused application from spamming with -- useless notifications. -- --@DOC_wibox_nwidget_rules_has_focus_EXAMPLE@ -- -- @matchingproperty has_focus -- @param boolean --- The notification is attached to a client with this class. -- --@DOC_wibox_nwidget_rules_has_class_EXAMPLE@ -- -- @matchingproperty has_class -- @param string -- @see has_instance --- The notification is attached to a client with this instance name. -- --@DOC_wibox_nwidget_rules_has_instance_EXAMPLE@ -- -- @matchingproperty has_instance -- @param string -- @see has_class --- Append some actions to a notification. -- -- Using `actions` directly is destructive since it will override existing -- actions. -- -- @clientruleproperty append_actions -- @param table --- Set a fallback timeout when the notification doesn't have an explicit timeout. -- -- The value is in seconds. If none is specified, the default is 5 seconds. If -- the notification specifies its own timeout, this property will be skipped. -- -- @clientruleproperty implicit_timeout -- @param number --- Do not let this notification timeout, even if it asks for it. -- @clientruleproperty never_timeout -- @param boolean local nrules = matcher() local function client_match_common(n, prop, value) local clients = n.clients if #clients == 0 then return false end for _, c in ipairs(clients) do if c[prop] == value then return true end end return false end nrules:add_property_matcher("has_class", function(n, value) return client_match_common(n, "class", value) end) nrules:add_property_matcher("has_instance", function(n, value) return client_match_common(n, "instance", value) end) nrules:add_property_matcher("has_focus", function(n) local clients = n.clients if #clients == 0 then return false end for _, c in ipairs(clients) do if c == capi.client.focus then return true end end return false end) nrules:add_property_setter("append_actions", function(n, value) local new_actions = gtable.clone(n.actions or {}, false) n.actions = gtable.merge(new_actions, value) end) nrules:add_property_setter("implicit_timeout", function(n, value) -- Check if there is an explicit timeout. if (not n._private.timeout) and (not n._private.never_timeout) then n.timeout = value end end) nrules:add_property_setter("never_timeout", function(n, value) if value then n.timeout = 0 end end) nrules:add_property_setter("timeout", function(n, value) -- `never_timeout` has an higher priority than `timeout`. if not n._private.never_timeout then n.timeout = value end end) local module = {} gobject._setup_class_signals(module) --- Remove a source. -- @tparam string name The source name. -- @treturn boolean If the source was removed. -- @staticfct ruled.notification.remove_rule_source function module.remove_rule_source(name) return nrules:remove_matching_source(name) end --- Apply the tag rules to a client. -- -- This is useful when it is necessary to apply rules after a tag has been -- created. Many workflows can make use of "blank" tags which wont match any -- rules until later. -- -- @tparam naughty.notification n The notification. -- @staticfct ruled.notification.apply -- @noreturn function module.apply(n) local callbacks, props = {}, {} if n.preset then for k, v in pairs(n.preset) do if not n._private[v] then props[k] = v end end end for _, v in ipairs(nrules._matching_source) do v.callback(nrules, n, props, callbacks) end nrules:_execute(n, props, callbacks) end --- Add a new rule to the default set. -- @tparam table rule A valid rule. -- @staticfct ruled.notification.append_rule -- @noreturn function module.append_rule(rule) nrules:append_rule("ruled.notifications", rule) end --- Add a new rules to the default set. -- @tparam table rule A table with rules. -- @staticfct ruled.notification.append_rules -- @noreturn function module.append_rules(rules) nrules:append_rules("ruled.notifications", rules) end --- Remove a new rule to the default set. -- @tparam table rule A valid rule. -- @treturn boolean `true` if the rule was removed. -- @staticfct ruled.notification.remove_rule function module.remove_rule(rule) local ret = nrules:remove_rule("ruled.notifications", rule) module.emit_signal("rule::removed", rule) return ret end --- Add a new rule source. -- -- A rule source is a provider called when a client initially request tags. It -- allows to configure, select or create a tag (or many) to be attached to the -- client. -- -- @tparam string name The provider name. It must be unique. -- @tparam function callback The callback that is called to produce properties. -- @tparam client callback.c The client -- @tparam table callback.properties The current properties. The callback should -- add to and overwrite properties in this table -- @tparam table callback.callbacks A table of all callbacks scheduled to be -- executed after the main properties are applied. -- @tparam[opt={}] table depends_on A list of names of sources this source depends on -- (sources that must be executed *before* `name`. -- @tparam[opt={}] table precede A list of names of sources this source have a -- priority over. -- @treturn boolean Returns false if a dependency conflict was found. -- @staticfct ruled.notifications.add_rule_source function module.add_rule_source(name, cb, ...) return nrules:add_matching_function(name, cb, ...) end -- Allow to clear the rules for testing purpose only. -- Because multiple modules might set their rules, it is a bad idea to let -- them be purged under their feet. function module._clear() for k in pairs(nrules._matching_rules["ruled.notifications"]) do nrules._matching_rules["ruled.notifications"][k] = nil end end -- Add signals. local conns = gobject._setup_class_signals(module) -- First time getting a notification? Request some rules. capi.awesome.connect_signal("startup", function() if conns["request::rules"] and #conns["request::rules"] > 0 then module.emit_signal("request::rules") -- This will disable the legacy preset support. require("naughty").connect_signal("request::preset", function(n) module.apply(n) end) end end) --@DOC_rule_COMMON@ return module