summaryrefslogtreecommitdiff
path: root/awesome/lib/gears/object.lua
diff options
context:
space:
mode:
Diffstat (limited to 'awesome/lib/gears/object.lua')
-rw-r--r--awesome/lib/gears/object.lua285
1 files changed, 0 insertions, 285 deletions
diff --git a/awesome/lib/gears/object.lua b/awesome/lib/gears/object.lua
deleted file mode 100644
index e6436e3..0000000
--- a/awesome/lib/gears/object.lua
+++ /dev/null
@@ -1,285 +0,0 @@
----------------------------------------------------------------------------
--- The object oriented programming base class used by various Awesome
--- widgets and components.
---
--- It provide basic observer pattern, signaling and dynamic properties.
---
--- @author Uli Schlachter
--- @copyright 2010 Uli Schlachter
--- @classmod gears.object
----------------------------------------------------------------------------
-
-local setmetatable = setmetatable
-local pairs = pairs
-local type = type
-local error = error
-local properties = require("gears.object.properties")
-
-local object = { properties = properties, mt = {} }
-
---- Verify that obj is indeed a valid object as returned by new()
-local function check(obj)
- if type(obj) ~= "table" or type(obj._signals) ~= "table" then
- error("called on non-object")
- end
-end
-
---- Find a given signal
--- @tparam table obj The object to search in
--- @tparam string name The signal to find
--- @treturn table The signal table
-local function find_signal(obj, name)
- check(obj)
- if not obj._signals[name] then
- assert(type(name) == "string", "name must be a string, got: " .. type(name))
- obj._signals[name] = {
- strong = {},
- weak = setmetatable({}, { __mode = "kv" })
- }
- end
- return obj._signals[name]
-end
-
-function object.add_signal()
- require("awful.util").deprecate("Use signals without explicitly adding them. This is now done implicitly.")
-end
-
---- Connect to a signal.
--- @tparam string name The name of the signal
--- @tparam function func The callback to call when the signal is emitted
-function object:connect_signal(name, func)
- assert(type(func) == "function", "callback must be a function, got: " .. type(func))
- local sig = find_signal(self, name)
- assert(sig.weak[func] == nil, "Trying to connect a strong callback which is already connected weakly")
- sig.strong[func] = true
-end
-
-local function make_the_gc_obey(func)
- if _VERSION <= "Lua 5.1" then
- -- Lua 5.1 only has the behaviour we want if a userdata is used as the
- -- value in a weak table. Thus, do some magic so that we get a userdata.
-
- -- luacheck: globals newproxy getfenv setfenv
- local userdata = newproxy(true)
- getmetatable(userdata).__gc = function() end
- -- Now bind the lifetime of userdata to the lifetime of func. For this,
- -- we mess with the function's environment and add a table for all the
- -- various userdata that it should keep alive.
- local key = "_secret_key_used_by_gears_object_in_Lua51"
- local old_env = getfenv(func)
- if old_env[key] then
- -- Assume the code in the else branch added this and the function
- -- already has its own, private environment
- table.insert(old_env[key], userdata)
- else
- -- No table yet, add it
- local new_env = { [key] = { userdata } }
- setmetatable(new_env, { __index = old_env, __newindex = old_env })
- setfenv(func, new_env)
- end
- assert(_G[key] == nil, "Something broke, things escaped to _G")
- return userdata
- end
- -- Lua 5.2+ already behaves the way we want with functions directly, no magic
- return func
-end
-
---- Connect to a signal weakly. This allows the callback function to be garbage
--- collected and automatically disconnects the signal when that happens.
--- @tparam string name The name of the signal
--- @tparam function func The callback to call when the signal is emitted
-function object:weak_connect_signal(name, func)
- assert(type(func) == "function", "callback must be a function, got: " .. type(func))
- local sig = find_signal(self, name)
- assert(sig.strong[func] == nil, "Trying to connect a weak callback which is already connected strongly")
- sig.weak[func] = make_the_gc_obey(func)
-end
-
---- Disonnect to a signal.
--- @tparam string name The name of the signal
--- @tparam function func The callback that should be disconnected
-function object:disconnect_signal(name, func)
- local sig = find_signal(self, name)
- sig.weak[func] = nil
- sig.strong[func] = nil
-end
-
---- Emit a signal.
---
--- @tparam string name The name of the signal
--- @param ... Extra arguments for the callback functions. Each connected
--- function receives the object as first argument and then any extra arguments
--- that are given to emit_signal()
-function object:emit_signal(name, ...)
- local sig = find_signal(self, name)
- for func in pairs(sig.strong) do
- func(self, ...)
- end
- for func in pairs(sig.weak) do
- func(self, ...)
- end
-end
-
-local function get_miss(self, key)
- local class = rawget(self, "_class")
-
- if rawget(self, "get_"..key) then
- return rawget(self, "get_"..key)(self)
- elseif class and class["get_"..key] then
- return class["get_"..key](self)
- elseif class then
- return class[key]
- end
-
-end
-
-local function set_miss(self, key, value)
- local class = rawget(self, "_class")
-
- if rawget(self, "set_"..key) then
- return rawget(self, "set_"..key)(self, value)
- elseif class and class["set_"..key] then
- return class["set_"..key](self, value)
- elseif rawget(self, "_enable_auto_signals") then
- local changed = class[key] ~= value
- class[key] = value
-
- if changed then
- self:emit_signal("property::"..key, value)
- end
- elseif (not rawget(self, "get_"..key))
- and not (class and class["get_"..key]) then
- return rawset(self, key, value)
- else
- error("Cannot set '" .. tostring(key) .. "' on " .. tostring(self)
- .. " because it is read-only")
- end
-end
-
---- Returns a new object. You can call `:emit_signal()`, `:disconnect_signal()`
--- and `:connect_signal()` on the resulting object.
---
--- Note that `args.enable_auto_signals` is only supported when
--- `args.enable_properties` is true.
---
---
---
---
---**Usage example output**:
---
--- In get foo bar
--- bar
--- In set foo 42
--- In get foo 42
--- 42
--- In a mathod 1 2 3
--- nil
--- In the connection handler! a cow
--- a cow
---
---
--- @usage
--- -- Create a class for this object. It will be used as a backup source for
--- -- methods and accessors. It is also possible to set them directly on the
--- -- object.
---local class = {}
---function class:get_foo()
--- print('In get foo', self._foo or 'bar')
--- return self._foo or 'bar'
---end
---function class:set_foo(value)
--- print('In set foo', value)
--- -- In case it is necessary to bypass the object property system, use
--- -- `rawset`
--- rawset(self, '_foo', value)
--- -- When using custom accessors, the signals need to be handled manually
--- self:emit_signal('property::foo', value)
---end
---function class:method(a, b, c)
--- print('In a mathod', a, b, c)
---end
---local o = gears.object {
--- class = class,
--- enable_properties = true,
--- enable_auto_signals = true,
---}
---print(o.foo)
---o.foo = 42
---print(o.foo)
---o:method(1, 2, 3)
--- -- Random properties can also be added, the signal will be emitted automatically.
---o:connect_signal('property::something', function(obj, value)
--- assert(obj == o)
--- print('In the connection handler!', value)
---end)
---print(o.something)
---o.something = 'a cow'
---print(o.something)
--- @tparam[opt={}] table args The arguments
--- @tparam[opt=false] boolean args.enable_properties Automatically call getters and setters
--- @tparam[opt=false] boolean args.enable_auto_signals Generate "property::xxxx" signals
--- when an unknown property is set.
--- @tparam[opt=nil] table args.class
--- @treturn table A new object
--- @function gears.object
-local function new(args)
- args = args or {}
- local ret = {}
-
- -- Automatic signals cannot work without both miss handlers.
- assert(not (args.enable_auto_signals and args.enable_properties ~= true))
-
- -- Copy all our global functions to our new object
- for k, v in pairs(object) do
- if type(v) == "function" then
- ret[k] = v
- end
- end
-
- ret._signals = {}
-
- local mt = {}
-
- -- Look for methods in another table
- ret._class = args.class
- ret._enable_auto_signals = args.enable_auto_signals
-
- -- To catch all changes, a proxy is required
- if args.enable_auto_signals then
- ret._class = ret._class and setmetatable({}, {__index = args.class}) or {}
- end
-
- if args.enable_properties then
- -- Check got existing get_xxxx and set_xxxx
- mt.__index = get_miss
- mt.__newindex = set_miss
- elseif args.class then
- -- Use the class table a miss handler
- mt.__index = ret._class
- end
-
- return setmetatable(ret, mt)
-end
-
-function object.mt.__call(_, ...)
- return new(...)
-end
-
---- Helper function to get the module name out of `debug.getinfo`.
--- @usage
--- local mt = {}
--- mt.__tostring = function(o)
--- return require("gears.object").modulename(2)
--- end
--- return setmetatable(ret, mt)
---
--- @tparam[opt=2] integer level Level for `debug.getinfo(level, "S")`.
--- Typically 2 or 3.
--- @treturn string The module name, e.g. "wibox.container.background".
-function object.modulename(level)
- return debug.getinfo(level, "S").source:gsub(".*/lib/", ""):gsub("/", "."):gsub("%.lua", "")
-end
-
-return setmetatable(object, object.mt)
-
--- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80