summaryrefslogtreecommitdiff
path: root/awesome/lib/awful/tag.lua
diff options
context:
space:
mode:
Diffstat (limited to 'awesome/lib/awful/tag.lua')
-rw-r--r--awesome/lib/awful/tag.lua1505
1 files changed, 1505 insertions, 0 deletions
diff --git a/awesome/lib/awful/tag.lua b/awesome/lib/awful/tag.lua
new file mode 100644
index 0000000..dbf3a60
--- /dev/null
+++ b/awesome/lib/awful/tag.lua
@@ -0,0 +1,1505 @@
+---------------------------------------------------------------------------
+--- Useful functions for tag manipulation.
+--
+-- @author Julien Danjou <julien@danjou.info>
+-- @copyright 2008 Julien Danjou
+-- @module tag
+---------------------------------------------------------------------------
+
+-- Grab environment we need
+local util = require("awful.util")
+local ascreen = require("awful.screen")
+local beautiful = require("beautiful")
+local object = require("gears.object")
+local timer = require("gears.timer")
+local pairs = pairs
+local ipairs = ipairs
+local table = table
+local setmetatable = setmetatable
+local capi =
+{
+ tag = tag,
+ screen = screen,
+ mouse = mouse,
+ client = client,
+ root = root
+}
+
+local function get_screen(s)
+ return s and capi.screen[s]
+end
+
+local tag = {object = {}, mt = {} }
+
+-- Private data
+local data = {}
+data.history = {}
+
+-- History functions
+tag.history = {}
+tag.history.limit = 20
+
+-- Default values
+local defaults = {}
+
+-- The gap between clients (in points).
+defaults.gap = 0
+
+-- The default gap_count.
+defaults.gap_single_client = true
+
+-- The default master fill policy.
+defaults.master_fill_policy = "expand"
+
+-- The default master width factor.
+defaults.master_width_factor = 0.5
+
+-- The default master count.
+defaults.master_count = 1
+
+-- The default column count.
+defaults.column_count = 1
+
+-- screen.tags depend on index, it cannot be used by awful.tag
+local function raw_tags(scr)
+ local tmp_tags = {}
+ for _, t in ipairs(root.tags()) do
+ if get_screen(t.screen) == scr then
+ table.insert(tmp_tags, t)
+ end
+ end
+
+ return tmp_tags
+end
+
+--- The number of elements kept in the history.
+-- @tfield integer awful.tag.history.limit
+-- @tparam[opt=20] integer limit
+
+--- The tag index.
+--
+-- The index is the position as shown in the `awful.widget.taglist`.
+--
+-- **Signal:**
+--
+-- * *property::index*
+--
+-- @property index
+-- @param integer
+-- @treturn number The tag index.
+
+function tag.object.set_index(self, idx)
+ local scr = get_screen(tag.getproperty(self, "screen"))
+
+ -- screen.tags cannot be used as it depend on index
+ local tmp_tags = raw_tags(scr)
+
+ -- sort the tags by index
+ table.sort(tmp_tags, function(a, b)
+ local ia, ib = tag.getproperty(a, "index"), tag.getproperty(b, "index")
+ return (ia or math.huge) < (ib or math.huge)
+ end)
+
+ if (not idx) or (idx < 1) or (idx > #tmp_tags) then
+ return
+ end
+
+ local rm_index = nil
+
+ for i, t in ipairs(tmp_tags) do
+ if t == self then
+ table.remove(tmp_tags, i)
+ rm_index = i
+ break
+ end
+ end
+
+ table.insert(tmp_tags, idx, self)
+ for i = idx < rm_index and idx or rm_index, #tmp_tags do
+ local tmp_tag = tmp_tags[i]
+ tag.object.set_screen(tmp_tag, scr)
+ tag.setproperty(tmp_tag, "index", i)
+ end
+end
+
+function tag.object.get_index(query_tag)
+
+ local idx = tag.getproperty(query_tag, "index")
+
+ if idx then return idx end
+
+ -- Get an unordered list of tags
+ local tags = raw_tags(query_tag.screen)
+
+ -- Too bad, lets compute it
+ for i, t in ipairs(tags) do
+ if t == query_tag then
+ tag.setproperty(t, "index", i)
+ return i
+ end
+ end
+end
+
+--- Move a tag to an absolute position in the screen[]:tags() table.
+-- @deprecated awful.tag.move
+-- @param new_index Integer absolute position in the table to insert.
+-- @param target_tag The tag that should be moved. If null, the currently
+-- selected tag is used.
+-- @see index
+function tag.move(new_index, target_tag)
+ util.deprecate("Use t.index = new_index instead of awful.tag.move")
+
+ target_tag = target_tag or ascreen.focused().selected_tag
+ tag.object.set_index(target_tag, new_index)
+end
+
+--- Swap 2 tags
+-- @function tag.swap
+-- @param tag2 The second tag
+-- @see client.swap
+function tag.object.swap(self, tag2)
+ local idx1, idx2 = tag.object.get_index(self), tag.object.get_index(tag2)
+ local scr2, scr1 = tag.getproperty(tag2, "screen"), tag.getproperty(self, "screen")
+
+ -- If they are on the same screen, avoid recomputing the whole table
+ -- for nothing.
+ if scr1 == scr2 then
+ tag.setproperty(self, "index", idx2)
+ tag.setproperty(tag2, "index", idx1)
+ else
+ tag.object.set_screen(tag2, scr1)
+ tag.object.set_index (tag2, idx1)
+ tag.object.set_screen(self, scr2)
+ tag.object.set_index (self, idx2)
+ end
+end
+
+--- Swap 2 tags
+-- @deprecated awful.tag.swap
+-- @see tag.swap
+-- @param tag1 The first tag
+-- @param tag2 The second tag
+function tag.swap(tag1, tag2)
+ util.deprecate("Use t:swap(tag2) instead of awful.tag.swap")
+
+ tag.object.swap(tag1, tag2)
+end
+
+--- Add a tag.
+--
+-- This function allow to create tags from a set of properties:
+--
+-- local t = awful.tag.add("my new tag", {
+-- screen = screen.primary,
+-- layout = awful.layout.suit.max,
+-- })
+--
+-- @function awful.tag.add
+-- @param name The tag name, a string
+-- @param props The tags inital properties, a table
+-- @return The created tag
+-- @see tag.delete
+function tag.add(name, props)
+ local properties = props or {}
+
+ -- Be sure to set the screen before the tag is activated to avoid function
+ -- connected to property::activated to be called without a valid tag.
+ -- set properties cannot be used as this has to be set before the first
+ -- signal is sent
+ properties.screen = get_screen(properties.screen or ascreen.focused())
+ -- Index is also required
+ properties.index = properties.index or #raw_tags(properties.screen)+1
+
+ local newtag = capi.tag{ name = name }
+
+ -- Start with a fresh property table to avoid collisions with unsupported data
+ newtag.data.awful_tag_properties = {screen=properties.screen, index=properties.index}
+
+ newtag.activated = true
+
+ for k, v in pairs(properties) do
+ -- `rawget` doesn't work on userdata, `:clients()` is the only relevant
+ -- entry.
+ if k == "clients" or tag.object[k] then
+ newtag[k](newtag, v)
+ else
+ newtag[k] = v
+ end
+ end
+
+ return newtag
+end
+
+--- Create a set of tags and attach it to a screen.
+-- @function awful.tag.new
+-- @param names The tag name, in a table
+-- @param screen The tag screen, or 1 if not set.
+-- @param layout The layout or layout table to set for this tags by default.
+-- @return A table with all created tags.
+function tag.new(names, screen, layout)
+ screen = get_screen(screen or 1)
+ local tags = {}
+ for id, name in ipairs(names) do
+ table.insert(tags, id, tag.add(name, {screen = screen,
+ layout = (layout and layout[id]) or
+ layout}))
+ -- Select the first tag.
+ if id == 1 then
+ tags[id].selected = true
+ end
+ end
+
+ return tags
+end
+
+--- Find a suitable fallback tag.
+-- @function awful.tag.find_fallback
+-- @param screen The screen to look for a tag on. [awful.screen.focused()]
+-- @param invalids A table of tags we consider unacceptable. [selectedlist(scr)]
+function tag.find_fallback(screen, invalids)
+ local scr = screen or ascreen.focused()
+ local t = invalids or scr.selected_tags
+
+ for _, v in pairs(scr.tags) do
+ if not util.table.hasitem(t, v) then return v end
+ end
+end
+
+--- Delete a tag.
+--
+-- To delete the current tag:
+--
+-- mouse.screen.selected_tag:delete()
+--
+-- @function tag.delete
+-- @see awful.tag.add
+-- @see awful.tag.find_fallback
+-- @tparam[opt=awful.tag.find_fallback()] tag fallback_tag Tag to assign
+-- stickied tags to.
+-- @tparam[opt=false] boolean force Move even non-sticky clients to the fallback
+-- tag.
+-- @return Returns true if the tag is successfully deleted.
+-- If there are no clients exclusively on this tag then delete it. Any
+-- stickied clients are assigned to the optional 'fallback_tag'.
+-- If after deleting the tag there is no selected tag, try and restore from
+-- history or select the first tag on the screen.
+function tag.object.delete(self, fallback_tag, force)
+ -- abort if the taf isn't currently activated
+ if not self.activated then return false end
+
+ local target_scr = get_screen(tag.getproperty(self, "screen"))
+ local tags = target_scr.tags
+ local idx = tag.object.get_index(self)
+ local ntags = #tags
+
+ -- We can't use the target tag as a fallback.
+ if fallback_tag == self then return false end
+
+ -- No fallback_tag provided, try and get one.
+ if fallback_tag == nil then
+ fallback_tag = tag.find_fallback(target_scr, {self})
+ end
+
+ -- Abort if we would have un-tagged clients.
+ local clients = self:clients()
+ if #clients > 0 and fallback_tag == nil then return false end
+
+ -- Move the clients we can off of this tag.
+ for _, c in pairs(clients) do
+ local nb_tags = #c:tags()
+
+ -- If a client has only this tag, or stickied clients with
+ -- nowhere to go, abort.
+ if (not c.sticky and nb_tags == 1 and not force) then
+ return
+ -- If a client has multiple tags, then do not move it to fallback
+ elseif nb_tags < 2 then
+ c:tags({fallback_tag})
+ end
+ end
+
+ -- delete the tag
+ self.data.awful_tag_properties.screen = nil
+ self.activated = false
+
+ -- Update all indexes
+ for i=idx+1, #tags do
+ tag.setproperty(tags[i], "index", i-1)
+ end
+
+ -- If no tags are visible (and we did not delete the lasttag), try and
+ -- view one. The > 1 is because ntags is no longer synchronized with the
+ -- current count.
+ if target_scr.selected_tag == nil and ntags > 1 then
+ tag.history.restore(target_scr, 1)
+ if target_scr.selected_tag == nil then
+ local other_tag = tags[tags[1] == self and 2 or 1]
+ if other_tag then
+ other_tag.selected = true
+ end
+ end
+ end
+
+ return true
+end
+
+--- Delete a tag.
+-- @deprecated awful.tag.delete
+-- @see tag.delete
+-- @param target_tag Optional tag object to delete. [selected()]
+-- @param fallback_tag Tag to assign stickied tags to. [~selected()]
+-- @return Returns true if the tag is successfully deleted, nil otherwise.
+-- If there are no clients exclusively on this tag then delete it. Any
+-- stickied clients are assigned to the optional 'fallback_tag'.
+-- If after deleting the tag there is no selected tag, try and restore from
+-- history or select the first tag on the screen.
+function tag.delete(target_tag, fallback_tag)
+ util.deprecate("Use t:delete(fallback_tag) instead of awful.tag.delete")
+
+ return tag.object.delete(target_tag, fallback_tag)
+end
+
+--- Update the tag history.
+-- @function awful.tag.history.update
+-- @param obj Screen object.
+function tag.history.update(obj)
+ local s = get_screen(obj)
+ local curtags = s.selected_tags
+ -- create history table
+ if not data.history[s] then
+ data.history[s] = {}
+ else
+ if data.history[s].current then
+ -- Check that the list is not identical
+ local identical = #data.history[s].current == #curtags
+ if identical then
+ for idx, _tag in ipairs(data.history[s].current) do
+ if curtags[idx] ~= _tag then
+ identical = false
+ break
+ end
+ end
+ end
+
+ -- Do not update history the table are identical
+ if identical then return end
+ end
+
+ -- Limit history
+ if #data.history[s] >= tag.history.limit then
+ for i = tag.history.limit, #data.history[s] do
+ data.history[s][i] = nil
+ end
+ end
+ end
+
+ -- store previously selected tags in the history table
+ table.insert(data.history[s], 1, data.history[s].current)
+ data.history[s].previous = data.history[s][1]
+ -- store currently selected tags
+ data.history[s].current = setmetatable(curtags, { __mode = 'v' })
+end
+
+--- Revert tag history.
+-- @function awful.tag.history.restore
+-- @param screen The screen.
+-- @param idx Index in history. Defaults to "previous" which is a special index
+-- toggling between last two selected sets of tags. Number (eg 1) will go back
+-- to the given index in history.
+function tag.history.restore(screen, idx)
+ local s = get_screen(screen or ascreen.focused())
+ local i = idx or "previous"
+ local sel = s.selected_tags
+ -- do nothing if history empty
+ if not data.history[s] or not data.history[s][i] then return end
+ -- if all tags been deleted, try next entry
+ if #data.history[s][i] == 0 then
+ if i == "previous" then i = 0 end
+ tag.history.restore(s, i + 1)
+ return
+ end
+ -- deselect all tags
+ tag.viewnone(s)
+ -- select tags from the history entry
+ for _, t in ipairs(data.history[s][i]) do
+ if t.activated and t.screen then
+ t.selected = true
+ end
+ end
+ -- update currently selected tags table
+ data.history[s].current = data.history[s][i]
+ -- store previously selected tags
+ data.history[s].previous = setmetatable(sel, { __mode = 'v' })
+ -- remove the reverted history entry
+ if i ~= "previous" then table.remove(data.history[s], i) end
+
+ s:emit_signal("tag::history::update")
+end
+
+--- Get a list of all tags on a screen
+-- @deprecated awful.tag.gettags
+-- @tparam screen s Screen
+-- @return A table with all available tags
+-- @see screen.tags
+function tag.gettags(s)
+ util.deprecate("Use s.tags instead of awful.tag.gettags")
+
+ s = get_screen(s)
+
+ return s and s.tags or {}
+end
+
+--- Find a tag by name
+-- @tparam[opt] screen s The screen of the tag
+-- @tparam string name The name of the tag
+-- @return The tag found, or `nil`
+function tag.find_by_name(s, name)
+ local tags = s and s.tags or root.tags()
+ for _, t in ipairs(tags) do
+ if name == t.name then
+ return t
+ end
+ end
+end
+
+--- The tag screen.
+--
+-- **Signal:**
+--
+-- * *property::screen*
+--
+-- @property screen
+-- @param screen
+-- @see screen
+
+function tag.object.set_screen(t, s)
+
+ s = get_screen(s or ascreen.focused())
+ local sel = tag.selected
+ local old_screen = get_screen(tag.getproperty(t, "screen"))
+
+ if s == old_screen then return end
+
+ -- Keeping the old index make very little sense when changing screen
+ tag.setproperty(t, "index", nil)
+
+ -- Change the screen
+ tag.setproperty(t, "screen", s)
+ tag.setproperty(t, "index", #s:get_tags(true))
+
+ -- Make sure the client's screen matches its tags
+ for _,c in ipairs(t:clients()) do
+ c.screen = s --Move all clients
+ c:tags({t})
+ end
+
+ -- Update all indexes
+ for i,t2 in ipairs(old_screen.tags) do
+ tag.setproperty(t2, "index", i)
+ end
+
+ -- Restore the old screen history if the tag was selected
+ if sel then
+ tag.history.restore(old_screen, 1)
+ end
+end
+
+--- Set a tag's screen
+-- @deprecated awful.tag.setscreen
+-- @see screen
+-- @param s Screen
+-- @param t tag object
+function tag.setscreen(s, t)
+ -- For API consistency, the arguments have been swapped for Awesome 3.6
+ -- this method is already deprecated, so be silent and swap the args
+ if type(t) == "number" then
+ s, t = t, s
+ end
+
+ util.deprecate("Use t.screen = s instead of awful.tag.setscreen(t, s)")
+
+ tag.object.set_screen(t, s)
+end
+
+--- Get a tag's screen
+-- @deprecated awful.tag.getscreen
+-- @see screen
+-- @param[opt] t tag object
+-- @return Screen number
+function tag.getscreen(t)
+ util.deprecate("Use t.screen instead of awful.tag.getscreen(t)")
+
+ -- A new getter is not required
+
+ t = t or ascreen.focused().selected_tag
+ local prop = tag.getproperty(t, "screen")
+ return prop and prop.index
+end
+
+--- Return a table with all visible tags
+-- @deprecated awful.tag.selectedlist
+-- @param s Screen.
+-- @return A table with all selected tags.
+-- @see screen.selected_tags
+function tag.selectedlist(s)
+ util.deprecate("Use s.selected_tags instead of awful.tag.selectedlist")
+
+ s = get_screen(s or ascreen.focused())
+
+ return s.selected_tags
+end
+
+--- Return only the first visible tag.
+-- @deprecated awful.tag.selected
+-- @param s Screen.
+-- @see screen.selected_tag
+function tag.selected(s)
+ util.deprecate("Use s.selected_tag instead of awful.tag.selected")
+
+ s = get_screen(s or ascreen.focused())
+
+ return s.selected_tag
+end
+
+--- The default master width factor
+--
+-- @beautiful beautiful.master_width_factor
+-- @param number (default: 0.5)
+-- @see master_width_factor
+-- @see gap
+
+--- The tag master width factor.
+--
+-- The master width factor is one of the 5 main properties used to configure
+-- the `layout`. Each layout interpret (or ignore) this property differenly.
+--
+-- See the layout suit documentation for information about how the master width
+-- factor is used.
+--
+-- **Signal:**
+--
+-- * *property::mwfact* (deprecated)
+-- * *property::master_width_factor*
+--
+-- @property master_width_factor
+-- @param number Between 0 and 1
+-- @see master_count
+-- @see column_count
+-- @see master_fill_policy
+-- @see gap
+
+function tag.object.set_master_width_factor(t, mwfact)
+ if mwfact >= 0 and mwfact <= 1 then
+ tag.setproperty(t, "mwfact", mwfact)
+ tag.setproperty(t, "master_width_factor", mwfact)
+ end
+end
+
+function tag.object.get_master_width_factor(t)
+ return tag.getproperty(t, "master_width_factor")
+ or beautiful.master_width_factor
+ or defaults.master_width_factor
+end
+
+--- Set master width factor.
+-- @deprecated awful.tag.setmwfact
+-- @see master_fill_policy
+-- @see master_width_factor
+-- @param mwfact Master width factor.
+-- @param t The tag to modify, if null tag.selected() is used.
+function tag.setmwfact(mwfact, t)
+ util.deprecate("Use t.master_width_factor = mwfact instead of awful.tag.setmwfact")
+
+ tag.object.get_master_width_factor(t or ascreen.focused().selected_tag, mwfact)
+end
+
+--- Increase master width factor.
+-- @function awful.tag.incmwfact
+-- @see master_width_factor
+-- @param add Value to add to master width factor.
+-- @param t The tag to modify, if null tag.selected() is used.
+function tag.incmwfact(add, t)
+ t = t or t or ascreen.focused().selected_tag
+ tag.object.set_master_width_factor(t, tag.object.get_master_width_factor(t) + add)
+end
+
+--- Get master width factor.
+-- @deprecated awful.tag.getmwfact
+-- @see master_width_factor
+-- @see master_fill_policy
+-- @param[opt] t The tag.
+function tag.getmwfact(t)
+ util.deprecate("Use t.master_width_factor instead of awful.tag.getmwfact")
+
+ return tag.object.get_master_width_factor(t or ascreen.focused().selected_tag)
+end
+
+--- An ordered list of layouts.
+-- `awful.tag.layout` Is usually defined in `rc.lua`. It store the list of
+-- layouts used when selecting the previous and next layouts. This is the
+-- default:
+--
+-- -- Table of layouts to cover with awful.layout.inc, order matters.
+-- awful.layout.layouts = {
+-- awful.layout.suit.floating,
+-- awful.layout.suit.tile,
+-- awful.layout.suit.tile.left,
+-- awful.layout.suit.tile.bottom,
+-- awful.layout.suit.tile.top,
+-- awful.layout.suit.fair,
+-- awful.layout.suit.fair.horizontal,
+-- awful.layout.suit.spiral,
+-- awful.layout.suit.spiral.dwindle,
+-- awful.layout.suit.max,
+-- awful.layout.suit.max.fullscreen,
+-- awful.layout.suit.magnifier,
+-- awful.layout.suit.corner.nw,
+-- -- awful.layout.suit.corner.ne,
+-- -- awful.layout.suit.corner.sw,
+-- -- awful.layout.suit.corner.se,
+-- }
+--
+-- @field awful.tag.layouts
+
+--- The tag client layout.
+--
+-- This property hold the layout. A layout can be either stateless or stateful.
+-- Stateless layouts are used by default by Awesome. They tile clients without
+-- any other overhead. They take an ordered list of clients and place them on
+-- the screen. Stateful layouts create an object instance for each tags and
+-- can store variables and metadata. Because of this, they are able to change
+-- over time and be serialized (saved).
+--
+-- Both types of layouts have valid usage scenarios.
+--
+-- **Stateless layouts:**
+--
+-- These layouts are stored in `awful.layout.suit`. They expose a table with 2
+-- fields:
+--
+-- * **name** (*string*): The layout name. This should be unique.
+-- * **arrange** (*function*): The function called when the clients need to be
+-- placed. The only parameter is a table or arguments returned by
+-- `awful.layout.parameters`
+--
+-- **Stateful layouts:**
+--
+-- The stateful layouts API is the same as stateless, but they are a function
+-- returining a layout instead of a layout itself. They also should have an
+-- `is_dynamic = true` property. If they don't, `awful.tag` will create a new
+-- instance everytime the layout is set. If they do, the instance will be
+-- cached and re-used.
+--
+-- **Signal:**
+--
+-- * *property::layout*
+--
+-- @property layout
+-- @see awful.tag.layouts
+-- @tparam layout|function layout A layout table or a constructor function
+-- @return The layout
+
+function tag.object.set_layout(t, layout)
+ -- Check if the signature match a stateful layout
+ if type(layout) == "function" or (
+ type(layout) == "table"
+ and getmetatable(layout)
+ and getmetatable(layout).__call
+ ) then
+ if not t.dynamic_layout_cache then
+ t.dynamic_layout_cache = {}
+ end
+
+ local instance = t.dynamic_layout_cache[layout] or layout(t)
+
+ -- Always make sure the layout is notified it is enabled
+ if tag.getproperty(t, "screen").selected_tag == t and instance.wake_up then
+ instance:wake_up()
+ end
+
+ -- Avoid creating the same layout twice, use layout:reset() to reset
+ if instance.is_dynamic then
+ t.dynamic_layout_cache[layout] = instance
+ end
+
+ layout = instance
+ end
+
+ tag.setproperty(t, "layout", layout)
+
+ return layout
+end
+
+--- Set layout.
+-- @deprecated awful.tag.setlayout
+-- @see layout
+-- @param layout a layout table or a constructor function
+-- @param t The tag to modify
+-- @return The layout
+function tag.setlayout(layout, t)
+ util.deprecate("Use t.layout = layout instead of awful.tag.setlayout")
+
+ return tag.object.set_layout(t, layout)
+end
+
+--- Define if the tag must be deleted when the last client is untagged.
+--
+-- This is useful to create "throw-away" tags for operation like 50/50
+-- side-by-side views.
+--
+-- local t = awful.tag.add("Temporary", {
+-- screen = client.focus.screen,
+-- volatile = true,
+-- clients = {
+-- client.focus,
+-- awful.client.focus.history.get(client.focus.screen, 1)
+-- }
+-- }
+--
+-- **Signal:**
+--
+-- * *property::volatile*
+--
+-- @property volatile
+-- @param boolean
+
+-- Volatile accessors are implicit
+
+--- Set if the tag must be deleted when the last client is untagged
+-- @deprecated awful.tag.setvolatile
+-- @see volatile
+-- @tparam boolean volatile If the tag must be deleted when the last client is untagged
+-- @param t The tag to modify, if null tag.selected() is used.
+function tag.setvolatile(volatile, t)
+ util.deprecate("Use t.volatile = volatile instead of awful.tag.setvolatile")
+
+ tag.setproperty(t, "volatile", volatile)
+end
+
+--- Get if the tag must be deleted when the last client closes
+-- @deprecated awful.tag.getvolatile
+-- @see volatile
+-- @param t The tag to modify, if null tag.selected() is used.
+-- @treturn boolean If the tag will be deleted when the last client is untagged
+function tag.getvolatile(t)
+ util.deprecate("Use t.volatile instead of awful.tag.getvolatile")
+
+ return tag.getproperty(t, "volatile") or false
+end
+
+--- The default gap.
+--
+-- @beautiful beautiful.useless_gap
+-- @param number (default: 0)
+-- @see gap
+-- @see gap_single_client
+
+--- The gap (spacing, also called `useless_gap`) between clients.
+--
+-- This property allow to waste space on the screen in the name of style,
+-- unicorns and readability.
+--
+-- **Signal:**
+--
+-- * *property::useless_gap*
+--
+-- @property gap
+-- @param number The value has to be greater than zero.
+-- @see gap_single_client
+
+function tag.object.set_gap(t, useless_gap)
+ if useless_gap >= 0 then
+ tag.setproperty(t, "useless_gap", useless_gap)
+ end
+end
+
+function tag.object.get_gap(t)
+ return tag.getproperty(t, "useless_gap")
+ or beautiful.useless_gap
+ or defaults.gap
+end
+
+--- Set the spacing between clients
+-- @deprecated awful.tag.setgap
+-- @see gap
+-- @param useless_gap The spacing between clients
+-- @param t The tag to modify, if null tag.selected() is used.
+function tag.setgap(useless_gap, t)
+ util.deprecate("Use t.gap = useless_gap instead of awful.tag.setgap")
+
+ tag.object.set_gap(t or ascreen.focused().selected_tag, useless_gap)
+end
+
+--- Increase the spacing between clients
+-- @function awful.tag.incgap
+-- @see gap
+-- @param add Value to add to the spacing between clients
+-- @param t The tag to modify, if null tag.selected() is used.
+function tag.incgap(add, t)
+ t = t or t or ascreen.focused().selected_tag
+ tag.object.set_gap(t, tag.object.get_gap(t) + add)
+end
+
+--- Enable gaps for a single client.
+--
+-- @beautiful beautiful.gap_single_client
+-- @param boolean (default: true)
+-- @see gap
+-- @see gap_single_client
+
+--- Enable gaps for a single client.
+--
+-- **Signal:**
+--
+-- * *property::gap\_single\_client*
+--
+-- @property gap_single_client
+-- @param boolean Enable gaps for a single client
+
+function tag.object.set_gap_single_client(t, gap_single_client)
+ tag.setproperty(t, "gap_single_client", gap_single_client == true)
+end
+
+function tag.object.get_gap_single_client(t)
+ local val = tag.getproperty(t, "gap_single_client")
+ if val ~= nil then
+ return val
+ end
+ val = beautiful.gap_single_client
+ if val ~= nil then
+ return val
+ end
+ return defaults.gap_single_client
+end
+
+--- Get the spacing between clients.
+-- @deprecated awful.tag.getgap
+-- @see gap
+-- @tparam[opt=tag.selected()] tag t The tag.
+-- @tparam[opt] int numclients Number of (tiled) clients. Passing this will
+-- return 0 for a single client. You can override this function to change
+-- this behavior.
+function tag.getgap(t, numclients)
+ util.deprecate("Use t.gap instead of awful.tag.getgap")
+
+ if numclients == 1 then
+ return 0
+ end
+
+ return tag.object.get_gap(t or ascreen.focused().selected_tag)
+end
+
+--- The default fill policy.
+--
+-- ** Possible values**:
+--
+-- * *expand*: Take all the space
+-- * *master_width_factor*: Only take the ratio defined by the
+-- `master_width_factor`
+--
+-- @beautiful beautiful.master_fill_policy
+-- @param string (default: "expand")
+-- @see master_fill_policy
+
+--- Set size fill policy for the master client(s).
+--
+-- ** Possible values**:
+--
+-- * *expand*: Take all the space
+-- * *master_width_factor*: Only take the ratio defined by the
+-- `master_width_factor`
+--
+-- **Signal:**
+--
+-- * *property::master_fill_policy*
+--
+-- @property master_fill_policy
+-- @param string "expand" or "master_width_factor"
+
+function tag.object.get_master_fill_policy(t)
+ return tag.getproperty(t, "master_fill_policy")
+ or beautiful.master_fill_policy
+ or defaults.master_fill_policy
+end
+
+--- Set size fill policy for the master client(s)
+-- @deprecated awful.tag.setmfpol
+-- @see master_fill_policy
+-- @tparam string policy Can be set to
+-- "expand" (fill all the available workarea) or
+-- "master_width_factor" (fill only an area inside the master width factor)
+-- @tparam[opt=tag.selected()] tag t The tag to modify
+function tag.setmfpol(policy, t)
+ util.deprecate("Use t.master_fill_policy = policy instead of awful.tag.setmfpol")
+
+ t = t or ascreen.focused().selected_tag
+ tag.setproperty(t, "master_fill_policy", policy)
+end
+
+--- Toggle size fill policy for the master client(s)
+-- between "expand" and "master_width_factor".
+-- @function awful.tag.togglemfpol
+-- @see master_fill_policy
+-- @tparam tag t The tag to modify, if null tag.selected() is used.
+function tag.togglemfpol(t)
+ t = t or ascreen.focused().selected_tag
+
+ if tag.getmfpol(t) == "expand" then
+ tag.setproperty(t, "master_fill_policy", "master_width_factor")
+ else
+ tag.setproperty(t, "master_fill_policy", "expand")
+ end
+end
+
+--- Get size fill policy for the master client(s)
+-- @deprecated awful.tag.getmfpol
+-- @see master_fill_policy
+-- @tparam[opt=tag.selected()] tag t The tag
+-- @treturn string Possible values are
+-- "expand" (fill all the available workarea, default one) or
+-- "master_width_factor" (fill only an area inside the master width factor)
+function tag.getmfpol(t)
+ util.deprecate("Use t.master_fill_policy instead of awful.tag.getmfpol")
+
+ t = t or ascreen.focused().selected_tag
+ return tag.getproperty(t, "master_fill_policy")
+ or beautiful.master_fill_policy
+ or defaults.master_fill_policy
+end
+
+--- The default number of master windows.
+--
+-- @beautiful beautiful.master_count
+-- @param integer (default: 1)
+-- @see master_count
+
+--- Set the number of master windows.
+--
+-- **Signal:**
+--
+-- * *property::nmaster* (deprecated)
+-- * *property::master_count* (deprecated)
+--
+-- @property master_count
+-- @param integer nmaster Only positive values are accepted
+
+function tag.object.set_master_count(t, nmaster)
+ if nmaster >= 0 then
+ tag.setproperty(t, "nmaster", nmaster)
+ tag.setproperty(t, "master_count", nmaster)
+ end
+end
+
+function tag.object.get_master_count(t)
+ return tag.getproperty(t, "master_count")
+ or beautiful.master_count
+ or defaults.master_count
+end
+
+---
+-- @deprecated awful.tag.setnmaster
+-- @see master_count
+-- @param nmaster The number of master windows.
+-- @param[opt] t The tag.
+function tag.setnmaster(nmaster, t)
+ util.deprecate("Use t.master_count = nmaster instead of awful.tag.setnmaster")
+
+ tag.object.set_master_count(t or ascreen.focused().selected_tag, nmaster)
+end
+
+--- Get the number of master windows.
+-- @deprecated awful.tag.getnmaster
+-- @see master_count
+-- @param[opt] t The tag.
+function tag.getnmaster(t)
+ util.deprecate("Use t.master_count instead of awful.tag.setnmaster")
+
+ t = t or ascreen.focused().selected_tag
+ return tag.getproperty(t, "master_count") or 1
+end
+
+--- Increase the number of master windows.
+-- @function awful.tag.incnmaster
+-- @see master_count
+-- @param add Value to add to number of master windows.
+-- @param[opt] t The tag to modify, if null tag.selected() is used.
+-- @tparam[opt=false] boolean sensible Limit nmaster based on the number of
+-- visible tiled windows?
+function tag.incnmaster(add, t, sensible)
+ t = t or ascreen.focused().selected_tag
+
+ if sensible then
+ local screen = get_screen(tag.getproperty(t, "screen"))
+ local ntiled = #screen.tiled_clients
+
+ local nmaster = tag.object.get_master_count(t)
+ if nmaster > ntiled then
+ nmaster = ntiled
+ end
+
+ local newnmaster = nmaster + add
+ if newnmaster > ntiled then
+ newnmaster = ntiled
+ end
+ tag.object.set_master_count(t, newnmaster)
+ else
+ tag.object.set_master_count(t, tag.object.get_master_count(t) + add)
+ end
+end
+
+--- Set the tag icon.
+--
+-- **Signal:**
+--
+-- * *property::icon*
+--
+-- @property icon
+-- @tparam path|surface icon The icon
+
+-- accessors are implicit.
+
+--- Set the tag icon
+-- @deprecated awful.tag.seticon
+-- @see icon
+-- @param icon the icon to set, either path or image object
+-- @param _tag the tag
+function tag.seticon(icon, _tag)
+ util.deprecate("Use t.icon = icon instead of awful.tag.seticon")
+
+ _tag = _tag or ascreen.focused().selected_tag
+ tag.setproperty(_tag, "icon", icon)
+end
+
+--- Get the tag icon
+-- @deprecated awful.tag.geticon
+-- @see icon
+-- @param _tag the tag
+function tag.geticon(_tag)
+ util.deprecate("Use t.icon instead of awful.tag.geticon")
+
+ _tag = _tag or ascreen.focused().selected_tag
+ return tag.getproperty(_tag, "icon")
+end
+
+--- The default number of columns.
+--
+-- @beautiful beautiful.column_count
+-- @param integer (default: 1)
+-- @see column_count
+
+--- Set the number of columns.
+--
+-- **Signal:**
+--
+-- * *property::ncol* (deprecated)
+-- * *property::column_count*
+--
+-- @property column_count
+-- @tparam integer ncol Has to be greater than 1
+
+function tag.object.set_column_count(t, ncol)
+ if ncol >= 1 then
+ tag.setproperty(t, "ncol", ncol)
+ tag.setproperty(t, "column_count", ncol)
+ end
+end
+
+function tag.object.get_column_count(t)
+ return tag.getproperty(t, "column_count")
+ or beautiful.column_count
+ or defaults.column_count
+end
+
+--- Set number of column windows.
+-- @deprecated awful.tag.setncol
+-- @see column_count
+-- @param ncol The number of column.
+-- @param t The tag to modify, if null tag.selected() is used.
+function tag.setncol(ncol, t)
+ util.deprecate("Use t.column_count = new_index instead of awful.tag.setncol")
+
+ t = t or ascreen.focused().selected_tag
+ if ncol >= 1 then
+ tag.setproperty(t, "ncol", ncol)
+ tag.setproperty(t, "column_count", ncol)
+ end
+end
+
+--- Get number of column windows.
+-- @deprecated awful.tag.getncol
+-- @see column_count
+-- @param[opt] t The tag.
+function tag.getncol(t)
+ util.deprecate("Use t.column_count instead of awful.tag.getncol")
+
+ t = t or ascreen.focused().selected_tag
+ return tag.getproperty(t, "column_count") or 1
+end
+
+--- Increase number of column windows.
+-- @function awful.tag.incncol
+-- @param add Value to add to number of column windows.
+-- @param[opt] t The tag to modify, if null tag.selected() is used.
+-- @tparam[opt=false] boolean sensible Limit column_count based on the number
+-- of visible tiled windows?
+function tag.incncol(add, t, sensible)
+ t = t or ascreen.focused().selected_tag
+
+ if sensible then
+ local screen = get_screen(tag.getproperty(t, "screen"))
+ local ntiled = #screen.tiled_clients
+ local nmaster = tag.object.get_master_count(t)
+ local nsecondary = ntiled - nmaster
+
+ local ncol = tag.object.get_column_count(t)
+ if ncol > nsecondary then
+ ncol = nsecondary
+ end
+
+ local newncol = ncol + add
+ if newncol > nsecondary then
+ newncol = nsecondary
+ end
+
+ tag.object.set_column_count(t, newncol)
+ else
+ tag.object.set_column_count(t, tag.object.get_column_count(t) + add)
+ end
+end
+
+--- View no tag.
+-- @function awful.tag.viewnone
+-- @tparam[opt] int|screen screen The screen.
+function tag.viewnone(screen)
+ screen = screen or ascreen.focused()
+ local tags = screen.tags
+ for _, t in pairs(tags) do
+ t.selected = false
+ end
+end
+
+--- View a tag by its taglist index.
+--
+-- This is equivalent to `screen.tags[i]:view_only()`
+-- @function awful.tag.viewidx
+-- @see screen.tags
+-- @param i The **relative** index to see.
+-- @param[opt] screen The screen.
+function tag.viewidx(i, screen)
+ screen = get_screen(screen or ascreen.focused())
+ local tags = screen.tags
+ local showntags = {}
+ for _, t in ipairs(tags) do
+ if not tag.getproperty(t, "hide") then
+ table.insert(showntags, t)
+ end
+ end
+ local sel = screen.selected_tag
+ tag.viewnone(screen)
+ for k, t in ipairs(showntags) do
+ if t == sel then
+ showntags[util.cycle(#showntags, k + i)].selected = true
+ end
+ end
+ screen:emit_signal("tag::history::update")
+end
+
+--- Get a tag's index in the gettags() table.
+-- @deprecated awful.tag.getidx
+-- @see index
+-- @param query_tag The tag object to find. [selected()]
+-- @return The index of the tag, nil if the tag is not found.
+function tag.getidx(query_tag)
+ util.deprecate("Use t.index instead of awful.tag.getidx")
+
+ return tag.object.get_index(query_tag or ascreen.focused().selected_tag)
+end
+
+--- View next tag. This is the same as tag.viewidx(1).
+-- @function awful.tag.viewnext
+-- @param screen The screen.
+function tag.viewnext(screen)
+ return tag.viewidx(1, screen)
+end
+
+--- View previous tag. This is the same a tag.viewidx(-1).
+-- @function awful.tag.viewprev
+-- @param screen The screen.
+function tag.viewprev(screen)
+ return tag.viewidx(-1, screen)
+end
+
+--- View only a tag.
+-- @function tag.view_only
+-- @see selected
+function tag.object.view_only(self)
+ local tags = self.screen.tags
+ -- First, untag everyone except the viewed tag.
+ for _, _tag in pairs(tags) do
+ if _tag ~= self then
+ _tag.selected = false
+ end
+ end
+ -- Then, set this one to selected.
+ -- We need to do that in 2 operations so we avoid flickering and several tag
+ -- selected at the same time.
+ self.selected = true
+ capi.screen[self.screen]:emit_signal("tag::history::update")
+end
+
+--- View only a tag.
+-- @deprecated awful.tag.viewonly
+-- @see tag.view_only
+-- @param t The tag object.
+function tag.viewonly(t)
+ util.deprecate("Use t:view_only() instead of awful.tag.viewonly")
+
+ tag.object.view_only(t)
+end
+
+--- View only a set of tags.
+-- @function awful.tag.viewmore
+-- @param tags A table with tags to view only.
+-- @param[opt] screen The screen of the tags.
+function tag.viewmore(tags, screen)
+ screen = get_screen(screen or ascreen.focused())
+ local screen_tags = screen.tags
+ for _, _tag in ipairs(screen_tags) do
+ if not util.table.hasitem(tags, _tag) then
+ _tag.selected = false
+ end
+ end
+ for _, _tag in ipairs(tags) do
+ _tag.selected = true
+ end
+ screen:emit_signal("tag::history::update")
+end
+
+--- Toggle selection of a tag
+-- @function awful.tag.viewtoggle
+-- @see selected
+-- @tparam tag t Tag to be toggled
+function tag.viewtoggle(t)
+ t.selected = not t.selected
+ capi.screen[tag.getproperty(t, "screen")]:emit_signal("tag::history::update")
+end
+
+--- Get tag data table.
+--
+-- Do not use.
+--
+-- @deprecated awful.tag.getdata
+-- @tparam tag _tag The tag.
+-- @return The data table.
+function tag.getdata(_tag)
+ return _tag.data.awful_tag_properties
+end
+
+--- Get a tag property.
+--
+-- Use `_tag.prop` directly.
+--
+-- @deprecated awful.tag.getproperty
+-- @tparam tag _tag The tag.
+-- @tparam string prop The property name.
+-- @return The property.
+function tag.getproperty(_tag, prop)
+ if not _tag then return end -- FIXME: Turn this into an error?
+ if _tag.data.awful_tag_properties then
+ return _tag.data.awful_tag_properties[prop]
+ end
+end
+
+--- Set a tag property.
+-- This properties are internal to awful. Some are used to draw taglist, or to
+-- handle layout, etc.
+--
+-- Use `_tag.prop = value`
+--
+-- @deprecated awful.tag.setproperty
+-- @param _tag The tag.
+-- @param prop The property name.
+-- @param value The value.
+function tag.setproperty(_tag, prop, value)
+ if not _tag.data.awful_tag_properties then
+ _tag.data.awful_tag_properties = {}
+ end
+
+ if _tag.data.awful_tag_properties[prop] ~= value then
+ _tag.data.awful_tag_properties[prop] = value
+ _tag:emit_signal("property::" .. prop)
+ end
+end
+
+--- Tag a client with the set of current tags.
+-- @deprecated awful.tag.withcurrent
+-- @param c The client to tag.
+function tag.withcurrent(c)
+ util.deprecate("Use c:to_selected_tags() instead of awful.tag.selectedlist")
+
+ -- It can't use c:to_selected_tags() because awful.tag is loaded before
+ -- awful.client
+
+ local tags = {}
+ for _, t in ipairs(c:tags()) do
+ if get_screen(tag.getproperty(t, "screen")) == get_screen(c.screen) then
+ table.insert(tags, t)
+ end
+ end
+ if #tags == 0 then
+ tags = c.screen.selected_tags
+ end
+ if #tags == 0 then
+ tags = c.screen.tags
+ end
+ if #tags ~= 0 then
+ c:tags(tags)
+ end
+end
+
+local function attached_connect_signal_screen(screen, sig, func)
+ screen = get_screen(screen)
+ capi.tag.connect_signal(sig, function(_tag)
+ if get_screen(tag.getproperty(_tag, "screen")) == screen then
+ func(_tag)
+ end
+ end)
+end
+
+--- Add a signal to all attached tags and all tags that will be attached in the
+-- future. When a tag is detached from the screen, its signal is removed.
+--
+-- @function awful.tag.attached_connect_signal
+-- @screen The screen concerned, or all if nil.
+-- @tparam[opt] string Signal
+-- @tparam[opt] function Callback
+function tag.attached_connect_signal(screen, ...)
+ if screen then
+ attached_connect_signal_screen(screen, ...)
+ else
+ capi.tag.connect_signal(...)
+ end
+end
+
+-- Register standard signals.
+capi.client.connect_signal("property::screen", function(c)
+ -- First, the delayed timer is necessary to avoid a race condition with
+ -- awful.rules. It is also messing up the tags before the user have a chance
+ -- to set them manually.
+ timer.delayed_call(function()
+ local tags, new_tags = c:tags(), {}
+
+ for _, t in ipairs(tags) do
+ if t.screen == c.screen then
+ table.insert(new_tags, t)
+ end
+ end
+
+ if #new_tags == 0 then
+ c:emit_signal("request::tag", nil, {reason="screen"})
+ elseif #new_tags < #tags then
+ c:tags(new_tags)
+ end
+ end)
+end)
+
+-- Keep track of the number of urgent clients.
+local function update_urgent(t, modif)
+ local count = tag.getproperty(t, "urgent_count") or 0
+ count = (count + modif) >= 0 and (count + modif) or 0
+ tag.setproperty(t, "urgent" , count > 0)
+ tag.setproperty(t, "urgent_count", count )
+end
+
+-- Update the urgent counter when a client is tagged.
+local function client_tagged(c, t)
+ if c.urgent then
+ update_urgent(t, 1)
+ end
+end
+
+-- Update the urgent counter when a client is untagged.
+local function client_untagged(c, t)
+ if c.urgent then
+ update_urgent(t, -1)
+ end
+
+ if #t:clients() == 0 and tag.getproperty(t, "volatile") then
+ tag.object.delete(t)
+ end
+end
+
+-- Count the urgent clients.
+local function urgent_callback(c)
+ for _,t in ipairs(c:tags()) do
+ update_urgent(t, c.urgent and 1 or -1)
+ end
+end
+
+capi.client.connect_signal("property::urgent", urgent_callback)
+capi.client.connect_signal("untagged", client_untagged)
+capi.client.connect_signal("tagged", client_tagged)
+capi.tag.connect_signal("request::select", tag.object.view_only)
+
+--- True when a tagged client is urgent
+-- @signal property::urgent
+-- @see client.urgent
+
+--- The number of urgent tagged clients
+-- @signal property::urgent_count
+-- @see client.urgent
+
+capi.screen.connect_signal("tag::history::update", tag.history.update)
+
+capi.screen.connect_signal("removed", function(s)
+ -- First give other code a chance to move the tag to another screen
+ for _, t in pairs(s.tags) do
+ t:emit_signal("request::screen")
+ end
+ -- Everything that's left: Tell everyone that these tags go away (other code
+ -- could e.g. save clients)
+ for _, t in pairs(s.tags) do
+ t:emit_signal("removal-pending")
+ end
+ -- Give other code yet another change to save clients
+ for _, c in pairs(capi.client.get(s)) do
+ c:emit_signal("request::tag", nil, { reason = "screen-removed" })
+ end
+ -- Then force all clients left to go somewhere random
+ local fallback = nil
+ for other_screen in capi.screen do
+ if #other_screen.tags > 0 then
+ fallback = other_screen.tags[1]
+ break
+ end
+ end
+ for _, t in pairs(s.tags) do
+ t:delete(fallback, true)
+ end
+ -- If any tag survived until now, forcefully get rid of it
+ for _, t in pairs(s.tags) do
+ t.activated = false
+
+ if t.data.awful_tag_properties then
+ t.data.awful_tag_properties.screen = nil
+ end
+ end
+end)
+
+function tag.mt:__call(...)
+ return tag.new(...)
+end
+
+-- Extend the luaobject
+-- `awful.tag.setproperty` currently handle calling the setter method itself
+-- while `awful.tag.getproperty`.
+object.properties(capi.tag, {
+ getter_class = tag.object,
+ setter_class = tag.object,
+ getter_fallback = tag.getproperty,
+ setter_fallback = tag.setproperty,
+})
+
+return setmetatable(tag, tag.mt)
+
+-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80