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
13 local setmetatable
= setmetatable
22 --- Useful functions for tag manipulation.
28 data
.tags
= setmetatable({}, { __mode
= 'k' })
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
45 for i
, t
in ipairs(tmp_tags
) do
46 if t
== target_tag
then
47 table.remove(tmp_tags
, i
)
52 table.insert(tmp_tags
, new_index
, target_tag
)
53 capi
.screen
[scr
]:tags(tmp_tags
)
57 -- @param name The tag name, a string
58 -- @param props The tags properties, a table
59 -- @return The created tag
60 function add(name
, props
)
61 local properties
= props
or {}
62 local newtag = capi
.tag{name
= name
}
63 newtag.screen
= properties
.screen
or capi
.mouse
.screen
65 for k
, v
in pairs(properties
) do
66 setproperty(newtag, k
, v
)
72 --- Create a set of tags and attach it to a screen.
73 -- @param names The tag name, in a table
74 -- @param screen The tag screen, or 1 if not set.
75 -- @param layout The layout or layout table to set for this tags by default.
76 -- @return A table with all created tags.
77 function new(names
, screen
, layout
)
78 local screen
= screen
or 1
80 for id
, name
in ipairs(names
) do
81 table.insert(tags
, id
, add(name
, {screen
= screen
,
82 layout
= (layout
and layout
[id
]) or
84 -- Select the first tag.
86 tags
[id
].selected
= true
93 --- Find a suitable fallback tag.
94 -- @param screen The screen number to look for a tag on. [mouse.screen]
95 -- @param target A table of tags we consider unacceptable. [selectedlist(scr)]
96 function find_fallback(screen
, invalids
)
97 local scr
= screen
or capi
.mouse
.screen
98 local t
= invalids
or selectedlist(scr
)
100 for _
, v
in pairs(capi
.screen
[scr
]:tags()) do
101 if not util
.table.hasitem(t
, v
) then return v
end
106 -- @param target_tag Optional tag object to delete. [selected()]
107 -- @param fallback_tag Tag to assign stickied tags to. [~selected()]
108 -- @return Returns true if the tag is successfully deleted, nil otherwise.
109 -- If there are no clients exclusively on this tag then delete it. Any
110 -- stickied clients are assigned to the optional 'fallback_tag'.
111 -- If after deleting the tag there is no selected tag, try and restore from
112 -- history or select the first tag on the screen.
113 function delete(target_tag
, fallback_tag
)
114 -- abort if no tag is passed or currently selected
115 local target_tag
= target_tag
or selected()
116 if target_tag
== nil then return end
118 local ntags
= #capi
.screen
[target_tag
.screen
]:tags()
119 local target_scr
= target_tag
.screen
121 -- We can't use the target tag as a fallback.
122 local fallback_tag
= fallback_tag
123 if fallback_tag
== target_tag
then return end
125 -- No fallback_tag provided, try and get one.
126 if fallback_tag
== nil then
127 fallback_tag
= find_fallback(target_scr
, {target_tag
})
130 -- Abort if we would have un-tagged clients.
131 local clients
= target_tag
:clients()
132 if ( #clients
> 0 and ntags
<= 1 ) or fallback_tag
== nil then return end
134 -- Move the clients we can off of this tag.
135 for _
, c
in pairs(clients
) do
137 -- If a client has only this tag, or stickied clients with
138 -- nowhere to go, abort.
139 if (not c
.sticky
and #c
:tags() == 1) or
140 (c
.sticky
and fallback_tag
== nil) then
143 c
:tags({fallback_tag
})
148 target_tag
.screen
= nil
150 -- If no tags are visible, try and view one.
151 if selected(target_scr
) == nil and ntags
> 0 then
153 if selected(target_scr
) == nil then
154 capi
.screen
[target_scr
]:tags()[1].selected
= true
161 --- Update the tag history.
162 -- @param obj Screen object.
163 function history
.update(obj
)
165 local curtags
= selectedlist(s
)
166 -- create history table
167 if not data
.history
[s
] then
170 if data
.history
[s
].current
then
171 -- Check that the list is not identical
172 local identical
= true
173 for idx
, tag in ipairs(data
.history
[s
].current
) do
174 if curtags
[idx
] ~= tag then
180 -- Do not update history the table are identical
181 if identical
then return end
185 if #data
.history
[s
] >= history
.limit
then
186 for i
= history
.limit
, #data
.history
[s
] do
187 data
.history
[s
][i
] = nil
192 -- store previously selected tags in the history table
193 table.insert(data
.history
[s
], 1, data
.history
[s
].current
)
194 data
.history
[s
].previous
= data
.history
[s
][1]
195 -- store currently selected tags
196 data
.history
[s
].current
= setmetatable(curtags
, { __mode
= 'v' })
199 --- Revert tag history.
200 -- @param screen The screen number.
201 -- @param idx Index in history. Defaults to "previous" which is a special index
202 -- toggling between last two selected sets of tags. Number (eg 1) will go back
203 -- to the given index in history.
204 function history
.restore(screen
, idx
)
205 local s
= screen
or capi
.mouse
.screen
206 local i
= idx
or "previous"
207 local sel
= selectedlist(s
)
208 -- do nothing if history empty
209 if not data
.history
[s
] or not data
.history
[s
][i
] then return end
210 -- if all tags been deleted, try next entry
211 if #data
.history
[s
][i
] == 0 then
212 if i
== "previous" then i
= 0 end
213 history
.restore(s
, i
+ 1)
218 -- select tags from the history entry
219 for _
, t
in ipairs(data
.history
[s
][i
]) do
222 -- update currently selected tags table
223 data
.history
[s
].current
= data
.history
[s
][i
]
224 -- store previously selected tags
225 data
.history
[s
].previous
= setmetatable(sel
, { __mode
= 'v' })
226 -- remove the reverted history entry
227 if i
~= "previous" then table.remove(data
.history
[s
], i
) end
230 --- Return a table with all visible tags
231 -- @param s Screen number.
232 -- @return A table with all selected tags.
233 function selectedlist(s
)
234 local screen
= s
or capi
.mouse
.screen
235 local tags
= capi
.screen
[screen
]:tags()
237 for i
, t
in pairs(tags
) do
239 vtags
[#vtags
+ 1] = t
245 --- Return only the first visible tag.
246 -- @param s Screen number.
248 return selectedlist(s
)[1]
251 --- Set master width factor.
252 -- @param mwfact Master width factor.
253 function setmwfact(mwfact
, t
)
254 local t
= t
or selected()
255 if mwfact
>= 0 and mwfact
<= 1 then
256 setproperty(t
, "mwfact", mwfact
)
260 --- Increase master width factor.
261 -- @param add Value to add to master width factor.
262 function incmwfact(add
, t
)
263 setmwfact(getmwfact(t
) + add
)
266 --- Get master width factor.
267 -- @param t Optional tag.
268 function getmwfact(t
)
269 local t
= t
or selected()
270 return getproperty(t
, "mwfact") or 0.5
273 --- Set the number of master windows.
274 -- @param nmaster The number of master windows.
275 -- @param t Optional tag.
276 function setnmaster(nmaster
, t
)
277 local t
= t
or selected()
279 setproperty(t
, "nmaster", nmaster
)
283 --- Get the number of master windows.
284 -- @param t Optional tag.
285 function getnmaster(t
)
286 local t
= t
or selected()
287 return getproperty(t
, "nmaster") or 1
290 --- Increase the number of master windows.
291 -- @param add Value to add to number of master windows.
292 function incnmaster(add
, t
)
293 setnmaster(getnmaster(t
) + add
)
298 -- @param icon the icon to set, either path or image object
299 -- @param tag the tag
300 function seticon(icon
, tag)
301 local tag = tag or selected()
302 setproperty(tag, "icon", icon
)
307 function geticon(tag)
308 local tag = tag or selected()
309 return getproperty(tag, "icon")
312 --- Set number of column windows.
313 -- @param ncol The number of column.
314 function setncol(ncol
, t
)
315 local t
= t
or selected()
317 setproperty(t
, "ncol", ncol
)
321 --- Get number of column windows.
322 -- @param t Optional tag.
324 local t
= t
or selected()
325 return getproperty(t
, "ncol") or 1
328 --- Increase number of column windows.
329 -- @param add Value to add to number of column windows.
330 function incncol(add
, t
)
331 setncol(getncol(t
) + add
)
335 -- @param Optional screen number.
336 function viewnone(screen
)
337 local tags
= capi
.screen
[screen
or capi
.mouse
.screen
]:tags()
338 for i
, t
in pairs(tags
) do
343 --- View a tag by its taglist index.
344 -- @param i The relative index to see.
345 -- @param screen Optional screen number.
346 function viewidx(i
, screen
)
347 local screen
= screen
and screen
.index
or capi
.mouse
.screen
348 local tags
= capi
.screen
[screen
]:tags()
350 for k
, t
in ipairs(tags
) do
351 if not getproperty(t
, "hide") then
352 table.insert(showntags
, t
)
355 local sel
= selected(screen
)
357 for k
, t
in ipairs(showntags
) do
359 showntags
[util
.cycle(#showntags
, k
+ i
)].selected
= true
362 capi
.screen
[screen
]:emit_signal("tag::history::update")
365 --- Get a tag's index in the screen[]:tags() table.
366 -- @param query_tag The tag object to find. [selected()]
367 -- @return The index of the tag, nil if the tag is not found.
368 function getidx(query_tag
)
369 local query_tag
= query_tag
or selected()
370 if query_tag
== nil then return end
372 for i
, t
in ipairs(capi
.screen
[query_tag
.screen
]:tags()) do
373 if t
== query_tag
then
379 --- View next tag. This is the same as tag.viewidx(1).
380 -- @param screen The screen number.
381 function viewnext(screen
)
382 return viewidx(1, screen
)
385 --- View previous tag. This is the same a tag.viewidx(-1).
386 -- @param screen The screen number.
387 function viewprev(screen
)
388 return viewidx(-1, screen
)
392 -- @param t The tag object.
394 local tags
= capi
.screen
[t
.screen
]:tags()
395 -- First, untag everyone except the viewed tag.
396 for _
, tag in pairs(tags
) do
401 -- Then, set this one to selected.
402 -- We need to do that in 2 operations so we avoid flickering and several tag
403 -- selected at the same time.
405 capi
.screen
[t
.screen
]:emit_signal("tag::history::update")
408 --- View only a set of tags.
409 -- @param tags A table with tags to view only.
410 -- @param screen Optional screen number of the tags.
411 function viewmore(tags
, screen
)
412 local screen_tags
= capi
.screen
[screen
or capi
.mouse
.screen
]:tags()
413 for _
, tag in ipairs(screen_tags
) do
414 if not util
.table.hasitem(tags
, tag) then
418 for _
, tag in ipairs(tags
) do
421 capi
.screen
[screen
]:emit_signal("tag::history::update")
424 --- Toggle selection of a tag
425 -- @param tag Tag to be toggled
426 function viewtoggle(t
)
427 t
.selected
= not t
.selected
428 capi
.screen
[t
.screen
]:emit_signal("tag::history::update")
431 --- Get tag data table.
432 -- @param tag The Tag.
433 -- @return The data table.
434 function getdata(tag)
435 return data
.tags
[tag]
438 --- Get a tag property.
439 -- @param tag The tag.
440 -- @param prop The property name.
441 -- @return The property.
442 function getproperty(tag, prop
)
443 if data
.tags
[tag] then
444 return data
.tags
[tag][prop
]
448 --- Set a tag property.
449 -- This properties are internal to awful. Some are used to draw taglist, or to
450 -- handle layout, etc.
451 -- @param tag The tag.
452 -- @param prop The property name.
453 -- @param value The value.
454 function setproperty(tag, prop
, value
)
455 if not data
.tags
[tag] then
458 data
.tags
[tag][prop
] = value
459 tag:emit_signal("property::" .. prop
)
462 --- Tag a client with the set of current tags.
463 -- @param c The client to tag.
464 -- @param startup Optional: don't do anything if true.
465 function withcurrent(c
, startup
)
466 if startup
~= true then
467 if #c
:tags() == 0 then
468 c
:tags(selectedlist(c
.screen
))
473 local function attached_connect_signal_screen(screen
, sig
, func
)
474 capi
.screen
[screen
]:connect_signal("tag::attach", function (s
, tag)
475 tag:connect_signal(sig
, func
)
477 capi
.screen
[screen
]:connect_signal("tag::detach", function (s
, tag)
478 tag:disconnect_signal(sig
, func
)
480 for _
, tag in ipairs(capi
.screen
[screen
]:tags()) do
481 tag:connect_signal(sig
, func
)
485 --- Add a signal to all attached tag and all tag that will be attached in the
486 -- future. When a tag is detach from the screen, its signal is removed.
487 -- @param screen The screen concerned, or all if nil.
488 function attached_connect_signal(screen
, ...)
490 attached_connect_signal_screen(screen
, ...)
492 for screen
= 1, capi
.screen
.count() do
493 attached_connect_signal_screen(screen
, ...)
498 -- Register standards signals
499 capi
.client
.connect_signal("manage", function(c
, startup
)
500 -- If we are not managing this application at startup,
501 -- move it to the screen where the mouse is.
502 -- We only do it for "normal" windows (i.e. no dock, etc).
504 and c
.type ~= "desktop"
506 and c
.type ~= "splash" then
507 if c
.transient_for
then
508 c
.screen
= c
.transient_for
.screen
510 c
:tags(c
.transient_for
:tags())
513 c
.screen
= capi
.mouse
.screen
516 c
:connect_signal("property::screen", withcurrent
)
519 capi
.client
.connect_signal("manage", withcurrent
)
521 capi
.tag.add_signal("property::hide")
522 capi
.tag.add_signal("property::icon")
523 capi
.tag.add_signal("property::layout")
524 capi
.tag.add_signal("property::mwfact")
525 capi
.tag.add_signal("property::ncol")
526 capi
.tag.add_signal("property::nmaster")
527 capi
.tag.add_signal("property::windowfact")
529 for s
= 1, capi
.screen
.count() do
530 capi
.screen
[s
]:add_signal("tag::history::update")
531 capi
.screen
[s
]:connect_signal("tag::history::update", history
.update
)
534 setmetatable(_M
, { __call
= function (_
, ...) return new(...) end })
536 -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80