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 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
)
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
86 for id
, name
in ipairs(names
) do
87 table.insert(tags
, id
, add(name
, {screen
= screen
,
88 layout
= (layout
and layout
[id
]) or
90 -- Select the first tag.
92 tags
[id
].selected
= true
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
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
149 c
:tags({fallback_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
159 if selected(target_scr
) == nil then
160 capi
.screen
[target_scr
]:tags()[1].selected
= true
167 --- Update the tag history.
168 -- @param obj Screen object.
169 function history
.update(obj
)
171 local curtags
= capi
.screen
[s
]:tags()
172 -- create history table
173 if not data
.history
[s
] then
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)
207 -- select tags from the history entry
208 for _
, t
in ipairs(data
.history
[s
][i
]) do
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()
226 for i
, t
in pairs(tags
) do
228 vtags
[#vtags
+ 1] = t
234 --- Return only the first visible tag.
235 -- @param s Screen number.
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()
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
)
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
)
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()
306 setproperty(t
, "ncol", ncol
)
310 --- Get number of column windows.
311 -- @param t Optional tag.
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
)
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
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()
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
)
346 for k
, t
in ipairs(showntags
) do
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
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
)
381 -- @param t The tag object.
383 local tags
= capi
.screen
[t
.screen
]:tags()
384 -- First, untag everyone except the viewed tag.
385 for _
, tag in pairs(tags
) do
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.
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
407 for _
, tag in ipairs(tags
) do
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
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
)
466 capi
.screen
[screen
]:add_signal("tag::detach", function (s
, tag)
467 tag:remove_signal(sig
, func
)
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
, ...)
479 attached_add_signal_screen(screen
, ...)
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).
493 and c
.type ~= "desktop"
495 and c
.type ~= "splash" then
496 if c
.transient_for
then
497 c
.screen
= c
.transient_for
.screen
499 c
:tags(c
.transient_for
:tags())
502 c
.screen
= capi
.mouse
.screen
505 c
:add_signal("property::screen", withcurrent
)
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