1 ---------------------------------------------------------------------------
2 -- @author Julien Danjou <julien@danjou.info>
3 -- @copyright 2008 Julien Danjou
4 -- @release @AWESOME_VERSION@
5 ---------------------------------------------------------------------------
7 -- Grab environment we need
11 local loadstring
= loadstring
17 local setmetatable
= setmetatable
29 keygrabber
= keygrabber
32 --- awful: AWesome Functions very UsefuL
35 -- Local variable handling theme
38 -- Various public structures
44 prompt
.history
.data
= {}
50 client
.data
.maximize
= otable()
52 client
.focus
.history
= {}
53 client
.focus
.history
.data
= {}
57 tag.history
.data
.past
= {}
58 tag.history
.data
.current
= {}
60 titlebar
.data
= otable()
63 widget
.taglist
.label
= {}
65 widget
.tasklist
.label
= {}
67 client
.urgent
.stack
= {}
68 client
.urgent
.stack
.data
= {}
72 -- @param i An absolute index to fit into #t.
73 -- @return The object at new index.
74 local function cycle(t
, i
)
75 while i
> t
do i
= i
- t
end
76 while i
< 1 do i
= i
+ t
end
80 --- Get the first client that got the urgent hint.
81 -- @return The first urgent client.
82 function client
.urgent
.get()
83 if #client
.urgent
.stack
.data
> 0 then
84 return client
.urgent
.stack
.data
[1]
86 -- fallback behaviour: iterate through clients and get the first urgent
87 local clients
= capi
.client
.get()
88 for k
, cl
in pairs(clients
) do
96 --- Jump to the client that received the urgent hint first.
97 function client
.urgent
.jumpto()
98 local c
= client
.urgent
.get()
100 local s
= capi
.client
.focus
and capi
.client
.focus
.screen
or capi
.mouse
.screen
102 if s
~= c
.screen
then
103 capi
.mouse
.screen
= c
.screen
106 tag.viewonly(c
:tags()[1])
108 capi
.client
.focus
= c
113 --- Adds client to urgent stack.
114 -- @param The client object.
115 function client
.urgent
.stack
.add(c
)
116 table.insert(client
.urgent
.stack
.data
, c
)
119 --- Remove client from urgent stack.
120 -- @param The client object.
121 function client
.urgent
.stack
.delete(c
)
122 for k
, cl
in ipairs(client
.urgent
.stack
.data
) do
124 table.remove(client
.urgent
.stack
.data
, k
)
130 --- Remove a client from the focus history
131 -- @param c The client that must be removed.
132 function client
.focus
.history
.delete(c
)
133 for k
, v
in ipairs(client
.focus
.history
.data
) do
135 table.remove(client
.focus
.history
.data
, k
)
141 --- Update client focus history.
142 -- @param c The client that has been focused.
143 function client
.focus
.history
.add(c
)
144 -- Remove the client if its in stack
145 client
.focus
.history
.delete(c
)
146 -- Record the client has latest focused
147 table.insert(client
.focus
.history
.data
, 1, c
)
150 --- Get the latest focused client for a screen in history.
151 -- @param screen The screen number to look for.
152 -- @param idx The index: 0 will return first candidate,
153 -- 1 will return second, etc.
155 function client
.focus
.history
.get(screen
, idx
)
156 -- When this counter is equal to idx, we return the client
158 local vc
= capi
.client
.visible_get(screen
)
159 for k
, c
in ipairs(client
.focus
.history
.data
) do
160 if c
.screen
== screen
then
161 for j
, vcc
in ipairs(vc
) do
163 if counter
== idx
then
166 -- We found one, increment the counter only.
167 counter
= counter
+ 1
173 -- Argh nobody found in history, give the first one visible if there is one
179 --- Focus the previous client in history.
180 function client
.focus
.history
.previous()
181 local sel
= capi
.client
.focus
186 s
= capi
.mouse
.screen
188 local c
= client
.focus
.history
.get(s
, 1)
189 if c
then capi
.client
.focus
= c
end
192 --- Get a client by its relative index to the focused window.
193 -- @usage Set i to 1 to get next, -1 to get previous.
194 -- @param i The index.
195 -- @param c Optional client.
196 -- @return A client, or nil if no client is available.
197 function client
.next(i
, c
)
198 -- Get currently focused client
199 local sel
= c
or capi
.client
.focus
201 -- Get all visible clients
202 local cls
= capi
.client
.visible_get(sel
.screen
)
203 -- Loop upon each client
204 for idx
, c
in ipairs(cls
) do
207 return cls
[cycle(#cls
, idx
+i
)]
213 --- Return true whether client B is in the right direction
214 -- compared to client A.
215 -- @param dir The direction.
216 -- @param cA The first client.
217 -- @param cB The second client.
218 -- @return True if B is in the direction of A.
219 local function is_in_direction(dir
, cA
, cB
)
221 return cA
['y'] > cB
['y']
222 elseif dir
== "down" then
223 return cA
['y'] < cB
['y']
224 elseif dir
== "left" then
225 return cA
['x'] > cB
['x']
226 elseif dir
== "right" then
227 return cA
['x'] < cB
['x']
232 --- Calculate distance between two points.
233 -- i.e: if we want to move to the right, we will take the right border
234 -- of the currently focused client and the left side of the checked client.
235 -- This avoid the focus of an upper client when you move to the right in a
236 -- tilebottom layout with nmaster=2 and 5 clients open, for instance.
237 -- @param dir The direction.
238 -- @param cA The first client.
239 -- @param cB The second client.
240 -- @return The distance between the clients.
241 local function calculate_distance(dir
, cA
, cB
)
248 yB
= yB
+ cB
['height']
249 elseif dir
== "down" then
250 yA
= yA
+ cA
['height']
251 elseif dir
== "left" then
252 xB
= xB
+ cB
['width']
253 elseif dir
== "right" then
254 xA
= xA
+ cA
['width']
257 return math
.sqrt(math
.pow(xB
- xA
, 2) + math
.pow(yB
- yA
, 2))
260 --- Focus a client by the given direction.
261 -- @param dir The direction, can be either "up", "down", "left" or "right".
262 -- @param c Optional client.
263 function client
.focusbydirection(dir
, c
)
264 local sel
= c
or capi
.client
.focus
266 local coords
= sel
.coords
269 local cls
= capi
.client
.visible_get(sel
.screen
)
271 -- We check each client.
272 for i
, c
in ipairs(cls
) do
273 -- Check coords to see if client is located in the right direction.
274 if is_in_direction(dir
, coords
, c
.coords
) then
276 -- Calculate distance between focused client and checked client.
277 dist
= calculate_distance(dir
, coords
, c
.coords
)
279 -- If distance is shorter then keep the client.
280 if not target
or dist
< dist_min
then
287 -- If we found a client to focus, then do it.
289 capi
.client
.focus
= target
294 --- Focus a client by its relative index.
295 -- @param i The index.
296 -- @param c Optional client.
297 function client
.focusbyidx(i
, c
)
298 local target
= client
.next(i
, c
)
300 capi
.client
.focus
= target
304 --- Swap a client by its relative index.
305 -- @param i The index.
306 -- @param c Optional client, otherwise focused one is used.
307 function client
.swap(i
, c
)
308 local sel
= c
or capi
.client
.focus
309 local target
= client
.next(i
, sel
)
315 --- Get the master window.
316 -- @param screen Optional screen number, otherwise screen mouse is used.
317 -- @return The master window.
318 function client
.master(screen
)
319 local s
= screen
or capi
.mouse
.screen
320 return capi
.client
.visible_get(s
)[1]
323 --- Move/resize a client relative to current coordinates.
324 -- @param x The relative x coordinate.
325 -- @param y The relative y coordinate.
326 -- @param w The relative width.
327 -- @param h The relative height.
328 -- @param c The optional client, otherwise focused one is used.
329 function client
.moveresize(x
, y
, w
, h
, c
)
330 local sel
= c
or capi
.client
.focus
331 local coords
= sel
.coords
332 coords
['x'] = coords
['x'] + x
333 coords
['y'] = coords
['y'] + y
334 coords
['width'] = coords
['width'] + w
335 coords
['height'] = coords
['height'] + h
339 --- Maximize a client to use the full workarea.
340 -- @param c A client, or the focused one if nil.
341 function client
.maximize(c
)
342 local sel
= c
or capi
.client
.focus
344 local ws
= capi
.screen
[sel
.screen
].workarea
345 ws
.width
= ws
.width
- 2 * sel
.border_width
346 ws
.height
= ws
.height
- 2 * sel
.border_width
347 if sel
.floating
and client
.data
.maximize
[sel
] then
348 sel
.floating
= client
.data
.maximize
[sel
].floating
350 sel
.coords
= client
.data
.maximize
[sel
].coords
352 client
.data
.maximize
[sel
] = nil
354 client
.data
.maximize
[sel
] = { coords
= sel
.coords
, floating
= sel
.floating
}
362 --- Erase eventual client data in maximize.
363 -- @param c The client.
364 local function client_maximize_clean(c
)
365 client
.data
.maximize
[c
] = nil
368 --- Give the focus to a screen, and move pointer.
369 -- @param Screen number.
370 function screen
.focus(i
)
371 local s
= cycle(capi
.screen
.count(), capi
.mouse
.screen
+ i
)
372 local c
= client
.focus
.history
.get(s
, 0)
373 if c
then capi
.client
.focus
= c
end
374 -- Move the mouse on the screen
375 capi
.mouse
.screen
= s
378 --- Compare 2 tables of tags.
379 -- @param a The first table.
380 -- @param b The second table of tags.
381 -- @return True if the tables are identical, false otherwise.
382 local function tag_compare_select(a
, b
)
383 if not a
or not b
then
386 -- Quick size comparison
390 for ka
, va
in pairs(a
) do
391 if b
[ka
] ~= va
.selected
then
395 for kb
, vb
in pairs(b
) do
396 if a
[kb
].selected
~= vb
then
403 --- Update the tag history.
404 -- @param screen The screen number.
405 function tag.history
.update(screen
)
406 local curtags
= capi
.screen
[screen
]:tags()
407 if not tag_compare_select(curtags
, tag.history
.data
.current
[screen
]) then
408 tag.history
.data
.past
[screen
] = tag.history
.data
.current
[screen
]
409 tag.history
.data
.current
[screen
] = {}
410 for k
, v
in ipairs(curtags
) do
411 tag.history
.data
.current
[screen
][k
] = v
.selected
416 -- Revert tag history.
417 -- @param screen The screen number.
418 function tag.history
.restore(screen
)
419 local s
= screen
or capi
.mouse
.screen
420 local tags
= capi
.screen
[s
]:tags()
421 for k
, t
in pairs(tags
) do
422 t
.selected
= tag.history
.data
.past
[s
][k
]
426 --- Return a table with all visible tags
427 -- @param s Screen number.
428 -- @return A table with all selected tags.
429 function tag.selectedlist(s
)
430 local screen
= s
or capi
.mouse
.screen
431 local tags
= capi
.screen
[screen
]:tags()
433 for i
, t
in pairs(tags
) do
435 vtags
[#vtags
+ 1] = t
441 --- Return only the first visible tag.
442 -- @param s Screen number.
443 function tag.selected(s
)
444 return tag.selectedlist(s
)[1]
447 --- Set master width factor.
448 -- @param mwfact Master width factor.
449 function tag.setmwfact(mwfact
)
450 local t
= tag.selected()
456 --- Increase master width factor.
457 -- @param add Value to add to master width factor.
458 function tag.incmwfact(add
)
459 local t
= tag.selected()
461 t
.mwfact
= t
.mwfact
+ add
465 --- Set the number of master windows.
466 -- @param nmaster The number of master windows.
467 function tag.setnmaster(nmaster
)
468 local t
= tag.selected()
474 --- Increase the number of master windows.
475 -- @param add Value to add to number of master windows.
476 function tag.incnmaster(add
)
477 local t
= tag.selected()
479 t
.nmaster
= t
.nmaster
+ add
483 --- Set number of column windows.
484 -- @param ncol The number of column.
485 function tag.setncol(ncol
)
486 local t
= tag.selected()
492 --- Increase number of column windows.
493 -- @param add Value to add to number of column windows.
494 function tag.incncol(add
)
495 local t
= tag.selected()
497 t
.ncol
= t
.ncol
+ add
502 -- @param Optional screen number.
503 function tag.viewnone(screen
)
504 local tags
= capi
.screen
[screen
or capi
.mouse
.screen
]:tags()
505 for i
, t
in pairs(tags
) do
510 --- View a tag by its index.
511 -- @param i The relative index to see.
512 -- @param screen Optional screen number.
513 function tag.viewidx(i
, screen
)
514 local tags
= capi
.screen
[screen
or capi
.mouse
.screen
]:tags()
515 local sel
= tag.selected()
517 for k
, t
in ipairs(tags
) do
519 tags
[cycle(#tags
, k
+ i
)].selected
= true
524 --- View next tag. This is the same as tag.viewidx(1).
525 function tag.viewnext()
526 return tag.viewidx(1)
529 --- View previous tag. This is the same a tag.viewidx(-1).
530 function tag.viewprev()
531 return tag.viewidx(-1)
535 -- @param t The tag object.
536 function tag.viewonly(t
)
537 tag.viewnone(t
.screen
)
541 --- View only a set of tags.
542 -- @param tags A table with tags to view only.
543 -- @param screen Optional screen number of the tags.
544 function tag.viewmore(tags
, screen
)
546 for i
, t
in pairs(tags
) do
551 --- Move a client to a tag.
552 -- @param target The tag to move the client to.
553 -- @para c Optional client to move, otherwise the focused one is used.
554 function client
.movetotag(target
, c
)
555 local sel
= c
or capi
.client
.focus
557 -- Check that tag and client screen are identical
558 if sel
.screen
~= target
.screen
then return end
563 --- Toggle a tag on a client.
564 -- @param target The tag to toggle.
565 -- @param c Optional client to toggle, otherwise the focused one is used.
566 function client
.toggletag(target
, c
)
567 local sel
= c
or capi
.client
.focus
568 -- Check that tag and client screen are identical
569 if sel
and sel
.screen
== target
.screen
then
570 local tags
= sel
:tags()
574 tags
[target
] = target
580 --- Toggle the floating status of a client.
581 -- @param c Optional client, the focused on if not set.
582 function client
.togglefloating(c
)
583 local sel
= c
or capi
.client
.focus
585 sel
.floating
= not sel
.floating
589 --- Move a client to a screen. Default is next screen, cycling.
590 -- @param c The client to move.
591 -- @param s The screen number, default to current + 1.
592 function client
.movetoscreen(c
, s
)
593 local sel
= c
or capi
.client
.focus
595 local sc
= capi
.screen
.count()
599 if s
> sc
then s
= 1 elseif s
< 1 then s
= sc
end
601 capi
.mouse
.coords
= capi
.screen
[s
].coords
602 capi
.client
.focus
= sel
606 --- Get the current layout name.
607 -- @param screen The screen number.
608 function layout
.get(screen
)
609 local t
= tag.selected(screen
)
615 --- Create a new userhook (for external libs).
616 -- @param name Hook name.
617 function hooks
.user
.create(name
)
619 hooks
[name
].callbacks
= {}
620 hooks
[name
].register
= function (f
)
621 table.insert(hooks
[name
].callbacks
, f
)
623 hooks
[name
].unregister
= function (f
)
624 for k
, h
in ipairs(hooks
[name
].callbacks
) do
626 table.remove(hooks
[name
].callbacks
, k
)
633 --- Call a created userhook (for external libs).
634 -- @param name Hook name.
635 function hooks
.user
.call(name
, ...)
636 for name
, callback
in pairs(hooks
[name
].callbacks
) do
641 -- Just set an awful mark to a client to move it later.
642 local awfulmarked
= {}
643 hooks
.user
.create('marked')
644 hooks
.user
.create('unmarked')
646 --- Mark a client, and then call 'marked' hook.
647 -- @param c The client to mark, the focused one if not specified.
648 -- @return True if the client has been marked. False if the client was already marked.
649 function client
.mark (c
)
650 local cl
= c
or capi
.client
.focus
652 for k
, v
in pairs(awfulmarked
) do
658 table.insert(awfulmarked
, cl
)
661 hooks
.user
.call('marked', cl
)
666 --- Unmark a client and then call 'unmarked' hook.
667 -- @param c The client to unmark, or the focused one if not specified.
668 -- @return True if the client has been unmarked. False if the client was not marked.
669 function client
.unmark(c
)
670 local cl
= c
or capi
.client
.focus
672 for k
, v
in pairs(awfulmarked
) do
674 table.remove(awfulmarked
, k
)
675 hooks
.user
.call('unmarked', cl
)
683 --- Check if a client is marked.
684 -- @param c The client to check, or the focused one otherwise.
685 function client
.ismarked(c
)
686 local cl
= c
or capi
.client
.focus
688 for k
, v
in pairs(awfulmarked
) do
697 --- Toggle a client as marked.
698 -- @param c The client to toggle mark.
699 function client
.togglemarked(c
)
700 local cl
= c
or capi
.client
.focus
702 if not client
.mark(c
) then
707 --- Return the marked clients and empty the marked table.
708 -- @return A table with all marked clients.
709 function client
.getmarked()
710 for k
, v
in pairs(awfulmarked
) do
711 hooks
.user
.call('unmarked', v
)
719 --- Change the layout of the current tag.
720 -- @param layouts A table of layouts.
721 -- @param i Relative index.
722 function layout
.inc(layouts
, i
)
723 local t
= tag.selected()
724 local number_of_layouts
= 0
725 local rev_layouts
= {}
726 for i
, v
in ipairs(layouts
) do
728 number_of_layouts
= number_of_layouts
+ 1
731 local cur_layout
= layout
.get()
732 local new_layout_index
= (rev_layouts
[cur_layout
] + i
) % number_of_layouts
733 if new_layout_index
== 0 then
734 new_layout_index
= number_of_layouts
736 t
.layout
= layouts
[new_layout_index
]
740 --- Set the layout of the current tag by name.
741 -- @param layout Layout name.
742 function layout
.set(layout
)
743 local t
= tag.selected()
749 -- Autodeclare awful.hooks.* functions
750 -- mapped to awesome hooks.* functions
751 for name
, hook
in pairs(capi
.hooks
) do
752 if name
~= 'timer' then
754 hooks
[name
].register
= function (f
)
755 if not hooks
[name
].callbacks
then
756 hooks
[name
].callbacks
= {}
758 for i
, callback
in ipairs(hooks
[name
].callbacks
) do
764 table.insert(hooks
[name
].callbacks
, f
)
766 hooks
[name
].unregister
= function (f
)
767 if hooks
[name
].callbacks
then
768 for k
, h
in ipairs(hooks
[name
].callbacks
) do
770 table.remove(hooks
[name
].callbacks
, k
)
778 hooks
[name
].register
= function (time
, f
, runnow
)
779 if type(time
) ~= 'number' or type(f
) ~= 'function' or time
<= 0 then
783 if hooks
[name
].timer
then
784 -- Take the smallest between current and new
785 new_timer
= math
.min(time
, hooks
[name
].timer
)
789 if not hooks
[name
].callbacks
then
790 hooks
[name
].callbacks
= {}
792 if hooks
[name
].timer
~= new_timer
then
793 hooks
[name
].timer
= new_timer
794 hook(hooks
[name
].timer
, function (...)
795 for i
, callback
in ipairs(hooks
[name
].callbacks
) do
796 callback
['counter'] = callback
['counter'] + hooks
[name
].timer
797 if callback
['counter'] >= callback
['timer'] then
798 callback
['callback'](...)
799 callback
['counter'] = 0
806 table.insert(hooks
[name
].callbacks
, { callback
= f
, timer
= time
, counter
= time
})
808 table.insert(hooks
[name
].callbacks
, { callback
= f
, timer
= time
, counter
= 0 })
811 hooks
[name
].unregister
= function (f
)
812 if hooks
[name
].callbacks
then
813 for k
, h
in ipairs(hooks
[name
].callbacks
) do
814 if h
.callback
== f
then
815 table.remove(hooks
[name
].callbacks
, k
)
825 -- @param cmd The command.
826 -- @return The awesome.spawn return value.
828 if cmd
and cmd
~= "" then
829 return capi
.awesome
.spawn(cmd
.. "&", capi
.mouse
.screen
)
834 -- @return The return value of Lua code.
836 return assert(loadstring("return " .. s
))()
839 --- Load history file in history table
840 -- @param id The prompt history identifier which is the path to the filename
841 -- @param max Optional parameter, the maximum number of entries in file
842 local function prompt_history_check_load(id
, max)
844 and not prompt
.history
[id
] then
845 prompt
.history
[id
] = { max = 50, table = {} }
848 prompt
.history
[id
].max = max
851 local f
= io
.open(id
, "r")
855 for line
in f
:lines() do
856 table.insert(prompt
.history
[id
].table, line
)
857 if #prompt
.history
[id
].table >= prompt
.history
[id
].max then
865 --- Save history table in history file
866 -- @param id The prompt history identifier
867 local function prompt_history_save(id
)
868 if prompt
.history
[id
] then
869 local f
= assert(io
.open(id
, "w"))
870 for i
= 1, math
.min(#prompt
.history
[id
].table, prompt
.history
[id
].max) do
871 f
:write(prompt
.history
[id
].table[i
] .. "\n")
877 --- Return the number of items in history table regarding the id
878 -- @param id The prompt history identifier
879 -- @return the number of items in history table, -1 if history is disabled
880 local function prompt_history_items(id
)
881 if prompt
.history
[id
] then
882 return #prompt
.history
[id
].table
888 --- Add an entry to the history file
889 -- @param id The prompt history identifier
890 -- @param command The command to add
891 local function prompt_history_add(id
, command
)
892 if prompt
.history
[id
] then
894 and command
~= prompt
.history
[id
].table[#prompt
.history
[id
].table] then
895 table.insert(prompt
.history
[id
].table, command
)
897 -- Do not exceed our max_cmd
898 if #prompt
.history
[id
].table > prompt
.history
[id
].max then
899 table.remove(prompt
.history
[id
].table, 1)
902 prompt_history_save(id
)
907 --- Use bash completion system to complete command and filename.
908 -- @param command The command line.
909 -- @param cur_pos The cursor position.
910 -- @paran ncomp The element number to complete.
911 -- @return The new command and the new cursor position.
912 function completion
.bash(command
, cur_pos
, ncomp
)
916 local cword_index
= 0
917 local cword_start
= 0
920 local comptype
= "file"
922 -- do nothing if we are on a letter, i.e. not at len + 1 or on a space
923 if cur_pos
~= #command
+ 1 and command
:sub(cur_pos
, cur_pos
) ~= " " then
924 return command
, cur_pos
925 elseif #command
== 0 then
926 return command
, cur_pos
929 while wend
<= #command
do
930 wend
= command
:find(" ", wstart
)
931 if not wend
then wend
= #command
+ 1 end
932 table.insert(words
, command
:sub(wstart
, wend
- 1))
933 if cur_pos
>= wstart
and cur_pos
<= wend
+ 1 then
942 if cword_index
== 1 then
946 local c
= io
.popen("/usr/bin/env bash -c 'compgen -A " .. comptype
.. " " .. words
[cword_index
] .. "'")
950 local line
= c
:read("*line")
951 if not line
then break end
952 table.insert(output
, line
)
957 -- no completion, return
959 return command
, cur_pos
963 while ncomp
> #output
do
964 ncomp
= ncomp
- #output
967 local str
= command
:sub(1, cword_start
- 1) .. output
[ncomp
] .. command
:sub(cword_end
)
968 cur_pos
= cword_end
+ #output
[ncomp
] + 1
973 --- Draw the prompt text with a cursor.
974 -- @param text The text.
975 -- @param text_color The text color.
976 -- @param cursor_color The cursor color.
977 -- @param cursor_pos The cursor position.
978 local function prompt_text_with_cursor(text
, text_color
, cursor_color
, cursor_pos
)
980 if not text
then text
= "" end
981 if #text
< cursor_pos
then
984 char
= escape(text
:sub(cursor_pos
, cursor_pos
))
986 local text_start
= escape(text
:sub(1, cursor_pos
- 1))
987 local text_end
= escape(text
:sub(cursor_pos
+ 1))
988 return text_start
.. "<span background=\"" .. cursor_color
.. "\" foreground=\"" .. text_color
.. "\">" .. char
.. "</span>" .. text_end
991 --- Run a prompt in a box.
992 -- @param args A table with optional arguments: fg_cursor, bg_cursor, prompt.
993 -- @param textbox The textbox to use for the prompt.
994 -- @param exe_callback The callback function to call with command as argument when finished.
995 -- @param completion_callback The callback function to call to get completion.
996 -- @param history_path Optional parameter: file path where the history should be saved, set nil to disable history
997 -- @param history_max Optional parameter: set the maximum entries in history file, 50 by default
998 -- @param done_callback Optional parameter: the callback function to always call without arguments, regardless of whether the prompt was cancelled.
999 function prompt
.run(args
, textbox
, exe_callback
, completion_callback
, history_path
, history_max
, done_callback
)
1000 if not args
then args
= {} end
1002 local command_before_comp
1003 local cur_pos_before_comp
1004 local prettyprompt
= args
.prompt
or ""
1005 local inv_col
= args
.fg_cursor
or theme
.fg_focus
or "black"
1006 local cur_col
= args
.bg_cursor
or theme
.bg_focus
or "white"
1008 prompt_history_check_load(history_path
, history_max
)
1009 local history_index
= prompt_history_items(history_path
) + 1
1010 -- The cursor position
1012 -- The completion element to use on completion request.
1014 if not textbox
or not exe_callback
then
1017 textbox
.text
= prettyprompt
.. prompt_text_with_cursor(text
, inv_col
, cur_col
, cur_pos
)
1018 capi
.keygrabber
.run(
1020 local has_ctrl
= false
1022 -- Compute modifiers keys
1023 for k
, v
in ipairs(mod) do
1024 if v
== "Control" then has_ctrl
= true end
1029 if key
== "c" or key
== "g" then
1031 if done_callback
then done_callback() end
1033 elseif key
== "j" or key
== "m" then
1035 exec_callback(command
)
1036 if done_callback
then done_callback() end
1040 if key
== "Return" then
1042 prompt_history_add(history_path
, command
)
1043 exe_callback(command
)
1044 if done_callback
then done_callback() end
1046 elseif key
== "Escape" then
1048 if done_callback
then done_callback() end
1057 elseif key
== "b" then
1059 cur_pos
= cur_pos
- 1
1061 elseif key
== "d" then
1062 if cur_pos
<= #command
then
1063 command
= command
:sub(1, cur_pos
- 1) .. command
:sub(cur_pos
+ 1)
1065 elseif key
== "e" then
1066 cur_pos
= #command
+ 1
1067 elseif key
== "f" then
1068 if cur_pos
<= #command
then
1069 cur_pos
= cur_pos
+ 1
1071 elseif key
== "h" then
1073 command
= command
:sub(1, cur_pos
- 2) .. command
:sub(cur_pos
)
1074 cur_pos
= cur_pos
- 1
1076 elseif key
== "k" then
1077 command
= command
:sub(1, cur_pos
- 1)
1078 elseif key
== "u" then
1079 command
= command
:sub(cur_pos
, #command
)
1081 elseif key
== "w" then
1084 local cword_start
= 1
1086 while wend
< cur_pos
do
1087 wend
= command
:find(" ", wstart
)
1088 if not wend
then wend
= #command
+ 1 end
1089 if cur_pos
>= wstart
and cur_pos
<= wend
+ 1 then
1090 cword_start
= wstart
1091 cword_end
= cur_pos
- 1
1096 command
= command
:sub(1, cword_start
- 1) .. command
:sub(cword_end
+ 1)
1097 cur_pos
= cword_start
1100 if completion_callback
then
1102 if key
:byte() == 9 then
1104 command_before_comp
= command
1105 cur_pos_before_comp
= cur_pos
1107 command
, cur_pos
= completion_callback(command_before_comp
, cur_pos_before_comp
, ncomp
)
1116 if key
== "Home" then
1118 elseif key
== "End" then
1119 cur_pos
= #command
+ 1
1120 elseif key
== "BackSpace" then
1122 command
= command
:sub(1, cur_pos
- 2) .. command
:sub(cur_pos
)
1123 cur_pos
= cur_pos
- 1
1126 elseif key
:byte() == 127 then
1127 command
= command
:sub(1, cur_pos
- 1) .. command
:sub(cur_pos
+ 1)
1128 elseif key
== "Left" then
1129 cur_pos
= cur_pos
- 1
1130 elseif key
== "Right" then
1131 cur_pos
= cur_pos
+ 1
1132 elseif key
== "Up" then
1133 if history_index
> 1 then
1134 history_index
= history_index
- 1
1136 command
= prompt
.history
[history_path
].table[history_index
]
1137 cur_pos
= #command
+ 2
1139 elseif key
== "Down" then
1140 if history_index
< prompt_history_items(history_path
) then
1141 history_index
= history_index
+ 1
1143 command
= prompt
.history
[history_path
].table[history_index
]
1144 cur_pos
= #command
+ 2
1145 elseif history_index
== prompt_history_items(history_path
) then
1146 history_index
= history_index
+ 1
1152 -- len() is UTF-8 aware but #key is not,
1153 -- so check that we have one UTF-8 char but advance the cursor of # position
1154 if key
:len() == 1 then
1155 command
= command
:sub(1, cur_pos
- 1) .. key
.. command
:sub(cur_pos
)
1156 cur_pos
= cur_pos
+ #key
1161 elseif cur_pos
> #command
+ 1 then
1162 cur_pos
= #command
+ 1
1167 textbox
.text
= prettyprompt
.. prompt_text_with_cursor(command
, inv_col
, cur_col
, cur_pos
)
1173 --- Escape a string from XML char.
1174 -- Useful to set raw text in textbox.
1175 -- @param text Text to escape.
1176 -- @return Escape text.
1177 function escape(text
)
1179 text
= text
:gsub("&", "&")
1180 text
= text
:gsub("<", "<")
1181 text
= text
:gsub(">", ">")
1182 text
= text
:gsub("'", "'")
1183 text
= text
:gsub("\"", """)
1188 --- Unescape a string from entities.
1189 -- @param text Text to unescape.
1190 -- @return Unescaped text.
1191 function unescape(text
)
1193 text
= text
:gsub("&", "&")
1194 text
= text
:gsub("<", "<")
1195 text
= text
:gsub(">", ">")
1196 text
= text
:gsub("'", "'")
1197 text
= text
:gsub(""", "\"")
1202 --- Return labels for a taglist widget with all tag from screen.
1203 -- It returns the tag name and set a special
1204 -- foreground and background color for selected tags.
1205 -- @param t The tag.
1206 -- @param args The arguments table.
1207 -- bg_focus The background color for selected tag.
1208 -- fg_focus The foreground color for selected tag.
1209 -- bg_urgent The background color for urgent tags.
1210 -- fg_urgent The foreground color for urgent tags.
1211 -- taglist_squares Optional: set "true" or nil to display the taglist squares.
1212 -- @return A string to print.
1213 function widget
.taglist
.label
.all(t
, args
)
1214 if not args
then args
= {} end
1215 local fg_focus
= args
.fg_focus
or theme
.taglist_fg_focus
or theme
.fg_focus
1216 local bg_focus
= args
.bg_focus
or theme
.taglist_bg_focus
or theme
.bg_focus
1217 local fg_urgent
= args
.fg_urgent
or theme
.taglist_fg_urgent
or theme
.fg_urgent
1218 local bg_urgent
= args
.bg_urgent
or theme
.taglist_bg_urgent
or theme
.bg_urgent
1219 local taglist_squares
= args
.taglist_squares
or theme
.taglist_squares
1221 local background
= ""
1222 local sel
= capi
.client
.focus
1223 local bg_color
= nil
1224 local fg_color
= nil
1229 if sel
and sel
:tags()[t
] then
1230 if not taglist_squares
or taglist_squares
== "true" then
1231 background
= "resize=\"true\" image=\"@AWESOME_ICON_PATH@/taglist/squarefw.png\""
1233 elseif bg_urgent
and fg_urgent
then
1234 for k
, c
in pairs(t
:clients()) do
1235 if not taglist_squares
or taglist_squares
== "true" then
1236 background
= "resize=\"true\" image=\"@AWESOME_ICON_PATH@/taglist/squarew.png\""
1239 bg_color
= bg_urgent
1240 fg_color
= fg_urgent
1245 if bg_color
and fg_color
then
1246 text
= "<bg "..background
.." color='"..bg_color
.."'/> <span color='"..fg_color
.."'>"..escape(t
.name
).."</span> "
1248 text
= " <bg "..background
.." />"..escape(t
.name
).." "
1253 --- Return labels for a taglist widget with all *non empty* tags from screen.
1254 -- It returns the tag name and set a special
1255 -- foreground and background color for selected tags.
1256 -- @param t The tag.
1257 -- @param args The arguments table.
1258 -- bg_focus The background color for selected tag.
1259 -- fg_focus The foreground color for selected tag.
1260 -- bg_urgent The background color for urgent tags.
1261 -- fg_urgent The foreground color for urgent tags.
1262 -- @return A string to print.
1263 function widget
.taglist
.label
.noempty(t
, args
)
1264 if #t
:clients() > 0 or t
.selected
then
1265 if not args
then args
= {} end
1266 local fg_focus
= args
.fg_focus
or theme
.taglist_fg_focus
or theme
.fg_focus
1267 local bg_focus
= args
.bg_focus
or theme
.taglist_bg_focus
or theme
.bg_focus
1268 local fg_urgent
= args
.fg_urgent
or theme
.taglist_fg_urgent
or theme
.fg_urgent
1269 local bg_urgent
= args
.bg_urgent
or theme
.taglist_bg_urgent
or theme
.bg_urgent
1270 local bg_color
= nil
1271 local fg_color
= nil
1278 if bg_urgent
and fg_urgent
then
1279 for k
, c
in pairs(t
:clients()) do
1281 bg_color
= bg_urgent
1282 fg_color
= fg_urgent
1287 if fg_color
and bg_color
then
1288 text
= "<bg color='" .. bg_color
.. "'/> <span color='" .. fg_color
.. "'>" .. escape(t
.name
) .. "</span> "
1290 text
= " " .. escape(t
.name
) .. " "
1296 local function widget_tasklist_label_common(c
, args
)
1297 if not args
then args
= {} end
1298 local fg_focus
= args
.fg_focus
or theme
.tasklist_fg_focus
or theme
.fg_focus
1299 local bg_focus
= args
.bg_focus
or theme
.tasklist_bg_focus
or theme
.bg_focus
1300 local fg_urgent
= args
.fg_urgent
or theme
.tasklist_fg_urgent
or theme
.fg_urgent
1301 local bg_urgent
= args
.bg_urgent
or theme
.tasklist_bg_urgent
or theme
.bg_urgent
1305 text
= "<bg image=\"@AWESOME_ICON_PATH@/tasklist/floatingw.png\" align=\"right\"/>"
1308 name
= escape(c
.icon_name
) or ""
1310 name
= escape(c
.name
) or ""
1312 if capi
.client
.focus
== c
then
1313 text
= text
.. " <bg color='"..bg_focus
.."'/><span color='"..fg_focus
.."'>"..name
.."</span> "
1314 elseif c
.urgent
and bg_urgent
and fg_urgent
then
1315 text
= text
.. " <bg color='"..bg_urgent
.."'/><span color='"..fg_urgent
.."'>"..name
.."</span> "
1317 text
= text
.. " "..name
.." "
1322 --- Return labels for a tasklist widget with clients from all tags and screen.
1323 -- It returns the client name and set a special
1324 -- foreground and background color for focused client.
1325 -- It also puts a special icon for floating windows.
1326 -- @param c The client.
1327 -- @param screen The screen we are drawing on.
1328 -- @param args The arguments table.
1329 -- bg_focus The background color for focused client.
1330 -- fg_focus The foreground color for focused client.
1331 -- bg_urgent The background color for urgent clients.
1332 -- fg_urgent The foreground color for urgent clients.
1333 -- @return A string to print.
1334 function widget
.tasklist
.label
.allscreen(c
, screen
, args
)
1335 return widget_tasklist_label_common(c
, args
)
1338 --- Return labels for a tasklist widget with clients from all tags.
1339 -- It returns the client name and set a special
1340 -- foreground and background color for focused client.
1341 -- It also puts a special icon for floating windows.
1342 -- @param c The client.
1343 -- @param screen The screen we are drawing on.
1344 -- @param args The arguments table.
1345 -- bg_focus The background color for focused client.
1346 -- fg_focus The foreground color for focused client.
1347 -- bg_urgent The background color for urgent clients.
1348 -- fg_urgent The foreground color for urgent clients.
1349 -- @return A string to print.
1350 function widget
.tasklist
.label
.alltags(c
, screen
, args
)
1351 -- Only print client on the same screen as this widget
1352 if c
.screen
~= screen
then return end
1353 return widget_tasklist_label_common(c
, args
)
1356 --- Return labels for a tasklist widget with clients from currently selected tags.
1357 -- It returns the client name and set a special
1358 -- foreground and background color for focused client.
1359 -- It also puts a special icon for floating windows.
1360 -- @param c The client.
1361 -- @param screen The screen we are drawing on.
1362 -- @param args The arguments table.
1363 -- bg_focus The background color for focused client.
1364 -- fg_focus The foreground color for focused client.
1365 -- bg_urgent The background color for urgent clients.
1366 -- fg_urgent The foreground color for urgent clients.
1367 -- @return A string to print.
1368 function widget
.tasklist
.label
.currenttags(c
, screen
, args
)
1369 -- Only print client on the same screen as this widget
1370 if c
.screen
~= screen
then return end
1371 for k
, t
in ipairs(capi
.screen
[screen
]:tags()) do
1372 if t
.selected
and c
:tags()[t
] then
1373 return widget_tasklist_label_common(c
, args
)
1378 --- Create a standard titlebar.
1379 -- @param c The client.
1380 -- @param args Arguments.
1381 -- fg: the foreground color.
1382 -- bg: the background color.
1383 -- fg_focus: the foreground color for focused window.
1384 -- fg_focus: the background color for focused window.
1385 function titlebar
.add(c
, args
)
1386 if not args
then args
= {} end
1388 titlebar
.data
[c
] = {}
1389 titlebar
.data
[c
].fg
= args
.fg
or theme
.titlebar_fg_normal
or theme
.fg_normal
1390 titlebar
.data
[c
].bg
= args
.bg
or theme
.titlebar_bg_normal
or theme
.bg_normal
1391 titlebar
.data
[c
].fg_focus
= args
.fg_focus
or theme
.titlebar_fg_focus
or theme
.fg_focus
1392 titlebar
.data
[c
].bg_focus
= args
.bg_focus
or theme
.titlebar_bg_focus
or theme
.bg_focus
1396 if args
.fg
then targs
.fg
= args
.fg
end
1397 if args
.bg
then targs
.bg
= args
.bg
end
1398 local tb
= capi
.titlebar(targs
)
1400 local title
= capi
.widget({ type = "textbox", name
= "title", align
= "flex" })
1401 title
:mouse_add(capi
.mouse({ }, 1, function (t
) t
.client
:mouse_move() end))
1402 title
:mouse_add(capi
.mouse({ args
.modkey
}, 3, function (t
) t
.client
:mouse_resize() end))
1404 local close_button
= capi
.widget({ type = "textbox", name
= "close", align
= "right" })
1405 close_button
:mouse_add(capi
.mouse({ }, 1, function (t
) t
.client
:kill() end))
1409 capi
.widget({ type = "appicon", name
= "appicon", align
= "left" }),
1419 --- Update a titlebar. This should be called in some hooks.
1420 -- @param c The client to update.
1421 function titlebar
.update(c
)
1422 if c
.titlebar
and titlebar
.data
[c
] then
1423 local widgets
= c
.titlebar
.widgets
1425 for k
, v
in ipairs(widgets
) do
1426 if v
.name
== "title" then title
= v
end
1427 if v
.name
== "close" then close
= v
end
1428 if title
and close
then break end
1431 title
.text
= " " .. escape(c
.name
)
1433 if capi
.client
.focus
== c
then
1434 c
.titlebar
.fg
= titlebar
.data
[c
].fg_focus
1435 c
.titlebar
.bg
= titlebar
.data
[c
].bg_focus
1437 close
.text
= "<bg image=\"@AWESOME_ICON_PATH@/titlebar/closer.png\" resize=\"true\"/>"
1440 c
.titlebar
.fg
= titlebar
.data
[c
].fg
1441 c
.titlebar
.bg
= titlebar
.data
[c
].bg
1443 close
.text
= "<bg image=\"@AWESOME_ICON_PATH@/titlebar/close.png\" resize=\"true\"/>"
1449 --- Remove a titlebar from a client.
1450 -- @param c The client.
1451 function titlebar
.remove(c
)
1453 titlebar
.data
[c
] = nil
1456 --- Set the beautiful theme if any.
1457 -- @param The beautiful theme.
1458 function beautiful
.register(btheme
)
1466 -- Register standards hooks
1467 hooks
.arrange
.register(tag.history
.update
)
1469 hooks
.focus
.register(client
.focus
.history
.add
)
1470 hooks
.unmanage
.register(client
.focus
.history
.delete
)
1471 hooks
.unmanage
.register(client_maximize_clean
)
1473 hooks
.focus
.register(titlebar
.update
)
1474 hooks
.unfocus
.register(titlebar
.update
)
1475 hooks
.titleupdate
.register(titlebar
.update
)
1476 hooks
.unmanage
.register(titlebar
.remove)
1478 hooks
.urgent
.register(client
.urgent
.stack
.add
)
1479 hooks
.focus
.register(client
.urgent
.stack
.delete
)
1480 hooks
.unmanage
.register(client
.urgent
.stack
.delete
)
1482 -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80