Broke everything up into smaller files
This commit is contained in:
		
							
								
								
									
										809
									
								
								awesome/lib/lain/util/dkjson.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										809
									
								
								awesome/lib/lain/util/dkjson.lua
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,809 @@
 | 
			
		||||
-- Module options:
 | 
			
		||||
local always_use_lpeg = false
 | 
			
		||||
local register_global_module_table = false
 | 
			
		||||
local global_module_name = 'json'
 | 
			
		||||
 | 
			
		||||
--[==[
 | 
			
		||||
 | 
			
		||||
David Kolf's JSON module for Lua 5.1 - 5.4
 | 
			
		||||
 | 
			
		||||
Version 2.6
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
For the documentation see the corresponding readme.txt or visit
 | 
			
		||||
<http://dkolf.de/src/dkjson-lua.fsl/>.
 | 
			
		||||
 | 
			
		||||
You can contact the author by sending an e-mail to 'david' at the
 | 
			
		||||
domain 'dkolf.de'.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Copyright (C) 2010-2021 David Heiko Kolf
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining
 | 
			
		||||
a copy of this software and associated documentation files (the
 | 
			
		||||
"Software"), to deal in the Software without restriction, including
 | 
			
		||||
without limitation the rights to use, copy, modify, merge, publish,
 | 
			
		||||
distribute, sublicense, and/or sell copies of the Software, and to
 | 
			
		||||
permit persons to whom the Software is furnished to do so, subject to
 | 
			
		||||
the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be
 | 
			
		||||
included in all copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 | 
			
		||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 | 
			
		||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 | 
			
		||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 | 
			
		||||
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 | 
			
		||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 | 
			
		||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
			
		||||
SOFTWARE.
 | 
			
		||||
 | 
			
		||||
--]==]
 | 
			
		||||
 | 
			
		||||
-- global dependencies:
 | 
			
		||||
local pairs, type, tostring, tonumber, getmetatable, setmetatable =
 | 
			
		||||
	pairs, type, tostring, tonumber, getmetatable, setmetatable
 | 
			
		||||
local error, require, pcall, select = error, require, pcall, select
 | 
			
		||||
local floor, huge = math.floor, math.huge
 | 
			
		||||
local strrep, gsub, strsub, strbyte, strchar, strfind, strlen, strformat =
 | 
			
		||||
	string.rep, string.gsub, string.sub, string.byte, string.char, string.find, string.len, string.format
 | 
			
		||||
local strmatch = string.match
 | 
			
		||||
local concat = table.concat
 | 
			
		||||
 | 
			
		||||
local json = { version = 'dkjson 2.6' }
 | 
			
		||||
 | 
			
		||||
local jsonlpeg = {}
 | 
			
		||||
 | 
			
		||||
if register_global_module_table then
 | 
			
		||||
	if always_use_lpeg then
 | 
			
		||||
		_G[global_module_name] = jsonlpeg
 | 
			
		||||
	else
 | 
			
		||||
		_G[global_module_name] = json
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
_ENV = nil -- blocking globals in Lua 5.2 and later
 | 
			
		||||
 | 
			
		||||
