Functionality for deleting a tag using awful.tag.
[awesome.git] / lib / awful / tag.lua.in
blob4a869327f6b6ae5830f9f4df51feccbf604d200e
1 ---------------------------------------------------------------------------
2 -- @author Julien Danjou <julien@danjou.info>
3 -- @copyright 2008 Julien Danjou
4 -- @release @AWESOME_VERSION@
5 ---------------------------------------------------------------------------
7 -- Grab environment we need
8 local util = require("awful.util")
9 local tostring = tostring
10 local pairs = pairs
11 local ipairs = ipairs
12 local table = table
13 local setmetatable = setmetatable
14 local capi =
16 tag = tag,
17 screen = screen,
18 mouse = mouse,
19 client = client
22 --- Useful functions for tag manipulation.
23 module("awful.tag")
25 -- Private data
26 local data = {}
27 data.history = {}
28 data.tags = setmetatable({}, { __mode = 'k' })
30 -- History functions
31 history = {}
32 history.limit = 20
34 --- Move a tag to an absolute position in the screen[]:tags() table.
35 -- @param new_index Integer absolute position in the table to insert.
36 function move(new_index, target_tag)
37 local target_tag = target_tag or selected()
38 local scr = target_tag.screen
39 local tmp_tags = capi.screen[scr]:tags()
41 if (not new_index) or (new_index < 1) or (new_index > #tmp_tags) then
42 return
43 end
45 for i, t in ipairs(tmp_tags) do
46 if t == target_tag then
47 table.remove(tmp_tags, i)
48 break
49 end
50 end
52 table.insert(tmp_tags, new_index, target_tag)
53 capi.screen[scr]:tags(tmp_tags)
54 end
56 --- Add a tag.
57 -- @param name The tag name, a string
58 -- @param props The tags properties, a table
59 -- @return The created tag or nil if not created.
60 -- If the name is a valid string then the tag will be created and each key,value
61 -- pair in 'props' table is iterated and applied to the tag. If the 'name'
62 -- argument is nil or its string length is zero then the function returns nil.
63 function add(name, props)
64 -- abort if the name is not provided
65 if not name or #(tostring(name)) == 0 then return end
67 local properties = props or {}
68 local newtag = capi.tag{name = name}
69 newtag.screen = properties.screen or capi.mouse.screen
71 for k, v in pairs(properties) do
72 setproperty(newtag, k, v)
73 end
75 return newtag
76 end
78 --- Create a set of tags and attach it to a screen.
79 -- @param names The tag name, in a table
80 -- @param screen The tag screen, or 1 if not set.
81 -- @param layout The layout or layout table to set for this tags by default.
82 -- @return A table with all created tags.
83 function new(names, screen, layout)
84 local screen = screen or 1
85 local tags = {}
86 for id, name in ipairs(names) do
87 table.insert(tags, id, add(name, {screen = screen,
88 layout = (layout and layout[id]) or
89 layout}))
90 -- Select the first tag.
91 if id == 1 then
92 tags[id].selected = true
93 end
94 end
96 return tags
97 end
99 --- Find a suitable fallback tag.
100 -- @param screen The screen number to look for a tag on. [mouse.screen]
101 -- @param target A table of tags we consider unacceptable. [selectedlist(scr)]
102 function find_fallback(screen, invalids)
103 local scr = screen or capi.mouse.screen
104 local t = invalids or selectedlist(scr)
106 for _, v in pairs(capi.screen[scr]:tags()) do
107 if not util.table.hasitem(t, v) then return v end
111 --- Delete a tag.
112 -- @param target_tag Optional tag object to delete. [selected()]
113 -- @param fallback_tag Tag to assign stickied tags to. [~selected()]
114 -- @return Returns true if the tag is successfully deleted, nil otherwise.
115 -- If there are no clients exclusively on this tag then delete it. Any
116 -- stickied clients are assigned to the optional 'fallback_tag'.
117 -- If after deleting the tag there is no selected tag, try and restore from
118 -- history or select the first tag on the screen.
119 function delete(target_tag, fallback_tag)
120 -- abort if no tag is passed or currently selected
121 local target_tag = target_tag or selected()
122 if target_tag == nil then return end
124 local ntags = #capi.screen[target_tag.screen]:tags()
125 local target_scr = target_tag.screen
127 -- We can't use the target tag as a fallback.
128 local fallback_tag = fallback_tag
129 if fallback_tag == target_tag then return end
131 -- No fallback_tag provided, try and get one.
132 if fallback_tag == nil then
133 fallback_tag = find_fallback(target_scr, {target_tag})
136 -- Abort if we would have un-tagged clients.
137 local clients = target_tag:clients()
138 if ( #clients > 0 and ntags <= 1 ) or fallback_tag == nil then return end
140 -- Move the clients we can off of this tag.
141 for _, c in pairs(clients) do
143 -- If a client has only this tag, or stickied clients with
144 -- nowhere to go, abort.
145 if (not c.sticky and #c:tags() == 1) or
146 (c.sticky and fallback_tag == nil) then
147 return
148 else
149 c:tags({fallback_tag})
153 -- delete the tag
154 target_tag.screen = nil
156 -- If no tags are visible, try and view one.
157 if selected(target_scr) == nil and ntags > 0 then
158 history.restore()
159 if selected(target_scr) == nil then
160 capi.screen[target_scr]:tags()[1].selected = true
164 return true
167 --- Update the tag history.
168 -- @param obj Screen object.
169 function history.update(obj)
170 local s = obj.index
171 local curtags = capi.screen[s]:tags()
172 -- create history table
173 if not data.history[s] then
174 data.history[s] = {}
175 -- limit history
176 elseif #data.history[s] >= history.limit then
177 for i = history.limit, #data.history[s] do
178 data.history[s][i] = nil
181 -- store previously selected tags in the history table
182 table.insert(data.history[s], 1, data.history[s].current)
183 data.history[s].previous = data.history[s][1]
184 -- store currently selected tags
185 data.history[s].current = setmetatable(selectedlist(s), { __mode = 'v' })
188 --- Revert tag history.
189 -- @param screen The screen number.
190 -- @param idx Index in history. Defaults to "previous" which is a special index
191 -- toggling between last two selected sets of tags. Number (eg 1) will go back
192 -- to the given index in history.
193 function history.restore(screen, idx)
194 local s = screen or capi.mouse.screen
195 local i = idx or "previous"
196 local sel = selectedlist(s)
197 -- do nothing if history empty
198 if not data.history[s] or not data.history[s][i] then return end
199 -- if all tags been deleted, try next entry
200 if #data.history[s][i] == 0 then
201 if i == "previous" then i = 0 end
202 history.restore(s, i + 1)
203 return
205 -- deselect all tags
206 viewnone(s)
207 -- select tags from the history entry
208 for _, t in ipairs(data.history[s][i]) do
209 t.selected = true
211 -- update currently selected tags table
212 data.history[s].current = data.history[s][i]
213 -- store previously selected tags
214 data.history[s].previous = setmetatable(sel, { __mode = 'v' })
215 -- remove the reverted history entry
216 if i ~= "previous" then table.remove(data.history[s], i) end
219 --- Return a table with all visible tags
220 -- @param s Screen number.
221 -- @return A table with all selected tags.
222 function selectedlist(s)
223 local screen = s or capi.mouse.screen
224 local tags = capi.screen[screen]:tags()
225 local vtags = {}
226 for i, t in pairs(tags) do
227 if t.selected then
228 vtags[#vtags + 1] = t
231 return vtags
234 --- Return only the first visible tag.
235 -- @param s Screen number.
236 function selected(s)
237 return selectedlist(s)[1]
240 --- Set master width factor.
241 -- @param mwfact Master width factor.
242 function setmwfact(mwfact, t)
243 local t = t or selected()
244 if mwfact >= 0 and mwfact <= 1 then
245 setproperty(t, "mwfact", mwfact)
249 --- Increase master width factor.
250 -- @param add Value to add to master width factor.
251 function incmwfact(add, t)
252 setmwfact(getmwfact(t) + add)
255 --- Get master width factor.
256 -- @param t Optional tag.
257 function getmwfact(t)
258 local t = t or selected()
259 return getproperty(t, "mwfact") or 0.5
262 --- Set the number of master windows.
263 -- @param nmaster The number of master windows.
264 -- @param t Optional tag.
265 function setnmaster(nmaster, t)
266 local t = t or selected()
267 if nmaster >= 0 then
268 setproperty(t, "nmaster", nmaster)
272 --- Get the number of master windows.
273 -- @param t Optional tag.
274 function getnmaster(t)
275 local t = t or selected()
276 return getproperty(t, "nmaster") or 1
279 --- Increase the number of master windows.
280 -- @param add Value to add to number of master windows.
281 function incnmaster(add, t)
282 setnmaster(getnmaster(t) + add)
286 --- Set the tag icon
287 -- @param icon the icon to set, either path or image object
288 -- @param tag the tag
289 function seticon(icon, tag)
290 local tag = tag or selected()
291 setproperty(tag, "icon", icon)
294 --- Get the tag icon
295 -- @param t the tag
296 function geticon(tag)
297 local tag = tag or selected()
298 return getproperty(tag, "icon")
301 --- Set number of column windows.
302 -- @param ncol The number of column.
303 function setncol(ncol, t)
304 local t = t or selected()
305 if ncol >= 1 then
306 setproperty(t, "ncol", ncol)
310 --- Get number of column windows.
311 -- @param t Optional tag.
312 function getncol(t)
313 local t = t or selected()
314 return getproperty(t, "ncol") or 1
317 --- Increase number of column windows.
318 -- @param add Value to add to number of column windows.
319 function incncol(add, t)
320 setncol(getncol(t) + add)
323 --- View no tag.
324 -- @param Optional screen number.
325 function viewnone(screen)
326 local tags = capi.screen[screen or capi.mouse.screen]:tags()
327 for i, t in pairs(tags) do
328 t.selected = false
332 --- View a tag by its taglist index.
333 -- @param i The relative index to see.
334 -- @param screen Optional screen number.
335 function viewidx(i, screen)
336 local screen = screen and screen.index or capi.mouse.screen
337 local tags = capi.screen[screen]:tags()
338 local showntags = {}
339 for k, t in ipairs(tags) do
340 if not getproperty(t, "hide") then
341 table.insert(showntags, t)
344 local sel = selected(screen)
345 viewnone(screen)
346 for k, t in ipairs(showntags) do
347 if t == sel then
348 showntags[util.cycle(#showntags, k + i)].selected = true
351 capi.screen[screen]:emit_signal("tag::history::update")
354 --- Get a tag's index in the screen[]:tags() table.
355 -- @param query_tag The tag object to find. [selected()]
356 -- @return The index of the tag, nil if the tag is not found.
357 function getidx(query_tag)
358 local query_tag = query_tag or selected()
359 if query_tag == nil then return end
361 for i, t in ipairs(capi.screen[query_tag.screen]:tags()) do
362 if t == query_tag then
363 return i
368 --- View next tag. This is the same as tag.viewidx(1).
369 -- @param screen The screen number.
370 function viewnext(screen)
371 return viewidx(1, screen)
374 --- View previous tag. This is the same a tag.viewidx(-1).
375 -- @param screen The screen number.
376 function viewprev(screen)
377 return viewidx(-1, screen)
380 --- View only a tag.
381 -- @param t The tag object.
382 function viewonly(t)
383 local tags = capi.screen[t.screen]:tags()
384 -- First, untag everyone except the viewed tag.
385 for _, tag in pairs(tags) do
386 if tag ~= t then
387 tag.selected = false
390 -- Then, set this one to selected.
391 -- We need to do that in 2 operations so we avoid flickering and several tag
392 -- selected at the same time.
393 t.selected = true
394 capi.screen[t.screen]:emit_signal("tag::history::update")
397 --- View only a set of tags.
398 -- @param tags A table with tags to view only.
399 -- @param screen Optional screen number of the tags.
400 function viewmore(tags, screen)
401 local screen_tags = capi.screen[screen or capi.mouse.screen]:tags()
402 for _, tag in ipairs(screen_tags) do
403 if not util.table.hasitem(tags, tag) then
404 tag.selected = false
407 for _, tag in ipairs(tags) do
408 tag.selected = true
410 capi.screen[screen]:emit_signal("tag::history::update")
413 --- Toggle selection of a tag
414 -- @param tag Tag to be toggled
415 function viewtoggle(t)
416 t.selected = not t.selected
417 capi.screen[t.screen]:emit_signal("tag::history::update")
420 --- Get tag data table.
421 -- @param tag The Tag.
422 -- @return The data table.
423 function getdata(tag)
424 return data.tags[tag]
427 --- Get a tag property.
428 -- @param tag The tag.
429 -- @param prop The property name.
430 -- @return The property.
431 function getproperty(tag, prop)
432 if data.tags[tag] then
433 return data.tags[tag][prop]
437 --- Set a tag property.
438 -- This properties are internal to awful. Some are used to draw taglist, or to
439 -- handle layout, etc.
440 -- @param tag The tag.
441 -- @param prop The property name.
442 -- @param value The value.
443 function setproperty(tag, prop, value)
444 if not data.tags[tag] then
445 data.tags[tag] = {}
447 data.tags[tag][prop] = value
448 tag:emit_signal("property::" .. prop)
451 --- Tag a client with the set of current tags.
452 -- @param c The client to tag.
453 -- @param startup Optional: don't do anything if true.
454 function withcurrent(c, startup)
455 if startup ~= true and c.sticky == false then
456 if #c:tags() == 0 then
457 c:tags(selectedlist(c.screen))
462 local function attached_add_signal_screen(screen, sig, func)
463 capi.screen[screen]:add_signal("tag::attach", function (s, tag)
464 tag:add_signal(sig, func)
465 end)
466 capi.screen[screen]:add_signal("tag::detach", function (s, tag)
467 tag:remove_signal(sig, func)
468 end)
469 for _, tag in ipairs(capi.screen[screen]:tags()) do
470 tag:add_signal(sig, func)
474 --- Add a signal to all attached tag and all tag that will be attached in the
475 -- future. When a tag is detach from the screen, its signal is removed.
476 -- @param screen The screen concerned, or all if nil.
477 function attached_add_signal(screen, ...)
478 if screen then
479 attached_add_signal_screen(screen, ...)
480 else
481 for screen = 1, capi.screen.count() do
482 attached_add_signal_screen(screen, ...)
487 -- Register standards signals
488 capi.client.add_signal("manage", function(c, startup)
489 -- If we are not managing this application at startup,
490 -- move it to the screen where the mouse is.
491 -- We only do it for "normal" windows (i.e. no dock, etc).
492 if not startup
493 and c.type ~= "desktop"
494 and c.type ~= "dock"
495 and c.type ~= "splash" then
496 if c.transient_for then
497 c.screen = c.transient_for.screen
498 if not c.sticky then
499 c:tags(c.transient_for:tags())
501 else
502 c.screen = capi.mouse.screen
505 c:add_signal("property::screen", withcurrent)
506 end)
508 capi.client.add_signal("manage", withcurrent)
510 for s = 1, capi.screen.count() do
511 capi.screen[s]:add_signal("tag::history::update", history.update)
514 setmetatable(_M, { __call = function (_, ...) return new(...) end })
516 -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80