pcall(function()
 | 
			
		||||
	-- Enable access to blocked metatables.
 | 
			
		||||
	-- Don't worry, this module doesn't change anything in them.
 | 
			
		||||
	local debmeta = require('debug').getmetatable
 | 
			
		||||
	if debmeta then
 | 
			
		||||
		getmetatable = debmeta
 | 
			
		||||
	end
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
json.null = setmetatable({}, {
 | 
			
		||||
	__tojson = function()
 | 
			
		||||
		return 'null'
 | 
			
		||||
	end,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
local function isarray(tbl)
 | 
			
		||||
	local max, n, arraylen = 0, 0, 0
 | 
			
		||||
	for k, v in pairs(tbl) do
 | 
			
		||||
		if k == 'n' and type(v) == 'number' then
 | 
			
		||||
			arraylen = v
 | 
			
		||||
			if v > max then
 | 
			
		||||
				max = v
 | 
			
		||||
			end
 | 
			
		||||
		else
 | 
			
		||||
			if type(k) ~= 'number' or k < 1 or floor(k) ~= k then
 | 
			
		||||
				return false
 | 
			
		||||
			end
 | 
			
		||||
			if k > max then
 | 
			
		||||
				max = k
 | 
			
		||||
			end
 | 
			
		||||
			n = n + 1
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
	if max > 10 and max > arraylen and max > n * 2 then
 | 
			
		||||
		return false -- don't create an array with too many holes
 | 
			
		||||
	end
 | 
			
		||||
	return true, max
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local escapecodes = {
 | 
			
		||||
	['"'] = '\\"',
 | 
			
		||||
	['\\'] = '\\\\',
 | 
			
		||||
	['\b'] = '\\b',
 | 
			
		||||
	['\f'] = '\\f',
 | 
			
		||||
	['\n'] = '\\n',
 | 
			
		||||
	['\r'] = '\\r',
 | 
			
		||||
	['\t'] = '\\t',
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
local function escapeutf8(uchar)
 | 
			
		||||
	local value = escapecodes[uchar]
 | 
			
		||||
	if value then
 | 
			
		||||
		return value
 | 
			
		||||
	end
 | 
			
		||||
	local a, b, c, d = strbyte(uchar, 1, 4)
 | 
			
		||||
	a, b, c, d = a or 0, b or 0, c or 0, d or 0
 | 
			
		||||
	if a <= 0x7f then
 | 
			
		||||
		value = a
 | 
			
		||||
	elseif 0xc0 <= a and a <= 0xdf and b >= 0x80 then
 | 
			
		||||
		value = (a - 0xc0) * 0x40 + b - 0x80
 | 
			
		||||
	elseif 0xe0 <= a and a <= 0xef and b >= 0x80 and c >= 0x80 then
 | 
			
		||||
		value = ((a - 0xe0) * 0x40 + b - 0x80) * 0x40 + c - 0x80
 | 
			
		||||
	elseif 0xf0 <= a and a <= 0xf7 and b >= 0x80 and c >= 0x80 and d >= 0x80 then
 | 
			
		||||
		value = (((a - 0xf0) * 0x40 + b - 0x80) * 0x40 + c - 0x80) * 0x40 + d - 0x80
 | 
			
		||||
	else
 | 
			
		||||
		return ''
 | 
			
		||||
	end
 | 
			
		||||
	if value <= 0xffff then
 | 
			
		||||
		return strformat('\\u%.4x', value)
 | 
			
		||||
	elseif value <= 0x10ffff then
 | 
			
		||||
		-- encode as UTF-16 surrogate pair
 | 
			
		||||
		value = value - 0x10000
 | 
			
		||||
		local highsur, lowsur = 0xD800 + floor(value / 0x400), 0xDC00 + (value % 0x400)
 | 
			
		||||
		return strformat('\\u%.4x\\u%.4x', highsur, lowsur)
 | 
			
		||||
	else
 | 
			
		||||
		return ''
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function fsub(str, pattern, repl)
 | 
			
		||||
	-- gsub always builds a new string in a buffer, even when no match
 | 
			
		||||
	-- exists. First using find should be more efficient when most strings
 | 
			
		||||
	-- don't contain the pattern.
 | 
			
		||||
	if strfind(str, pattern) then
 | 
			
		||||
		return gsub(str, pattern, repl)
 | 
			
		||||
	else
 | 
			
		||||
		return str
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function quotestring(value)
 | 
			
		||||
	-- based on the regexp "escapable" in https://github.com/douglascrockford/JSON-js
 | 
			
		||||
	value = fsub(value, '[%z\1-\31"\\\127]', escapeutf8)
 | 
			
		||||
	if strfind(value, '[\194\216\220\225\226\239]') then
 | 
			
		||||
		value = fsub(value, '\194[\128-\159\173]', escapeutf8)
 | 
			
		||||
		value = fsub(value, '\216[\128-\132]', escapeutf8)
 | 
			
		||||
		value = fsub(value, '\220\143', escapeutf8)
 | 
			
		||||
		value = fsub(value, '\225\158[\180\181]', escapeutf8)
 | 
			
		||||
		value = fsub(value, '\226\128[\140-\143\168-\175]', escapeutf8)
 | 
			
		||||
		value = fsub(value, '\226\129[\160-\175]', escapeutf8)
 | 
			
		||||
		value = fsub(value, '\239\187\191', escapeutf8)
 | 
			
		||||
		value = fsub(value, '\239\191[\176-\191]', escapeutf8)
 | 
			
		||||
	end
 | 
			
		||||
	return '"' .. value .. '"'
 | 
			
		||||
end
 | 
			
		||||
json.quotestring = quotestring
 | 
			
		||||
 | 
			
		||||
local function replace(str, o, n)
 | 
			
		||||
	local i, j = strfind(str, o, 1, true)
 | 
			
		||||
	if i then
 | 
			
		||||
		return strsub(str, 1, i - 1) .. n .. strsub(str, j + 1, -1)
 | 
			
		||||
	else
 | 
			
		||||
		return str
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- locale independent num2str and str2num functions
 | 
			
		||||
local decpoint, numfilter
 | 
			
		||||
 | 
			
		||||
local function updatedecpoint()
 | 
			
		||||
	decpoint = strmatch(tostring(0.5), '([^05+])')
 | 
			
		||||
	-- build a filter that can be used to remove group separators
 | 
			
		||||
	numfilter = '[^0-9%-%+eE' .. gsub(decpoint, '[%^%$%(%)%%%.%[%]%*%+%-%?]', '%%%0') .. ']+'
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
updatedecpoint()
 | 
			
		||||
 | 
			
		||||
local function num2str(num)
 | 
			
		||||
	return replace(fsub(tostring(num), numfilter, ''), decpoint, '.')
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function str2num(str)
 | 
			
		||||
	local num = tonumber(replace(str, '.', decpoint))
 | 
			
		||||
	if not num then
 | 
			
		||||
		updatedecpoint()
 | 
			
		||||
		num = tonumber(replace(str, '.', decpoint))
 | 
			
		||||
	end
 | 
			
		||||
	return num
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function addnewline2(level, buffer, buflen)
 | 
			
		||||
	buffer[buflen + 1] = '\n'
 | 
			
		||||
	buffer[buflen + 2] = strrep('  ', level)
 | 
			
		||||
	buflen = buflen + 2
 | 
			
		||||
	return buflen
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function json.addnewline(state)
 | 
			
		||||
	if state.indent then
 | 
			
		||||
		state.bufferlen = addnewline2(state.level or 0, state.buffer, state.bufferlen or #state.buffer)
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local encode2 -- forward declaration
 | 
			
		||||
 | 
			
		||||
local function addpair(key, value, prev, indent, level, buffer, buflen, tables, globalorder, state)
 | 
			
		||||
	local kt = type(key)
 | 
			
		||||
	if kt ~= 'string' and kt ~= 'number' then
 | 
			
		||||
		return nil, "type '" .. kt .. "' is not supported as a key by JSON."
 | 
			
		||||
	end
 | 
			
		||||
	if prev then
 | 
			
		||||
		buflen = buflen + 1
 | 
			
		||||
		buffer[buflen] = ','
 | 
			
		||||
	end
 | 
			
		||||
	if indent then
 | 
			
		||||
		buflen = addnewline2(level, buffer, buflen)
 | 
			
		||||
	end
 | 
			
		||||
	buffer[buflen + 1] = quotestring(key)
 | 
			
		||||
	buffer[buflen + 2] = ':'
 | 
			
		||||
	return encode2(value, indent, level, buffer, buflen + 2, tables, globalorder, state)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function appendcustom(res, buffer, state)
 | 
			
		||||
	local buflen = state.bufferlen
 | 
			
		||||
	if type(res) == 'string' then
 | 
			
		||||
		buflen = buflen + 1
 | 
			
		||||
		buffer[buflen] = res
 | 
			
		||||
	end
 | 
			
		||||
	return buflen
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function exception(reason, value, state, buffer, buflen, defaultmessage)
 | 
			
		||||
	defaultmessage = defaultmessage or reason
 | 
			
		||||
	local handler = state.exception
 | 
			
		||||
	if not handler then
 | 
			
		||||
		return nil, defaultmessage
 | 
			
		||||
	else
 | 
			
		||||
		state.bufferlen = buflen
 | 
			
		||||
		local ret, msg = handler(reason, value, state, defaultmessage)
 | 
			
		||||
		if not ret then
 | 
			
		||||
			return nil, msg or defaultmessage
 | 
			
		||||
		end
 | 
			
		||||
		return appendcustom(ret, buffer, state)
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function json.encodeexception(_reason, _value, _state, defaultmessage)
 | 
			
		||||
	return quotestring('<' .. defaultmessage .. '>')
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
encode2 = function(value, indent, level, buffer, buflen, tables, globalorder, state)
 | 
			
		||||
	local valtype = type(value)
 | 
			
		||||
	local valmeta = getmetatable(value)
 | 
			
		||||
	valmeta = type(valmeta) == 'table' and valmeta -- only tables
 | 
			
		||||
	local valtojson = valmeta and valmeta.__tojson
 | 
			
		||||
	if valtojson then
 | 
			
		||||
		if tables[value] then
 | 
			
		||||
			return exception('reference cycle', value, state, buffer, buflen)
 | 
			
		||||
		end
 | 
			
		||||
		tables[value] = true
 | 
			
		||||
		state.bufferlen = buflen
 | 
			
		||||
		local ret, msg = valtojson(value, state)
 | 
			
		||||
		if not ret then
 | 
			
		||||
			return exception('custom encoder failed', value, state, buffer, buflen, msg)
 | 
			
		||||
		end
 | 
			
		||||
		tables[value] = nil
 | 
			
		||||
		buflen = appendcustom(ret, buffer, state)
 | 
			
		||||
	elseif value == nil then
 | 
			
		||||
		buflen = buflen + 1
 | 
			
		||||
		buffer[buflen] = 'null'
 | 
			
		||||
	elseif valtype == 'number' then
 | 
			
		||||
		local s
 | 
			
		||||
		if value ~= value or value >= huge or -value >= huge then
 | 
			
		||||
			-- This is the behaviour of the original JSON implementation.
 | 
			
		||||
			s = 'null'
 | 
			
		||||
		else
 | 
			
		||||
			s = num2str(value)
 | 
			
		||||
		end
 | 
			
		||||
		buflen = buflen + 1
 | 
			
		||||
		buffer[buflen] = s
 | 
			
		||||
	elseif valtype == 'boolean' then
 | 
			
		||||
		buflen = buflen + 1
 | 
			
		||||
		buffer[buflen] = value and 'true' or 'false'
 | 
			
		||||
	elseif valtype == 'string' then
 | 
			
		||||
		buflen = buflen + 1
 | 
			
		||||
		buffer[buflen] = quotestring(value)
 | 
			
		||||
	elseif valtype == 'table' then
 | 
			
		||||
		if tables[value] then
 | 
			
		||||
			return exception('reference cycle', value, state, buffer, buflen)
 | 
			
		||||
		end
 | 
			
		||||
		tables[value] = true
 | 
			
		||||
		level = level + 1
 | 
			
		||||
		local isa, n = isarray(value)
 | 
			
		||||
		if n == 0 and valmeta and valmeta.__jsontype == 'object' then
 | 
			
		||||
			isa = false
 | 
			
		||||
		end
 | 
			
		||||
		local msg
 | 
			
		||||
		if isa then -- JSON array
 | 
			
		||||
			buflen = buflen + 1
 | 
			
		||||
			buffer[buflen] = '['
 | 
			
		||||
			for i = 1, n do
 | 
			
		||||
				buflen, msg = encode2(value[i], indent, level, buffer, buflen, tables, globalorder, state)
 | 
			
		||||
				if not buflen then
 | 
			
		||||
					return nil, msg
 | 
			
		||||
				end
 | 
			
		||||
				if i < n then
 | 
			
		||||
					buflen = buflen + 1
 | 
			
		||||
					buffer[buflen] = ','
 | 
			
		||||
				end
 | 
			
		||||
			end
 | 
			
		||||
			buflen = buflen + 1
 | 
			
		||||
			buffer[buflen] = ']'
 | 
			
		||||
		else -- JSON object
 | 
			
		||||
			local prev = false
 | 
			
		||||
			buflen = buflen + 1
 | 
			
		||||
			buffer[buflen] = '{'
 | 
			
		||||
			local order = valmeta and valmeta.__jsonorder or globalorder
 | 
			
		||||
			if order then
 | 
			
		||||
				local used = {}
 | 
			
		||||
				n = #order
 | 
			
		||||
				for i = 1, n do
 | 
			
		||||
					local k = order[i]
 | 
			
		||||
					local v = value[k]
 | 
			
		||||
					if v ~= nil then
 | 
			
		||||
						used[k] = true
 | 
			
		||||
						buflen, _msg = addpair(k, v, prev, indent, level, buffer, buflen, tables, globalorder, state)
 | 
			
		||||
						prev = true -- add a seperator before the next element
 | 
			
		||||
					end
 | 
			
		||||
				end
 | 
			
		||||
				for k, v in pairs(value) do
 | 
			
		||||
					if not used[k] then
 | 
			
		||||
						buflen, msg = addpair(k, v, prev, indent, level, buffer, buflen, tables, globalorder, state)
 | 
			
		||||
						if not buflen then
 | 
			
		||||
							return nil, msg
 | 
			
		||||
						end
 | 
			
		||||
						prev = true -- add a seperator before the next element
 | 
			
		||||
					end
 | 
			
		||||
				end
 | 
			
		||||
			else -- unordered
 | 
			
		||||
				for k, v in pairs(value) do
 | 
			
		||||
					buflen, msg = addpair(k, v, prev, indent, level, buffer, buflen, tables, globalorder, state)
 | 
			
		||||
					if not buflen then
 | 
			
		||||
						return nil, msg
 | 
			
		||||
					end
 | 
			
		||||
					prev = true -- add a seperator before the next element
 | 
			
		||||
				end
 | 
			
		||||
			end
 | 
			
		||||
			if indent then
 | 
			
		||||
				buflen = addnewline2(level - 1, buffer, buflen)
 | 
			
		||||
			end
 | 
			
		||||
			buflen = buflen + 1
 | 
			
		||||
			buffer[buflen] = '}'
 | 
			
		||||
		end
 | 
			
		||||
		tables[value] = nil
 | 
			
		||||
	else
 | 
			
		||||
		return exception(
 | 
			
		||||
			'unsupported type',
 | 
			
		||||
			value,
 | 
			
		||||
			state,
 | 
			
		||||
			buffer,
 | 
			
		||||
			buflen,
 | 
			
		||||
			"type '" .. valtype .. "' is not supported by JSON."
 | 
			
		||||
		)
 | 
			
		||||
	end
 | 
			
		||||
	return buflen
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function json.encode(value, state)
 | 
			
		||||
	state = state or {}
 | 
			
		||||
	local oldbuffer = state.buffer
 | 
			
		||||
	local buffer = oldbuffer or {}
 | 
			
		||||
	state.buffer = buffer
 | 
			
		||||
	updatedecpoint()
 | 
			
		||||
	local ret, msg = encode2(
 | 
			
		||||
		value,
 | 
			
		||||
		state.indent,
 | 
			
		||||
		state.level or 0,
 | 
			
		||||
		buffer,
 | 
			
		||||
		state.bufferlen or 0,
 | 
			
		||||
		state.tables or {},
 | 
			
		||||
		state.keyorder,
 | 
			
		||||
		state
 | 
			
		||||
	)
 | 
			
		||||
	if not ret then
 | 
			
		||||
		error(msg, 2)
 | 
			
		||||
	elseif oldbuffer == buffer then
 | 
			
		||||
		state.bufferlen = ret
 | 
			
		||||
		return true
 | 
			
		||||
	else
 | 
			
		||||
		state.bufferlen = nil
 | 
			
		||||
		state.buffer = nil
 | 
			
		||||
		return concat(buffer)
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function loc(str, where)
 | 
			
		||||
	local line, pos, linepos = 1, 1, 0
 | 
			
		||||
	while true do
 | 
			
		||||
		pos = strfind(str, '\n', pos, true)
 | 
			
		||||
		if pos and pos < where then
 | 
			
		||||
			line = line + 1
 | 
			
		||||
			linepos = pos
 | 
			
		||||
			pos = pos + 1
 | 
			
		||||
		else
 | 
			
		||||
			break
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
	return 'line ' .. line .. ', column ' .. (where - linepos)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function unterminated(str, what, where)
 | 
			
		||||
	return nil, strlen(str) + 1, 'unterminated ' .. what .. ' at ' .. loc(str, where)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function scanwhite(str, pos)
 | 
			
		||||
	while true do
 | 
			
		||||
		pos = strfind(str, '%S', pos)
 | 
			
		||||
		if not pos then
 | 
			
		||||
			return nil
 | 
			
		||||
		end
 | 
			
		||||
		local sub2 = strsub(str, pos, pos + 1)
 | 
			
		||||
		if sub2 == '\239\187' and strsub(str, pos + 2, pos + 2) == '\191' then
 | 
			
		||||
			-- UTF-8 Byte Order Mark
 | 
			
		||||
			pos = pos + 3
 | 
			
		||||
		elseif sub2 == '//' then
 | 
			
		||||
			pos = strfind(str, '[\n\r]', pos + 2)
 | 
			
		||||
			if not pos then
 | 
			
		||||
				return nil
 | 
			
		||||
			end
 | 
			
		||||
		elseif sub2 == '/*' then
 | 
			
		||||
			pos = strfind(str, '*/', pos + 2)
 | 
			
		||||
			if not pos then
 | 
			
		||||
				return nil
 | 
			
		||||
			end
 | 
			
		||||
			pos = pos + 2
 | 
			
		||||
		else
 | 
			
		||||
			return pos
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local escapechars = {
 | 
			
		||||
	['"'] = '"',
 | 
			
		||||
	['\\'] = '\\',
 | 
			
		||||
	['/'] = '/',
 | 
			
		||||
	['b'] = '\b',
 | 
			
		||||
	['f'] = '\f',
 | 
			
		||||
	['n'] = '\n',
 | 
			
		||||
	['r'] = '\r',
 | 
			
		||||
	['t'] = '\t',
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
local function unichar(value)
 | 
			
		||||
	if value < 0 then
 | 
			
		||||
		return nil
 | 
			
		||||
	elseif value <= 0x007f then
 | 
			
		||||
		return strchar(value)
 | 
			
		||||
	elseif value <= 0x07ff then
 | 
			
		||||
		return strchar(0xc0 + floor(value / 0x40), 0x80 + (floor(value) % 0x40))
 | 
			
		||||
	elseif value <= 0xffff then
 | 
			
		||||
		return strchar(0xe0 + floor(value / 0x1000), 0x80 + (floor(value / 0x40) % 0x40), 0x80 + (floor(value) % 0x40))
 | 
			
		||||
	elseif value <= 0x10ffff then
 | 
			
		||||
		return strchar(
 | 
			
		||||
			0xf0 + floor(value / 0x40000),
 | 
			
		||||
			0x80 + (floor(value / 0x1000) % 0x40),
 | 
			
		||||
			0x80 + (floor(value / 0x40) % 0x40),
 | 
			
		||||
			0x80 + (floor(value) % 0x40)
 | 
			
		||||
		)
 | 
			
		||||
	else
 | 
			
		||||
		return nil
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function scanstring(str, pos)
 | 
			
		||||
	local lastpos = pos + 1
 | 
			
		||||
	local buffer, n = {}, 0
 | 
			
		||||
	while true do
 | 
			
		||||
		local nextpos = strfind(str, '["\\]', lastpos)
 | 
			
		||||
		if not nextpos then
 | 
			
		||||
			return unterminated(str, 'string', pos)
 | 
			
		||||
		end
 | 
			
		||||
		if nextpos > lastpos then
 | 
			
		||||
			n = n + 1
 | 
			
		||||
			buffer[n] = strsub(str, lastpos, nextpos - 1)
 | 
			
		||||
		end
 | 
			
		||||
		if strsub(str, nextpos, nextpos) == '"' then
 | 
			
		||||
			lastpos = nextpos + 1
 | 
			
		||||
			break
 | 
			
		||||
		else
 | 
			
		||||
			local escchar = strsub(str, nextpos + 1, nextpos + 1)
 | 
			
		||||
			local value
 | 
			
		||||
			if escchar == 'u' then
 | 
			
		||||
				value = tonumber(strsub(str, nextpos + 2, nextpos + 5), 16)
 | 
			
		||||
				if value then
 | 
			
		||||
					local value2
 | 
			
		||||
					if 0xD800 <= value and value <= 0xDBff then
 | 
			
		||||
						-- we have the high surrogate of UTF-16. Check if there is a
 | 
			
		||||
						-- low surrogate escaped nearby to combine them.
 | 
			
		||||
						if strsub(str, nextpos + 6, nextpos + 7) == '\\u' then
 | 
			
		||||
							value2 = tonumber(strsub(str, nextpos + 8, nextpos + 11), 16)
 | 
			
		||||
							if value2 and 0xDC00 <= value2 and value2 <= 0xDFFF then
 | 
			
		||||
								value = (value - 0xD800) * 0x400 + (value2 - 0xDC00) + 0x10000
 | 
			
		||||
							else
 | 
			
		||||
								value2 = nil -- in case it was out of range for a low surrogate
 | 
			
		||||
							end
 | 
			
		||||
						end
 | 
			
		||||
					end
 | 
			
		||||
					value = value and unichar(value)
 | 
			
		||||
					if value then
 | 
			
		||||
						if value2 then
 | 
			
		||||
							lastpos = nextpos + 12
 | 
			
		||||
						else
 | 
			
		||||
							lastpos = nextpos + 6
 | 
			
		||||
						end
 | 
			
		||||
					end
 | 
			
		||||
				end
 | 
			
		||||
			end
 | 
			
		||||
			if not value then
 | 
			
		||||
				value = escapechars[escchar] or escchar
 | 
			
		||||
				lastpos = nextpos + 2
 | 
			
		||||
			end
 | 
			
		||||
			n = n + 1
 | 
			
		||||
			buffer[n] = value
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
	if n == 1 then
 | 
			
		||||
		return buffer[1], lastpos
 | 
			
		||||
	elseif n > 1 then
 | 
			
		||||
		return concat(buffer), lastpos
 | 
			
		||||
	else
 | 
			
		||||
		return '', lastpos
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local scanvalue -- forward declaration
 | 
			
		||||
 | 
			
		||||
local function scantable(what, closechar, str, startpos, nullval, objectmeta, arraymeta)
 | 
			
		||||
	local tbl, n = {}, 0
 | 
			
		||||
	local pos = startpos + 1
 | 
			
		||||
	if what == 'object' then
 | 
			
		||||
		setmetatable(tbl, objectmeta)
 | 
			
		||||
	else
 | 
			
		||||
		setmetatable(tbl, arraymeta)
 | 
			
		||||
	end
 | 
			
		||||
	while true do
 | 
			
		||||
		pos = scanwhite(str, pos)
 | 
			
		||||
		if not pos then
 | 
			
		||||
			return unterminated(str, what, startpos)
 | 
			
		||||
		end
 | 
			
		||||
		local char = strsub(str, pos, pos)
 | 
			
		||||
		if char == closechar then
 | 
			
		||||
			return tbl, pos + 1
 | 
			
		||||
		end
 | 
			
		||||
		local val1, err
 | 
			
		||||
		val1, pos, err = scanvalue(str, pos, nullval, objectmeta, arraymeta)
 | 
			
		||||
		if err then
 | 
			
		||||
			return nil, pos, err
 | 
			
		||||
		end
 | 
			
		||||
		pos = scanwhite(str, pos)
 | 
			
		||||
		if not pos then
 | 
			
		||||
			return unterminated(str, what, startpos)
 | 
			
		||||
		end
 | 
			
		||||
		char = strsub(str, pos, pos)
 | 
			
		||||
		if char == ':' then
 | 
			
		||||
			if val1 == nil then
 | 
			
		||||
				return nil, pos, 'cannot use nil as table index (at ' .. loc(str, pos) .. ')'
 | 
			
		||||
			end
 | 
			
		||||
			pos = scanwhite(str, pos + 1)
 | 
			
		||||
			if not pos then
 | 
			
		||||
				return unterminated(str, what, startpos)
 | 
			
		||||
			end
 | 
			
		||||
			local val2
 | 
			
		||||
			val2, pos, err = scanvalue(str, pos, nullval, objectmeta, arraymeta)
 | 
			
		||||
			if err then
 | 
			
		||||
				return nil, pos, err
 | 
			
		||||
			end
 | 
			
		||||
			tbl[val1] = val2
 | 
			
		||||
			pos = scanwhite(str, pos)
 | 
			
		||||
			if not pos then
 | 
			
		||||
				return unterminated(str, what, startpos)
 | 
			
		||||
			end
 | 
			
		||||
			char = strsub(str, pos, pos)
 | 
			
		||||
		else
 | 
			
		||||
			n = n + 1
 | 
			
		||||
			tbl[n] = val1
 | 
			
		||||
		end
 | 
			
		||||
		if char == ',' then
 | 
			
		||||
			pos = pos + 1
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
scanvalue = function(str, pos, nullval, objectmeta, arraymeta)
 | 
			
		||||
	pos = pos or 1
 | 
			
		||||
	pos = scanwhite(str, pos)
 | 
			
		||||
	if not pos then
 | 
			
		||||
		return nil, strlen(str) + 1, 'no valid JSON value (reached the end)'
 | 
			
		||||
	end
 | 
			
		||||
	local char = strsub(str, pos, pos)
 | 
			
		||||
	if char == '{' then
 | 
			
		||||
		return scantable('object', '}', str, pos, nullval, objectmeta, arraymeta)
 | 
			
		||||
	elseif char == '[' then
 | 
			
		||||
		return scantable('array', ']', str, pos, nullval, objectmeta, arraymeta)
 | 
			
		||||
	elseif char == '"' then
 | 
			
		||||
		return scanstring(str, pos)
 | 
			
		||||
	else
 | 
			
		||||
		local pstart, pend = strfind(str, '^%-?[%d%.]+[eE]?[%+%-]?%d*', pos)
 | 
			
		||||
		if pstart then
 | 
			
		||||
			local number = str2num(strsub(str, pstart, pend))
 | 
			
		||||
			if number then
 | 
			
		||||
				return number, pend + 1
 | 
			
		||||
			end
 | 
			
		||||
		end
 | 
			
		||||
		pstart, pend = strfind(str, '^%a%w*', pos)
 | 
			
		||||
		if pstart then
 | 
			
		||||
			local name = strsub(str, pstart, pend)
 | 
			
		||||
			if name == 'true' then
 | 
			
		||||
				return true, pend + 1
 | 
			
		||||
			elseif name == 'false' then
 | 
			
		||||
				return false, pend + 1
 | 
			
		||||
			elseif name == 'null' then
 | 
			
		||||
				return nullval, pend + 1
 | 
			
		||||
			end
 | 
			
		||||
		end
 | 
			
		||||
		return nil, pos, 'no valid JSON value at ' .. loc(str, pos)
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function optionalmetatables(...)
 | 
			
		||||
	if select('#', ...) > 0 then
 | 
			
		||||
		return ...
 | 
			
		||||
	else
 | 
			
		||||
		return { __jsontype = 'object' }, { __jsontype = 'array' }
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function json.decode(str, pos, nullval, ...)
 | 
			
		||||
	local objectmeta, arraymeta = optionalmetatables(...)
 | 
			
		||||
	return scanvalue(str, pos, nullval, objectmeta, arraymeta)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function json.use_lpeg()
 | 
			
		||||
	local g = require('lpeg')
 | 
			
		||||
 | 
			
		||||
	if g.version() == '0.11' then
 | 
			
		||||
		error('due to a bug in LPeg 0.11, it cannot be used for JSON matching')
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	local pegmatch = g.match
 | 
			
		||||
	local P, S, R = g.P, g.S, g.R
 | 
			
		||||
 | 
			
		||||
	local function ErrorCall(str, pos, msg, state)
 | 
			
		||||
		if not state.msg then
 | 
			
		||||
			state.msg = msg .. ' at ' .. loc(str, pos)
 | 
			
		||||
			state.pos = pos
 | 
			
		||||
		end
 | 
			
		||||
		return false
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	local function Err(msg)
 | 
			
		||||
		return g.Cmt(g.Cc(msg) * g.Carg(2), ErrorCall)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	local function ErrorUnterminatedCall(str, pos, what, state)
 | 
			
		||||
		return ErrorCall(str, pos - 1, 'unterminated ' .. what, state)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	local SingleLineComment = P('//') * (1 - S('\n\r')) ^ 0
 | 
			
		||||
	local MultiLineComment = P('/*') * (1 - P('*/')) ^ 0 * P('*/')
 | 
			
		||||
	local Space = (S(' \n\r\t') + P('\239\187\191') + SingleLineComment + MultiLineComment) ^ 0
 | 
			
		||||
 | 
			
		||||
	local function ErrUnterminated(what)
 | 
			
		||||
		return g.Cmt(g.Cc(what) * g.Carg(2), ErrorUnterminatedCall)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	local PlainChar = 1 - S('"\\\n\r')
 | 
			
		||||
	local EscapeSequence = (P('\\') * g.C(S('"\\/bfnrt') + Err('unsupported escape sequence'))) / escapechars
 | 
			
		||||
	local HexDigit = R('09', 'af', 'AF')
 | 
			
		||||
	local function UTF16Surrogate(_match, _pos, high, low)
 | 
			
		||||
		high, low = tonumber(high, 16), tonumber(low, 16)
 | 
			
		||||
		if 0xD800 <= high and high <= 0xDBff and 0xDC00 <= low and low <= 0xDFFF then
 | 
			
		||||
			return true, unichar((high - 0xD800) * 0x400 + (low - 0xDC00) + 0x10000)
 | 
			
		||||
		else
 | 
			
		||||
			return false
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
	local function UTF16BMP(hex)
 | 
			
		||||
		return unichar(tonumber(hex, 16))
 | 
			
		||||
	end
 | 
			
		||||
	local U16Sequence = (P('\\u') * g.C(HexDigit * HexDigit * HexDigit * HexDigit))
 | 
			
		||||
	local UnicodeEscape = g.Cmt(U16Sequence * U16Sequence, UTF16Surrogate) + U16Sequence / UTF16BMP
 | 
			
		||||
	local Char = UnicodeEscape + EscapeSequence + PlainChar
 | 
			
		||||
	local String = P('"') * (g.Cs(Char ^ 0) * P('"') + ErrUnterminated('string'))
 | 
			
		||||
	local Integer = P('-') ^ -1 * (P('0') + (R('19') * R('09') ^ 0))
 | 
			
		||||
	local Fractal = P('.') * R('09') ^ 0
 | 
			
		||||
	local Exponent = (S('eE')) * (S('+-')) ^ -1 * R('09') ^ 1
 | 
			
		||||
	local Number = (Integer * Fractal ^ -1 * Exponent ^ -1) / str2num
 | 
			
		||||
	local Constant = P('true') * g.Cc(true) + P('false') * g.Cc(false) + P('null') * g.Carg(1)
 | 
			
		||||
	local SimpleValue = Number + String + Constant
 | 
			
		||||
	local ArrayContent, ObjectContent
 | 
			
		||||
 | 
			
		||||
	-- The functions parsearray and parseobject parse only a single value/pair
 | 
			
		||||
	-- at a time and store them directly to avoid hitting the LPeg limits.
 | 
			
		||||
	local function parsearray(str, pos, nullval, state)
 | 
			
		||||
		local obj, cont
 | 
			
		||||
		local start = pos
 | 
			
		||||
		local npos
 | 
			
		||||
		local t, nt = {}, 0
 | 
			
		||||
		repeat
 | 
			
		||||
			obj, cont, npos = pegmatch(ArrayContent, str, pos, nullval, state)
 | 
			
		||||
			if cont == 'end' then
 | 
			
		||||
				return ErrorUnterminatedCall(str, start, 'array', state)
 | 
			
		||||
			end
 | 
			
		||||
			pos = npos
 | 
			
		||||
			if cont == 'cont' or cont == 'last' then
 | 
			
		||||
				nt = nt + 1
 | 
			
		||||
				t[nt] = obj
 | 
			
		||||
			end
 | 
			
		||||
		until cont ~= 'cont'
 | 
			
		||||
		return pos, setmetatable(t, state.arraymeta)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	local function parseobject(str, pos, nullval, state)
 | 
			
		||||
		local obj, key, cont
 | 
			
		||||
		local start = pos
 | 
			
		||||
		local npos
 | 
			
		||||
		local t = {}
 | 
			
		||||
		repeat
 | 
			
		||||
			key, obj, cont, npos = pegmatch(ObjectContent, str, pos, nullval, state)
 | 
			
		||||
			if cont == 'end' then
 | 
			
		||||
				return ErrorUnterminatedCall(str, start, 'object', state)
 | 
			
		||||
			end
 | 
			
		||||
			pos = npos
 | 
			
		||||
			if cont == 'cont' or cont == 'last' then
 | 
			
		||||
				t[key] = obj
 | 
			
		||||
			end
 | 
			
		||||
		until cont ~= 'cont'
 | 
			
		||||
		return pos, setmetatable(t, state.objectmeta)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	local Array = P('[') * g.Cmt(g.Carg(1) * g.Carg(2), parsearray)
 | 
			
		||||
	local Object = P('{') * g.Cmt(g.Carg(1) * g.Carg(2), parseobject)
 | 
			
		||||
	local Value = Space * (Array + Object + SimpleValue)
 | 
			
		||||
	local ExpectedValue = Value + Space * Err('value expected')
 | 
			
		||||
	local ExpectedKey = String + Err('key expected')
 | 
			
		||||
	local End = P(-1) * g.Cc('end')
 | 
			
		||||
	local ErrInvalid = Err('invalid JSON')
 | 
			
		||||
	ArrayContent = (
 | 
			
		||||
		Value * Space * (P(',') * g.Cc('cont') + P(']') * g.Cc('last') + End + ErrInvalid)
 | 
			
		||||
		+ g.Cc(nil) * (P(']') * g.Cc('empty') + End + ErrInvalid)
 | 
			
		||||
	) * g.Cp()
 | 
			
		||||
	local Pair = g.Cg(Space * ExpectedKey * Space * (P(':') + Err('colon expected')) * ExpectedValue)
 | 
			
		||||
	ObjectContent = (
 | 
			
		||||
		g.Cc(nil) * g.Cc(nil) * P('}') * g.Cc('empty')
 | 
			
		||||
		+ End
 | 
			
		||||
		+ (Pair * Space * (P(',') * g.Cc('cont') + P('}') * g.Cc('last') + End + ErrInvalid) + ErrInvalid)
 | 
			
		||||
	) * g.Cp()
 | 
			
		||||
	local DecodeValue = ExpectedValue * g.Cp()
 | 
			
		||||
 | 
			
		||||
	jsonlpeg.version = json.version
 | 
			
		||||
	jsonlpeg.encode = json.encode
 | 
			
		||||
	jsonlpeg.null = json.null
 | 
			
		||||
	jsonlpeg.quotestring = json.quotestring
 | 
			
		||||
	jsonlpeg.addnewline = json.addnewline
 | 
			
		||||
	jsonlpeg.encodeexception = json.encodeexception
 | 
			
		||||
	jsonlpeg.using_lpeg = true
 | 
			
		||||
 | 
			
		||||
	function jsonlpeg.decode(str, pos, nullval, ...)
 | 
			
		||||
		local state = {}
 | 
			
		||||
		state.objectmeta, state.arraymeta = optionalmetatables(...)
 | 
			
		||||
		local obj, retpos = pegmatch(DecodeValue, str, pos, nullval, state)
 | 
			
		||||
		if state.msg then
 | 
			
		||||
			return nil, state.pos, state.msg
 | 
			
		||||
		else
 | 
			
		||||
			return obj, retpos
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	-- cache result of this function:
 | 
			
		||||
	json.use_lpeg = function()
 | 
			
		||||
		return jsonlpeg
 | 
			
		||||
	end
 | 
			
		||||
	jsonlpeg.use_lpeg = json.use_lpeg
 | 
			
		||||
 | 
			
		||||
	return jsonlpeg
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
if always_use_lpeg then
 | 
			
		||||
	return json.use_lpeg()
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return json
 | 
			
		||||
							
								
								
									
										210
									
								
								awesome/lib/lain/util/init.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										210
									
								
								awesome/lib/lain/util/init.lua
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,210 @@
 | 
			
		||||
--[[
 | 
			
		||||
 | 
			
		||||
     Lain
 | 
			
		||||
     Layouts, widgets and utilities for Awesome WM
 | 
			
		||||
 | 
			
		||||
     Utilities section
 | 
			
		||||
 | 
			
		||||
     Licensed under GNU General Public License v2
 | 
			
		||||
      * (c) 2013,      Luca CPZ
 | 
			
		||||
      * (c) 2010-2012, Peter Hofmann
 | 
			
		||||
 | 
			
		||||
--]]
 | 
			
		||||
 | 
			
		||||
local awful = require('awful')
 | 
			
		||||
local sqrt = math.sqrt
 | 
			
		||||
local pairs = pairs
 | 
			
		||||
local client = client
 | 
			
		||||
local tonumber = tonumber
 | 
			
		||||
local wrequire = require('lain.helpers').wrequire
 | 
			
		||||
local setmetatable = setmetatable
 | 
			
		||||
 | 
			
		||||
-- Lain utilities submodule
 | 
			
		||||
-- lain.util
 | 
			
		||||
local util = { _NAME = 'lain.util' }
 | 
			
		||||
 | 
			
		||||
-- Like awful.menu.clients, but only show clients of currently selected tags
 | 
			
		||||
function util.menu_clients_current_tags(menu, args)
 | 
			
		||||
	-- List of currently selected tags.
 | 
			
		||||
	local cls_tags = awful.screen.focused().selected_tags
 | 
			
		||||
 | 
			
		||||
	if cls_tags == nil then
 | 
			
		||||
		return nil
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	-- Final list of menu items.
 | 
			
		||||
	local cls_t = {}
 | 
			
		||||
 | 
			
		||||
	-- For each selected tag get all clients of that tag and add them to
 | 
			
		||||
	-- the menu. A click on a menu item will raise that client.
 | 
			
		||||
	for i = 1, #cls_tags do
 | 
			
		||||
		local t = cls_tags[i]
 | 
			
		||||
		local cls = t:clients()
 | 
			
		||||
 | 
			
		||||
		for _, c in pairs(cls) do
 | 
			
		||||
			cls_t[#cls_t + 1] = {
 | 
			
		||||
				awful.util.escape(c.name) or '',
 | 
			
		||||
				function()
 | 
			
		||||
					c.minimized = false
 | 
			
		||||
					client.focus = c
 | 
			
		||||
					c:raise()
 | 
			
		||||
				end,
 | 
			
		||||
				c.icon,
 | 
			
		||||
			}
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	-- No clients? Then quit.
 | 
			
		||||
	if #cls_t <= 0 then
 | 
			
		||||
		return nil
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	-- menu may contain some predefined values, otherwise start with a
 | 
			
		||||
	-- fresh menu.
 | 
			
		||||
	if not menu then
 | 
			
		||||
		menu = {}
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	-- Set the list of items and show the menu.
 | 
			
		||||
	menu.items = cls_t
 | 
			
		||||
	local m = awful.menu(menu)
 | 
			
		||||
	m:show(args)
 | 
			
		||||
 | 
			
		||||
	return m
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Magnify a client: set it to "float" and resize it.
 | 
			
		||||
function util.magnify_client(c, width_f, height_f)
 | 
			
		||||
	if c and not c.floating then
 | 
			
		||||
		util.magnified_client = c
 | 
			
		||||
		util.mc(c, width_f, height_f)
 | 
			
		||||
	else
 | 
			
		||||
		util.magnified_client = nil
 | 
			
		||||
		c.floating = false
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- https://github.com/lcpz/lain/issues/195
 | 
			
		||||
function util.mc(c, width_f, height_f)
 | 
			
		||||
	c = c or util.magnified_client
 | 
			
		||||
	if not c then
 | 
			
		||||
		return
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	c.floating = true
 | 
			
		||||
	local s = awful.screen.focused()
 | 
			
		||||
	local mg = s.workarea
 | 
			
		||||
	local g = {}
 | 
			
		||||
	local mwfact = width_f or s.selected_tag.master_width_factor or 0.5
 | 
			
		||||
	g.width = sqrt(mwfact) * mg.width
 | 
			
		||||
	g.height = sqrt(height_f or mwfact) * mg.height
 | 
			
		||||
	g.x = mg.x + (mg.width - g.width) / 2
 | 
			
		||||
	g.y = mg.y + (mg.height - g.height) / 2
 | 
			
		||||
 | 
			
		||||
	if c then
 | 
			
		||||
		c:geometry(g)
 | 
			
		||||
	end -- if c is still a valid object
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Non-empty tag browsing
 | 
			
		||||
-- direction in {-1, 1} <-> {previous, next} non-empty tag
 | 
			
		||||
function util.tag_view_nonempty(direction, sc)
 | 
			
		||||
	direction = direction or 1
 | 
			
		||||
	local s = sc or awful.screen.focused()
 | 
			
		||||
	local tags = s.tags
 | 
			
		||||
	local sel = s.selected_tag
 | 
			
		||||
 | 
			
		||||
	local i = sel.index
 | 
			
		||||
	repeat
 | 
			
		||||
		i = i + direction
 | 
			
		||||
 | 
			
		||||
		-- Wrap around when we reach one of the bounds
 | 
			
		||||
		if i > #tags then
 | 
			
		||||
			i = i - #tags
 | 
			
		||||
		end
 | 
			
		||||
		if i < 1 then
 | 
			
		||||
			i = i + #tags
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		local t = tags[i]
 | 
			
		||||
 | 
			
		||||
		-- Stop when we get back to where we started
 | 
			
		||||
		if t == sel then
 | 
			
		||||
			break
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		-- If it's The One, view it.
 | 
			
		||||
		if #t:clients() > 0 then
 | 
			
		||||
			t:view_only()
 | 
			
		||||
			return
 | 
			
		||||
		end
 | 
			
		||||
	until false
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- {{{ Dynamic tagging
 | 
			
		||||
 | 
			
		||||
-- Add a new tag
 | 
			
		||||
function util.add_tag(layout)
 | 
			
		||||
	awful.prompt.run({
 | 
			
		||||
		prompt = 'New tag name: ',
 | 
			
		||||
		textbox = awful.screen.focused().mypromptbox.widget,
 | 
			
		||||
		exe_callback = function(name)
 | 
			
		||||
			if not name or #name == 0 then
 | 
			
		||||
				return
 | 
			
		||||
			end
 | 
			
		||||
			awful.tag
 | 
			
		||||
				.add(name, { screen = awful.screen.focused(), layout = layout or awful.layout.suit.tile })
 | 
			
		||||
				:view_only()
 | 
			
		||||
		end,
 | 
			
		||||
	})
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Rename current tag
 | 
			
		||||
function util.rename_tag()
 | 
			
		||||
	awful.prompt.run({
 | 
			
		||||
		prompt = 'Rename tag: ',
 | 
			
		||||
		textbox = awful.screen.focused().mypromptbox.widget,
 | 
			
		||||
		exe_callback = function(new_name)
 | 
			
		||||
			if not new_name or #new_name == 0 then
 | 
			
		||||
				return
 | 
			
		||||
			end
 | 
			
		||||
			local t = awful.screen.focused().selected_tag
 | 
			
		||||
			if t then
 | 
			
		||||
				t.name = new_name
 | 
			
		||||
			end
 | 
			
		||||
		end,
 | 
			
		||||
	})
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Move current tag
 | 
			
		||||
-- pos in {-1, 1} <-> {previous, next} tag position
 | 
			
		||||
function util.move_tag(pos)
 | 
			
		||||
	local tag = awful.screen.focused().selected_tag
 | 
			
		||||
	if tonumber(pos) <= -1 then
 | 
			
		||||
		awful.tag.move(tag.index - 1, tag)
 | 
			
		||||
	else
 | 
			
		||||
		awful.tag.move(tag.index + 1, tag)
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Delete current tag
 | 
			
		||||
-- Any rule set on the tag shall be broken
 | 
			
		||||
function util.delete_tag()
 | 
			
		||||
	local t = awful.screen.focused().selected_tag
 | 
			
		||||
	if not t then
 | 
			
		||||
		return
 | 
			
		||||
	end
 | 
			
		||||
	t:delete()
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- }}}
 | 
			
		||||
 | 
			
		||||
-- On the fly useless gaps change
 | 
			
		||||
function util.useless_gaps_resize(thatmuch, s, t)
 | 
			
		||||
	local scr = s or awful.screen.focused()
 | 
			
		||||
	local tag = t or scr.selected_tag
 | 
			
		||||
	tag.gap = tag.gap + tonumber(thatmuch)
 | 
			
		||||
	awful.layout.arrange(scr)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return setmetatable(util, { __index = wrequire })
 | 
			
		||||
							
								
								
									
										92
									
								
								awesome/lib/lain/util/markup.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								awesome/lib/lain/util/markup.lua
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,92 @@
 | 
			
		||||
--[[
 | 
			
		||||
 | 
			
		||||
     Licensed under MIT License
 | 
			
		||||
      * (c) 2013, Luca CPZ
 | 
			
		||||
      * (c) 2009, Uli Schlachter
 | 
			
		||||
      * (c) 2009, Majic
 | 
			
		||||
 | 
			
		||||
--]]
 | 
			
		||||
 | 
			
		||||
local format = string.format
 | 
			
		||||
local setmetatable = setmetatable
 | 
			
		||||
 | 
			
		||||
-- Lain markup util submodule
 | 
			
		||||
-- lain.util.markup
 | 
			
		||||
local markup = { fg = {}, bg = {} }
 | 
			
		||||
 | 
			
		||||
-- Convenience tags
 | 
			
		||||
function markup.bold(text)
 | 
			
		||||
	return format('<b>%s</b>', text)
 | 
			
		||||
end
 | 
			
		||||
function markup.italic(text)
 | 
			
		||||
	return format('<i>%s</i>', text)
 | 
			
		||||
end
 | 
			
		||||
function markup.strike(text)
 | 
			
		||||
	return format('<s>%s</s>', text)
 | 
			
		||||
end
 | 
			
		||||
function markup.underline(text)
 | 
			
		||||
	return format('<u>%s</u>', text)
 | 
			
		||||
end
 | 
			
		||||
function markup.monospace(text)
 | 
			
		||||
	return format('<tt>%s</tt>', text)
 | 
			
		||||
end
 | 
			
		||||
function markup.big(text)
 | 
			
		||||
	return format('<big>%s</big>', text)
 | 
			
		||||
end
 | 
			
		||||
function markup.small(text)
 | 
			
		||||
	return format('<small>%s</small>', text)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Set the font
 | 
			
		||||
function markup.font(font, text)
 | 
			
		||||
	return format("<span font='%s'>%s</span>", font, text)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Set the foreground
 | 
			
		||||
function markup.fg.color(color, text)
 | 
			
		||||
	return format("<span foreground='%s'>%s</span>", color, text)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Set the background
 | 
			
		||||
function markup.bg.color(color, text)
 | 
			
		||||
	return format("<span background='%s'>%s</span>", color, text)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Set foreground and background
 | 
			
		||||
function markup.color(fg, bg, text)
 | 
			
		||||
	return format("<span foreground='%s' background='%s'>%s</span>", fg, bg, text)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Set font and foreground
 | 
			
		||||
function markup.fontfg(font, fg, text)
 | 
			
		||||
	return format("<span font='%s' foreground='%s'>%s</span>", font, fg, text)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Set font and background
 | 
			
		||||
function markup.fontbg(font, bg, text)
 | 
			
		||||
	return format("<span font='%s' background='%s'>%s</span>", font, bg, text)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Set font, foreground and background
 | 
			
		||||
function markup.fontcolor(font, fg, bg, text)
 | 
			
		||||
	return format("<span font='%s' foreground='%s' background='%s'>%s</span>", font, fg, bg, text)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- link markup.{fg,bg}(...) calls to markup.{fg,bg}.color(...)
 | 
			
		||||
setmetatable(markup.fg, {
 | 
			
		||||
	__call = function(_, ...)
 | 
			
		||||
		return markup.fg.color(...)
 | 
			
		||||
	end,
 | 
			
		||||
})
 | 
			
		||||
setmetatable(markup.bg, {
 | 
			
		||||
	__call = function(_, ...)
 | 
			
		||||
		return markup.bg.color(...)
 | 
			
		||||
	end,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
-- link markup(...) calls to markup.fg.color(...)
 | 
			
		||||
return setmetatable(markup, {
 | 
			
		||||
	__call = function(_, ...)
 | 
			
		||||
		return markup.fg.color(...)
 | 
			
		||||
	end,
 | 
			
		||||
})
 | 
			
		||||
							
								
								
									
										151
									
								
								awesome/lib/lain/util/menu_iterator.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								awesome/lib/lain/util/menu_iterator.lua
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,151 @@
 | 
			
		||||
--[[
 | 
			
		||||
 | 
			
		||||
     Licensed under GNU General Public License v2
 | 
			
		||||
      * (c) 2017, Simon Désaulniers <sim.desaulniers@gmail.com>
 | 
			
		||||
      * (c) 2017, Uli Schlachter
 | 
			
		||||
      * (c) 2017, Jeferson Siqueira <jefersonlsiq@gmail.com>
 | 
			
		||||
 | 
			
		||||
--]]
 | 
			
		||||
 | 
			
		||||
-- Menu iterator with Naughty notifications
 | 
			
		||||
-- lain.util.menu_iterator
 | 
			
		||||
 | 
			
		||||
local naughty = require('naughty')
 | 
			
		||||
local helpers = require('lain.helpers')
 | 
			
		||||
local atable = require('awful.util').table
 | 
			
		||||
local assert = assert
 | 
			
		||||
local pairs = pairs
 | 
			
		||||
local tconcat = table.concat
 | 
			
		||||
local unpack = unpack or table.unpack -- lua 5.1 retro-compatibility
 | 
			
		||||
 | 
			
		||||
local state = { cid = nil }
 | 
			
		||||
 | 
			
		||||
local function naughty_destroy_callback(reason)
 | 
			
		||||
	local closed = naughty.notificationClosedReason
 | 
			
		||||
	if reason == closed.expired or reason == closed.dismissedByUser then
 | 
			
		||||
		local actions = state.index and state.menu[state.index - 1][2]
 | 
			
		||||
		if actions then
 | 
			
		||||
			for _, action in pairs(actions) do
 | 
			
		||||
				-- don't try to call nil callbacks
 | 
			
		||||
				if action then
 | 
			
		||||
					action()
 | 
			
		||||
				end
 | 
			
		||||
			end
 | 
			
		||||
			state.index = nil
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Iterates over a menu.
 | 
			
		||||
-- After the timeout, callbacks associated to the last visited choice are
 | 
			
		||||
-- executed. Inputs:
 | 
			
		||||
-- * menu:    a list of {label, {callbacks}} pairs
 | 
			
		||||
-- * timeout: time to wait before confirming the menu selection
 | 
			
		||||
-- * icon:    icon to display in the notification of the chosen label
 | 
			
		||||
local function iterate(menu, timeout, icon)
 | 
			
		||||
	timeout = timeout or 4 -- default timeout for each menu entry
 | 
			
		||||
	icon = icon or nil -- icon to display on the menu
 | 
			
		||||
 | 
			
		||||
	-- Build the list of choices
 | 
			
		||||
	if not state.index then
 | 
			
		||||
		state.menu = menu
 | 
			
		||||
		state.index = 1
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	-- Select one and display the appropriate notification
 | 
			
		||||
	local label
 | 
			
		||||
	local next = state.menu[state.index]
 | 
			
		||||
	state.index = state.index + 1
 | 
			
		||||
 | 
			
		||||
	if not next then
 | 
			
		||||
		label = 'Cancel'
 | 
			
		||||
		state.index = nil
 | 
			
		||||
	else
 | 
			
		||||
		label, _ = unpack(next)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	state.cid = naughty.notify({
 | 
			
		||||
		text = label,
 | 
			
		||||
		icon = icon,
 | 
			
		||||
		timeout = timeout,
 | 
			
		||||
		screen = mouse.screen,
 | 
			
		||||
		replaces_id = state.cid,
 | 
			
		||||
		destroy = naughty_destroy_callback,
 | 
			
		||||
	}).id
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Generates a menu compatible with the first argument of `iterate` function and
 | 
			
		||||
-- suitable for the following cases:
 | 
			
		||||
-- * all possible choices individually (partition of singletons);
 | 
			
		||||
-- * all possible subsets of the set of choices (powerset).
 | 
			
		||||
--
 | 
			
		||||
-- Inputs:
 | 
			
		||||
-- * args: an array containing the following members:
 | 
			
		||||
--   * choices:       Array of choices (string) on which the menu will be
 | 
			
		||||
--                    generated.
 | 
			
		||||
--   * name:          Displayed name of the menu (in the form "name: choices").
 | 
			
		||||
--   * selected_cb:   Callback to execute for each selected choice. Takes
 | 
			
		||||
--                    the choice as a string argument. Can be `nil` (no action
 | 
			
		||||
--                    to execute).
 | 
			
		||||
--   * rejected_cb:   Callback to execute for each rejected choice (possible
 | 
			
		||||
--                    choices which are not selected). Takes the choice as a
 | 
			
		||||
--                    string argument. Can be `nil` (no action to execute).
 | 
			
		||||
--   * extra_choices: An array of extra { choice_str, callback_fun } pairs to be
 | 
			
		||||
--                    added to the menu. Each callback_fun can be `nil`.
 | 
			
		||||
--   * combination:   The combination of choices to generate. Possible values:
 | 
			
		||||
--                    "powerset" and "single" (default).
 | 
			
		||||
-- Output:
 | 
			
		||||
-- * m: menu to be iterated over.
 | 
			
		||||
local function menu(args)
 | 
			
		||||
	local choices = assert(args.choices or args[1])
 | 
			
		||||
	local name = assert(args.name or args[2])
 | 
			
		||||
	local selected_cb = args.selected_cb
 | 
			
		||||
	local rejected_cb = args.rejected_cb
 | 
			
		||||
	local extra_choices = args.extra_choices or {}
 | 
			
		||||
 | 
			
		||||
	local ch_combinations = args.combination == 'powerset' and helpers.powerset(choices)
 | 
			
		||||
		or helpers.trivial_partition_set(choices)
 | 
			
		||||
 | 
			
		||||
	for _, c in pairs(extra_choices) do
 | 
			
		||||
		ch_combinations = atable.join(ch_combinations, { { c[1] } })
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	local m = {} -- the menu
 | 
			
		||||
 | 
			
		||||
	for _, c in pairs(ch_combinations) do
 | 
			
		||||
		if #c > 0 then
 | 
			
		||||
			local cbs = {}
 | 
			
		||||
 | 
			
		||||
			-- selected choices
 | 
			
		||||
			for _, ch in pairs(c) do
 | 
			
		||||
				if atable.hasitem(choices, ch) then
 | 
			
		||||
					cbs[#cbs + 1] = selected_cb and function()
 | 
			
		||||
						selected_cb(ch)
 | 
			
		||||
					end or nil
 | 
			
		||||
				end
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
			-- rejected choices
 | 
			
		||||
			for _, ch in pairs(choices) do
 | 
			
		||||
				if not atable.hasitem(c, ch) and atable.hasitem(choices, ch) then
 | 
			
		||||
					cbs[#cbs + 1] = rejected_cb and function()
 | 
			
		||||
						rejected_cb(ch)
 | 
			
		||||
					end or nil
 | 
			
		||||
				end
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
			-- add user extra choices (like the choice "None" for example)
 | 
			
		||||
			for _, x in pairs(extra_choices) do
 | 
			
		||||
				if x[1] == c[1] then
 | 
			
		||||
					cbs[#cbs + 1] = x[2]
 | 
			
		||||
				end
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
			m[#m + 1] = { name .. ': ' .. tconcat(c, ' + '), cbs }
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	return m
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return { iterate = iterate, menu = menu }
 | 
			
		||||
							
								
								
									
										203
									
								
								awesome/lib/lain/util/quake.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										203
									
								
								awesome/lib/lain/util/quake.lua
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,203 @@
 | 
			
		||||
--[[
 | 
			
		||||
 | 
			
		||||
     Licensed under GNU General Public License v2
 | 
			
		||||
      * (c) 2016, Luca CPZ
 | 
			
		||||
      * (c) 2015, unknown
 | 
			
		||||
 | 
			
		||||
--]]
 | 
			
		||||
 | 
			
		||||
local awful = require('awful')
 | 
			
		||||
local capi = { client = client }
 | 
			
		||||
local math = math
 | 
			
		||||
local string = string
 | 
			
		||||
local pairs = pairs
 | 
			
		||||
local screen = screen
 | 
			
		||||
local setmetatable = setmetatable
 | 
			
		||||
 | 
			
		||||
-- Quake-like Dropdown application spawn
 | 
			
		||||
local quake = {}
 | 
			
		||||
 | 
			
		||||
-- If you have a rule like "awful.client.setslave" for your terminals,
 | 
			
		||||
-- ensure you use an exception for QuakeDD. Otherwise, you may
 | 
			
		||||
-- run into problems with focus.
 | 
			
		||||
 | 
			
		||||
function quake:display()
 | 
			
		||||
	if self.followtag then
 | 
			
		||||
		self.screen = awful.screen.focused()
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	-- First, we locate the client
 | 
			
		||||
	local client = nil
 | 
			
		||||
	local i = 0
 | 
			
		||||
	for c in
 | 
			
		||||
		awful.client.iterate(function(c)
 | 
			
		||||
			-- c.name may be changed!
 | 
			
		||||
			return c.instance == self.name
 | 
			
		||||
		end)
 | 
			
		||||
	do
 | 
			
		||||
		i = i + 1
 | 
			
		||||
		if i == 1 then
 | 
			
		||||
			client = c
 | 
			
		||||
		else
 | 
			
		||||
			-- Additional matching clients, let's remove the sticky bit
 | 
			
		||||
			-- which may persist between awesome restarts. We don't close
 | 
			
		||||
			-- them as they may be valuable. They will just turn into
 | 
			
		||||
			-- normal clients.
 | 
			
		||||
			c.sticky = false
 | 
			
		||||
			c.ontop = false
 | 
			
		||||
			c.above = false
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	if not client and not self.visible then
 | 
			
		||||
		return
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	if not client then
 | 
			
		||||
		-- The client does not exist, we spawn it
 | 
			
		||||
		local cmd = string.format('%s %s %s', self.app, string.format(self.argname, self.name), self.extra)
 | 
			
		||||
		awful.spawn(cmd, { tag = self.screen.selected_tag })
 | 
			
		||||
		return
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	-- Set geometry
 | 
			
		||||
	client.floating = true
 | 
			
		||||
	client.border_width = self.border
 | 
			
		||||
	client.size_hints_honor = false
 | 
			
		||||
	local maximized = client.maximized
 | 
			
		||||
	local fullscreen = client.fullscreen
 | 
			
		||||
	client:geometry(self.geometry[self.screen.index] or self:compute_size())
 | 
			
		||||
 | 
			
		||||
	-- Set not sticky and on top
 | 
			
		||||
	client.sticky = false
 | 
			
		||||
	client.ontop = true
 | 
			
		||||
	client.above = true
 | 
			
		||||
	client.skip_taskbar = true
 | 
			
		||||
 | 
			
		||||
	-- Additional user settings
 | 
			
		||||
	if self.settings then
 | 
			
		||||
		self.settings(client)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	-- Toggle display
 | 
			
		||||
	if self.visible then
 | 
			
		||||
		client.hidden = false
 | 
			
		||||
		client.maximized = self.maximized
 | 
			
		||||
		client.fullscreen = self.fullscreen
 | 
			
		||||
		client:raise()
 | 
			
		||||
		self.last_tag = self.screen.selected_tag
 | 
			
		||||
		client:tags({ self.screen.selected_tag })
 | 
			
		||||
		capi.client.focus = client
 | 
			
		||||
	else
 | 
			
		||||
		self.maximized = maximized
 | 
			
		||||
		self.fullscreen = fullscreen
 | 
			
		||||
		client.maximized = false
 | 
			
		||||
		client.fullscreen = false
 | 
			
		||||
		client.hidden = true
 | 
			
		||||
		local ctags = client:tags()
 | 
			
		||||
		for j, _ in pairs(ctags) do
 | 
			
		||||
			ctags[j] = nil
 | 
			
		||||
		end
 | 
			
		||||
		client:tags(ctags)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	return client
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function quake:compute_size()
 | 
			
		||||
	-- skip if we already have a geometry for this screen
 | 
			
		||||
	if not self.geometry[self.screen.index] then
 | 
			
		||||
		local geom
 | 
			
		||||
		if not self.overlap then
 | 
			
		||||
			geom = screen[self.screen.index].workarea
 | 
			
		||||
		else
 | 
			
		||||
			geom = screen[self.screen.index].geometry
 | 
			
		||||
		end
 | 
			
		||||
		local width, height = self.width, self.height
 | 
			
		||||
		if width <= 1 then
 | 
			
		||||
			width = math.floor(geom.width * width) - 2 * self.border
 | 
			
		||||
		end
 | 
			
		||||
		if height <= 1 then
 | 
			
		||||
			height = math.floor(geom.height * height)
 | 
			
		||||
		end
 | 
			
		||||
		local x, y
 | 
			
		||||
		if self.horiz == 'left' then
 | 
			
		||||
			x = geom.x
 | 
			
		||||
		elseif self.horiz == 'right' then
 | 
			
		||||
			x = geom.width + geom.x - width
 | 
			
		||||
		else
 | 
			
		||||
			x = geom.x + (geom.width - width) / 2
 | 
			
		||||
		end
 | 
			
		||||
		if self.vert == 'top' then
 | 
			
		||||
			y = geom.y
 | 
			
		||||
		elseif self.vert == 'bottom' then
 | 
			
		||||
			y = geom.height + geom.y - height
 | 
			
		||||
		else
 | 
			
		||||
			y = geom.y + (geom.height - height) / 2
 | 
			
		||||
		end
 | 
			
		||||
		self.geometry[self.screen.index] = { x = x, y = y, width = width, height = height }
 | 
			
		||||
	end
 | 
			
		||||
	return self.geometry[self.screen.index]
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function quake:toggle()
 | 
			
		||||
	if self.followtag then
 | 
			
		||||
		self.screen = awful.screen.focused()
 | 
			
		||||
	end
 | 
			
		||||
	local current_tag = self.screen.selected_tag
 | 
			
		||||
	if current_tag and self.last_tag ~= current_tag and self.visible then
 | 
			
		||||
		local c = self:display()
 | 
			
		||||
		if c then
 | 
			
		||||
			c:move_to_tag(current_tag)
 | 
			
		||||
		end
 | 
			
		||||
	else
 | 
			
		||||
		self.visible = not self.visible
 | 
			
		||||
		self:display()
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function quake.new(conf)
 | 
			
		||||
	conf = conf or {}
 | 
			
		||||
 | 
			
		||||
	conf.app = conf.app or 'xterm' -- application to spawn
 | 
			
		||||
	conf.name = conf.name or 'QuakeDD' -- window name
 | 
			
		||||
	conf.argname = conf.argname or '-name %s' -- how to specify window name
 | 
			
		||||
	conf.extra = conf.extra or '' -- extra arguments
 | 
			
		||||
	conf.border = conf.border or 1 -- client border width
 | 
			
		||||
	conf.visible = conf.visible or false -- initially not visible
 | 
			
		||||
	conf.followtag = conf.followtag or false -- spawn on currently focused screen
 | 
			
		||||
	conf.overlap = conf.overlap or false -- overlap wibox
 | 
			
		||||
	conf.screen = conf.screen or awful.screen.focused()
 | 
			
		||||
	conf.settings = conf.settings
 | 
			
		||||
 | 
			
		||||
	-- If width or height <= 1 this is a proportion of the workspace
 | 
			
		||||
	conf.height = conf.height or 0.25 -- height
 | 
			
		||||
	conf.width = conf.width or 1 -- width
 | 
			
		||||
	conf.vert = conf.vert or 'top' -- top, bottom or center
 | 
			
		||||
	conf.horiz = conf.horiz or 'left' -- left, right or center
 | 
			
		||||
	conf.geometry = {} -- internal use
 | 
			
		||||
 | 
			
		||||
	conf.maximized = false
 | 
			
		||||
	conf.fullscreen = false
 | 
			
		||||
 | 
			
		||||
	local dropdown = setmetatable(conf, { __index = quake })
 | 
			
		||||
 | 
			
		||||
	capi.client.connect_signal('manage', function(c)
 | 
			
		||||
		if c.instance == dropdown.name and c.screen == dropdown.screen then
 | 
			
		||||
			dropdown:display()
 | 
			
		||||
		end
 | 
			
		||||
	end)
 | 
			
		||||
	capi.client.connect_signal('unmanage', function(c)
 | 
			
		||||
		if c.instance == dropdown.name and c.screen == dropdown.screen then
 | 
			
		||||
			dropdown.visible = false
 | 
			
		||||
		end
 | 
			
		||||
	end)
 | 
			
		||||
 | 
			
		||||
	return dropdown
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return setmetatable(quake, {
 | 
			
		||||
	__call = function(_, ...)
 | 
			
		||||
		return quake.new(...)
 | 
			
		||||
	end,
 | 
			
		||||
})
 | 
			
		||||
							
								
								
									
										118
									
								
								awesome/lib/lain/util/separators.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								awesome/lib/lain/util/separators.lua
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,118 @@
 | 
			
		||||
--[[
 | 
			
		||||
 | 
			
		||||
     Licensed under GNU General Public License v2
 | 
			
		||||
      * (c) 2015, Luca CPZ
 | 
			
		||||
      * (c) 2015, plotnikovanton
 | 
			
		||||
 | 
			
		||||
--]]
 | 
			
		||||
 | 
			
		||||
local wibox = require('wibox')
 | 
			
		||||
local gears = require('gears')
 | 
			
		||||
local beautiful = require('beautiful')
 | 
			
		||||
 | 
			
		||||
-- Lain Cairo separators util submodule
 | 
			
		||||
-- lain.util.separators
 | 
			
		||||
local separators = { height = beautiful.separators_height or 0, width = beautiful.separators_width or 9 }
 | 
			
		||||
 | 
			
		||||
-- [[ Arrow
 | 
			
		||||
 | 
			
		||||
-- Right
 | 
			
		||||
function separators.arrow_right(col1, col2)
 | 
			
		||||
	local widget = wibox.widget.base.make_widget()
 | 
			
		||||
	widget.col1 = col1
 | 
			
		||||
	widget.col2 = col2
 | 
			
		||||
 | 
			
		||||
	widget.fit = function(_, _, _)
 | 
			
		||||
		return separators.width, separators.height
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	widget.update = function(_, _)
 | 
			
		||||
		widget.col1 = col1
 | 
			
		||||
		widget.col2 = col2
 | 
			
		||||
		widget:emit_signal('widget::redraw_needed')
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	widget.draw = function(_, _, cr, width, height)
 | 
			
		||||
		if widget.col2 ~= 'alpha' then
 | 
			
		||||
			cr:set_source_rgba(gears.color.parse_color(widget.col2))
 | 
			
		||||
			cr:new_path()
 | 
			
		||||
			cr:move_to(0, 0)
 | 
			
		||||
			cr:line_to(width, height / 2)
 | 
			
		||||
			cr:line_to(width, 0)
 | 
			
		||||
			cr:close_path()
 | 
			
		||||
			cr:fill()
 | 
			
		||||
 | 
			
		||||
			cr:new_path()
 | 
			
		||||
			cr:move_to(0, height)
 | 
			
		||||
			cr:line_to(width, height / 2)
 | 
			
		||||
			cr:line_to(width, height)
 | 
			
		||||
			cr:close_path()
 | 
			
		||||
			cr:fill()
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		if widget.col1 ~= 'alpha' then
 | 
			
		||||
			cr:set_source_rgba(gears.color.parse_color(widget.col1))
 | 
			
		||||
			cr:new_path()
 | 
			
		||||
			cr:move_to(0, 0)
 | 
			
		||||
			cr:line_to(width, height / 2)
 | 
			
		||||
			cr:line_to(0, height)
 | 
			
		||||
			cr:close_path()
 | 
			
		||||
			cr:fill()
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	return widget
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Left
 | 
			
		||||
function separators.arrow_left(col1, col2)
 | 
			
		||||
	local widget = wibox.widget.base.make_widget()
 | 
			
		||||
	widget.col1 = col1
 | 
			
		||||
	widget.col2 = col2
 | 
			
		||||
 | 
			
		||||
	widget.fit = function(_, _, _)
 | 
			
		||||
		return separators.width, separators.height
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	widget.update = function(c1, c2)
 | 
			
		||||
		widget.col1 = c1
 | 
			
		||||
		widget.col2 = c2
 | 
			
		||||
		widget:emit_signal('widget::redraw_needed')
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	widget.draw = function(_, _, cr, width, height)
 | 
			
		||||
		if widget.col1 ~= 'alpha' then
 | 
			
		||||
			cr:set_source_rgba(gears.color.parse_color(widget.col1))
 | 
			
		||||
			cr:new_path()
 | 
			
		||||
			cr:move_to(width, 0)
 | 
			
		||||
			cr:line_to(0, height / 2)
 | 
			
		||||
			cr:line_to(0, 0)
 | 
			
		||||
			cr:close_path()
 | 
			
		||||
			cr:fill()
 | 
			
		||||
 | 
			
		||||
			cr:new_path()
 | 
			
		||||
			cr:move_to(width, height)
 | 
			
		||||
			cr:line_to(0, height / 2)
 | 
			
		||||
			cr:line_to(0, height)
 | 
			
		||||
			cr:close_path()
 | 
			
		||||
			cr:fill()
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		if widget.col2 ~= 'alpha' then
 | 
			
		||||
			cr:new_path()
 | 
			
		||||
			cr:move_to(width, 0)
 | 
			
		||||
			cr:line_to(0, height / 2)
 | 
			
		||||
			cr:line_to(width, height)
 | 
			
		||||
			cr:close_path()
 | 
			
		||||
 | 
			
		||||
			cr:set_source_rgba(gears.color.parse_color(widget.col2))
 | 
			
		||||
			cr:fill()
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	return widget
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- ]]
 | 
			
		||||
 | 
			
		||||
return separators
 | 
			
		||||
		Reference in New Issue
	
	Block a user