tag: remove get and geti methods
[awesome.git] / lib / awful.lua.in
blob4affe7975f1f305e57f7269602a4841683676cba
1 ---------------------------------------------------------------------------
2 -- @author Julien Danjou <julien@danjou.info>
3 -- @copyright 2008 Julien Danjou
4 ---------------------------------------------------------------------------
6 -- Grab environment we need
7 local type = type
8 local string = string
9 local assert = assert
10 local loadstring = loadstring
11 local ipairs = ipairs
12 local pairs = pairs
13 local os = os
14 local io = io
15 local math = math
16 local setmetatable = setmetatable
17 local table = table
18 local otable = otable
19 local capi =
21 screen = screen,
22 client = client,
23 mouse = mouse,
24 titlebar = titlebar,
25 widget = widget,
26 hooks = hooks,
27 keygrabber = keygrabber
30 --- awful: AWesome Functions very UsefuL
31 module("awful")
33 -- Local variable handling theme
34 local theme = {}
36 -- Various public structures
37 beautiful = {}
38 hooks = {}
39 hooks.user = {}
40 prompt = {}
41 completion = {}
42 screen = {}
43 layout = {}
44 client = {}
45 client.focus = {}
46 client.focus.history = {}
47 client.focus.history.data = {}
48 tag = {}
49 tag.history = {}
50 tag.history.data = {}
51 tag.history.data.past = {}
52 tag.history.data.current = {}
53 titlebar = {}
54 titlebar.data = otable()
55 widget = {}
56 widget.taglist = {}
57 widget.taglist.label = {}
58 widget.tasklist = {}
59 widget.tasklist.label = {}
61 --- Make i cycle.
62 -- @param t A length.
63 -- @param i An absolute index to fit into #t.
64 -- @return The object at new index.
65 local function cycle(t, i)
66 while i > t do i = i - t end
67 while i < 1 do i = i + t end
68 return i
69 end
71 --- Remove a client from the focus history
72 -- @param c The client that must be removed.
73 function client.focus.history.delete(c)
74 for k, v in ipairs(client.focus.history.data) do
75 if v == c then
76 table.remove(client.focus.history.data, k)
77 break
78 end
79 end
80 end
82 --- Update client focus history.
83 -- @param c The client that has been focused.
84 function client.focus.history.add(c)
85 -- Remove the client if its in stack
86 client.focus.history.delete(c)
87 -- Record the client has latest focused
88 table.insert(client.focus.history.data, 1, c)
89 end
91 --- Get the latest focused client for a screen in history.
92 -- @param screen The screen number to look for.
93 -- @param idx The index: 0 will return first candidate,
94 -- 1 will return second, etc.
95 -- @return A client.
96 function client.focus.history.get(screen, idx)
97 -- When this counter is equal to idx, we return the client
98 local counter = 0
99 local vc = capi.client.visible_get(screen)
100 for k, c in ipairs(client.focus.history.data) do
101 if c.screen == screen then
102 for j, vcc in ipairs(vc) do
103 if vcc == c then
104 if counter == idx then
105 return c
107 -- We found one, increment the counter only.
108 counter = counter + 1
109 break
114 -- Argh nobody found in history, give the first one visible if there is one
115 if counter == 0 then
116 return vc[1]
120 --- Focus the previous client in history.
121 function client.focus.history.previous()
122 local sel = capi.client.focus_get()
123 local s
124 if sel then
125 s = sel.screen
126 else
127 s = capi.mouse.screen
129 local c = client.focus.history.get(s, 1)
130 if c then c:focus_set() end
133 --- Get a client by its relative index to the focused window.
134 -- @usage Set i to 1 to get next, -1 to get previous.
135 -- @param i The index.
136 -- @param c Optional client.
137 -- @return A client, or nil if no client is available.
138 function client.next(i, c)
139 -- Get currently focused client
140 local sel = c or capi.client.focus_get()
141 if sel then
142 -- Get all visible clients
143 local cls = capi.client.visible_get(sel.screen)
144 -- Loop upon each client
145 for idx, c in ipairs(cls) do
146 if c == sel then
147 -- Cycle
148 return cls[cycle(#cls, idx +i)]
154 --- Return true whether client B is in the right direction
155 -- compared to client A.
156 -- @param dir The direction.
157 -- @param cA The first client.
158 -- @param cB The second client.
159 -- @return True if B is in the direction of A.
160 local function is_in_direction(dir, cA, cB)
161 if dir == "up" then
162 return cA['y'] > cB['y']
163 elseif dir == "down" then
164 return cA['y'] < cB['y']
165 elseif dir == "left" then
166 return cA['x'] > cB['x']
167 elseif dir == "right" then
168 return cA['x'] < cB['x']
170 return false
173 --- Calculate distance between two points.
174 -- i.e: if we want to move to the right, we will take the right border
175 -- of the currently focused client and the left side of the checked client.
176 -- This avoid the focus of an upper client when you move to the right in a
177 -- tilebottom layout with nmaster=2 and 5 clients open, for instance.
178 -- @param dir The direction.
179 -- @param cA The first client.
180 -- @param cB The second client.
181 -- @return The distance between the clients.
182 local function calculate_distance(dir, cA, cB)
183 local xA = cA['x']
184 local xB = cB['x']
185 local yA = cA['y']
186 local yB = cB['y']
188 if dir == "up" then
189 yB = yB + cB['height']
190 elseif dir == "down" then
191 yA = yA + cA['height']
192 elseif dir == "left" then
193 xB = xB + cB['width']
194 elseif dir == "right" then
195 xA = xA + cA['width']
198 return math.sqrt(math.pow(xB - xA, 2) + math.pow(yB - yA, 2))
201 --- Focus a client by the given direction.
202 -- @param dir The direction, can be either "up", "down", "left" or "right".
203 -- @param c Optional client.
204 function client.focusbydirection(dir, c)
205 local sel = c or capi.client.focus_get()
206 if sel then
207 local coords = sel.coords
208 local dist, dist_min
209 local target = nil
210 local cls = capi.client.visible_get(sel.screen)
212 -- We check each client.
213 for i, c in ipairs(cls) do
214 -- Check coords to see if client is located in the right direction.
215 if is_in_direction(dir, coords, c.coords) then
217 -- Calculate distance between focused client and checked client.
218 dist = calculate_distance(dir, coords, c.coords)
220 -- If distance is shorter then keep the client.
221 if not target or dist < dist_min then
222 target = c
223 dist_min = dist
228 -- If we found a client to focus, then do it.
229 if target then
230 target:focus_set()
235 --- Focus a client by its relative index.
236 -- @param i The index.
237 -- @param c Optional client.
238 function client.focusbyidx(i, c)
239 local target = client.next(i, c)
240 if target then
241 target:focus_set()
245 --- Swap a client by its relative index.
246 -- @param i The index.
247 -- @param c Optional client, otherwise focused one is used.
248 function client.swap(i, c)
249 local sel = c or capi.client.focus_get()
250 local target = client.next(i, sel)
251 if target then
252 target:swap(sel)
256 --- Get the master window.
257 -- @param screen Optional screen number, otherwise screen mouse is used.
258 -- @return The master window.
259 function client.master(screen)
260 local s = screen or capi.mouse.screen
261 return capi.client.visible_get(s)[1]
264 --- Move/resize a client relative to current coordinates.
265 -- @param x The relative x coordinate.
266 -- @param y The relative y coordinate.
267 -- @param w The relative width.
268 -- @param h The relative height.
269 -- @param c The optional client, otherwise focused one is used.
270 function client.moveresize(x, y, w, h, c)
271 local sel = c or capi.client.focus_get()
272 local coords = sel.coords
273 coords['x'] = coords['x'] + x
274 coords['y'] = coords['y'] + y
275 coords['width'] = coords['width'] + w
276 coords['height'] = coords['height'] + h
277 sel.coords = coords
280 --- Maximize a client to use the full workarea.
281 -- @param c A client, or the focused one if nil.
282 function client.maximize(c)
283 local sel = c or capi.client.focus_get()
284 if sel then
285 sel.floating = true
286 local ws = capi.screen[sel.screen].workarea
287 ws.width = ws.width - 2 * sel.border_width
288 ws.height = ws.height - 2 * sel.border_width
289 sel.coords = ws
293 --- Give the focus to a screen, and move pointer.
294 -- @param Screen number.
295 function screen.focus(i)
296 local sel = capi.client.focus_get()
297 local s
298 if sel then
299 s = sel.screen
300 else
301 s = capi.mouse.screen
303 s = cycle(capi.screen.count(), s + i)
304 local c = client.focus.history.get(s, 0)
305 if c then c:focus_set() end
306 -- Move the mouse on the screen
307 capi.mouse.coords = capi.screen[s].coords
310 --- Compare 2 tables of tags.
311 -- @param a The first table.
312 -- @param b The second table of tags.
313 -- @return True if the tables are identical, false otherwise.
314 local function tag_compare_select(a, b)
315 if not a or not b then
316 return false
318 -- Quick size comparison
319 if #a ~= #b then
320 return false
322 for ka, va in pairs(a) do
323 if b[ka] ~= va.selected then
324 return false
327 for kb, vb in pairs(b) do
328 if a[kb].selected ~= vb then
329 return false
332 return true
335 --- Update the tag history.
336 -- @param screen The screen number.
337 function tag.history.update(screen)
338 local curtags = capi.screen[screen].tags
339 if not tag_compare_select(curtags, tag.history.data.current[screen]) then
340 tag.history.data.past[screen] = tag.history.data.current[screen]
341 tag.history.data.current[screen] = {}
342 for k, v in ipairs(curtags) do
343 tag.history.data.current[screen][k] = v.selected
348 -- Revert tag history.
349 -- @param screen The screen number.
350 function tag.history.restore(screen)
351 local s = screen or capi.mouse.screen
352 local tags = capi.screen[s].tags
353 for k, t in pairs(tags) do
354 t.selected = tag.history.data.past[s][k]
358 --- Return a table with all visible tags
359 -- @param s Screen number.
360 -- @return A table with all selected tags.
361 function tag.selectedlist(s)
362 local screen = s or capi.mouse.screen
363 local tags = capi.screen[screen].tags
364 local vtags = {}
365 for i, t in pairs(tags) do
366 if t.selected then
367 vtags[#vtags + 1] = t
370 return vtags
373 --- Return only the first visible tag.
374 -- @param s Screen number.
375 function tag.selected(s)
376 return tag.selectedlist(s)[1]
379 --- Set master width factor.
380 -- @param mwfact Master width factor.
381 function tag.setmwfact(mwfact)
382 local t = tag.selected()
383 if t then
384 t.mwfact = mwfact
388 --- Increase master width factor.
389 -- @param add Value to add to master width factor.
390 function tag.incmwfact(add)
391 local t = tag.selected()
392 if t then
393 t.mwfact = t.mwfact + add
397 --- Set the number of master windows.
398 -- @param nmaster The number of master windows.
399 function tag.setnmaster(nmaster)
400 local t = tag.selected()
401 if t then
402 t.nmaster = nmaster
406 --- Increase the number of master windows.
407 -- @param add Value to add to number of master windows.
408 function tag.incnmaster(add)
409 local t = tag.selected()
410 if t then
411 t.nmaster = t.nmaster + add
415 --- Set number of column windows.
416 -- @param ncol The number of column.
417 function tag.setncol(ncol)
418 local t = tag.selected()
419 if t then
420 t.ncol = ncol
424 --- Increase number of column windows.
425 -- @param add Value to add to number of column windows.
426 function tag.incncol(add)
427 local t = tag.selected()
428 if t then
429 t.ncol = t.ncol + add
433 --- View no tag.
434 -- @param Optional screen number.
435 function tag.viewnone(screen)
436 local tags = capi.screen[screen or capi.mouse.screen].tags
437 for i, t in pairs(tags) do
438 t.selected = false
442 --- View a tag by its index.
443 -- @param i The relative index to see.
444 -- @param screen Optional screen number.
445 function tag.viewidx(i, screen)
446 local tags = capi.screen[screen or capi.mouse.screen].tags
447 local sel = tag.selected()
448 tag.viewnone()
449 for k, t in ipairs(tags) do
450 if t == sel then
451 tags[cycle(#tags, k + i)].selected = true
456 --- View next tag. This is the same as tag.viewidx(1).
457 function tag.viewnext()
458 return tag.viewidx(1)
461 --- View previous tag. This is the same a tag.viewidx(-1).
462 function tag.viewprev()
463 return tag.viewidx(-1)
466 --- View only a tag.
467 -- @param t The tag object.
468 function tag.viewonly(t)
469 tag.viewnone(t.screen)
470 t.selected = true
473 --- View only a set of tags.
474 -- @param tags A table with tags to view only.
475 -- @param screen Optional screen number of the tags.
476 function tag.viewmore(tags, screen)
477 tag.viewnone(screen)
478 for i, t in pairs(tags) do
479 t.selected = true
483 --- Move a client to a tag.
484 -- @param target The tag to move the client to.
485 -- @para c Optional client to move, otherwise the focused one is used.
486 function client.movetotag(target, c)
487 local sel = c or capi.client.focus_get();
488 if sel then
489 -- Check that tag and client screen are identical
490 if sel.screen ~= target.screen then return end
491 local tags = { target }
492 sel.tags = tags
496 --- Toggle a tag on a client.
497 -- @param target The tag to toggle.
498 -- @param c Optional client to toggle, otherwise the focused one is used.
499 function client.toggletag(target, c)
500 local sel = c or capi.client.focus_get();
501 -- Check that tag and client screen are identical
502 if sel and sel.screen == target.screen then
503 local tags = client.tags
504 if tags[target] then
505 tags[target] = nil
506 else
507 tags[target] = target
509 sel.tags = tags
513 --- Toggle the floating status of a client.
514 -- @param c Optional client, the focused on if not set.
515 function client.togglefloating(c)
516 local sel = c or capi.client.focus_get();
517 if sel then
518 sel.floating = not sel.floating
522 --- Move a client to a screen. Default is next screen, cycling.
523 -- @param c The client to move.
524 -- @param s The screen number, default to current + 1.
525 function client.movetoscreen(c, s)
526 local sel = c or capi.client.focus_get();
527 if sel then
528 local sc = capi.screen.count()
529 if not s then
530 s = sel.screen + 1
532 if s > sc then s = 1 elseif s < 1 then s = sc end
533 sel.screen = s
534 capi.mouse.coords = capi.screen[s].coords
535 sel:focus_set()
539 --- Get the current layout name.
540 -- @param screen The screen number.
541 function layout.get(screen)
542 local t = tag.selected(screen)
543 if t then
544 return t.layout
548 --- Create a new userhook (for external libs).
549 -- @param name Hook name.
550 function hooks.user.create(name)
551 hooks[name] = {}
552 hooks[name].callbacks = {}
553 hooks[name].register = function (f)
554 table.insert(hooks[name].callbacks, f)
556 hooks[name].unregister = function (f)
557 for k, h in ipairs(hooks[name].callbacks) do
558 if h == f then
559 table.remove(hooks[name].callbacks, k)
560 break
566 --- Call a created userhook (for external libs).
567 -- @param name Hook name.
568 function hooks.user.call(name, ...)
569 for name, callback in pairs(hooks[name].callbacks) do
570 callback(...)
574 -- Just set an awful mark to a client to move it later.
575 local awfulmarked = {}
576 hooks.user.create('marked')
577 hooks.user.create('unmarked')
579 --- Mark a client, and then call 'marked' hook.
580 -- @param c The client to mark, the focused one if not specified.
581 -- @return True if the client has been marked. False if the client was already marked.
582 function client.mark (c)
583 local cl = c or capi.client.focus_get()
584 if cl then
585 for k, v in pairs(awfulmarked) do
586 if cl == v then
587 return false
591 table.insert(awfulmarked, cl)
593 -- Call callback
594 hooks.user.call('marked', cl)
595 return true
599 --- Unmark a client and then call 'unmarked' hook.
600 -- @param c The client to unmark, or the focused one if not specified.
601 -- @return True if the client has been unmarked. False if the client was not marked.
602 function client.unmark(c)
603 local cl = c or capi.client.focus_get()
605 for k, v in pairs(awfulmarked) do
606 if cl == v then
607 table.remove(awfulmarked, k)
608 hooks.user.call('unmarked', cl)
609 return true
613 return false
616 --- Check if a client is marked.
617 -- @param c The client to check, or the focused one otherwise.
618 function client.ismarked(c)
619 local cl = c or capi.client.focus_get()
620 if cl then
621 for k, v in pairs(awfulmarked) do
622 if cl == v then
623 return true
627 return false
630 --- Toggle a client as marked.
631 -- @param c The client to toggle mark.
632 function client.togglemarked(c)
633 local cl = c or capi.client.focus_get()
635 if not client.mark(c) then
636 client.unmark(c)
640 --- Return the marked clients and empty the marked table.
641 -- @return A table with all marked clients.
642 function client.getmarked()
643 for k, v in pairs(awfulmarked) do
644 hooks.user.call('unmarked', v)
647 t = awfulmarked
648 awfulmarked = {}
649 return t
652 --- Change the layout of the current tag.
653 -- @param layouts A table of layouts.
654 -- @param i Relative index.
655 function layout.inc(layouts, i)
656 local t = tag.selected()
657 local number_of_layouts = 0
658 local rev_layouts = {}
659 for i, v in ipairs(layouts) do
660 rev_layouts[v] = i
661 number_of_layouts = number_of_layouts + 1
663 if t then
664 local cur_layout = layout.get()
665 local new_layout_index = (rev_layouts[cur_layout] + i) % number_of_layouts
666 if new_layout_index == 0 then
667 new_layout_index = number_of_layouts
669 t.layout = layouts[new_layout_index]
673 --- Set the layout of the current tag by name.
674 -- @param layout Layout name.
675 function layout.set(layout)
676 local t = tag.selected()
677 if t then
678 t.layout = layout
682 -- Autodeclare awful.hooks.* functions
683 -- mapped to awesome hooks.* functions
684 for name, hook in pairs(capi.hooks) do
685 if name ~= 'timer' then
686 hooks[name] = {}
687 hooks[name].register = function (f)
688 if not hooks[name].callbacks then
689 hooks[name].callbacks = {}
690 hook(function (...)
691 for i, callback in ipairs(hooks[name].callbacks) do
692 callback(...)
694 end)
697 table.insert(hooks[name].callbacks, f)
699 hooks[name].unregister = function (f)
700 if hooks[name].callbacks then
701 for k, h in ipairs(hooks[name].callbacks) do
702 if h == f then
703 table.remove(hooks[name].callbacks, k)
704 break
709 else
710 hooks[name] = {}
711 hooks[name].register = function (time, f, runnow)
712 if type(time) ~= 'number' or type(f) ~= 'function' then
713 return
715 if not hooks[name].callbacks then
716 hooks[name].callbacks = {}
717 hook(1, function (...)
718 for i, callback in ipairs(hooks[name].callbacks) do
719 if callback['counter'] >= callback['timer'] then
720 callback['counter'] = 1
721 callback['callback'](...)
722 else
723 callback['counter'] = callback['counter'] + 1
726 end)
729 if runnow then
730 table.insert(hooks[name].callbacks, { callback = f, timer = time, counter = time })
731 else
732 table.insert(hooks[name].callbacks, { callback = f, timer = time, counter = 0 })
735 hooks[name].unregister = function (f)
736 if hooks[name].callbacks then
737 for k, h in ipairs(hooks[name].callbacks) do
738 if h.callback == f then
739 table.remove(hooks[name].callbacks, k)
740 break
748 --- Spawn a program.
749 -- @param cmd The command.
750 -- @return The os.execute() return value.
751 function spawn(cmd)
752 return os.execute(cmd .. "&")
755 --- Eval Lua code.
756 -- @return The return value of Lua code.
757 function eval(s)
758 return assert(loadstring("return " .. s))()
761 --- Use bash completion system to complete command and filename.
762 -- @param command The command line.
763 -- @param cur_pos The cursor position.
764 -- @paran ncomp The element number to complete.
765 -- @return The new commande and the new cursor position.
766 function completion.bash(command, cur_pos, ncomp)
767 local wstart = 1
768 local wend = 1
769 local words = {}
770 local cword_index = 0
771 local cword_start = 0
772 local cword_end = 0
773 local i = 1
774 local comptype = "file"
776 -- do nothing if we are on a letter, i.e. not at len + 1 or on a space
777 if cur_pos ~= #command + 1 and command:sub(cur_pos, cur_pos) ~= " " then
778 return command, cur_pos
779 elseif #command == 0 then
780 return command, cur_pos
783 while wend <= #command do
784 wend = command:find(" ", wstart)
785 if not wend then wend = #command + 1 end
786 table.insert(words, command:sub(wstart, wend - 1))
787 if cur_pos >= wstart and cur_pos <= wend + 1 then
788 cword_start = wstart
789 cword_end = wend
790 cword_index = i
792 wstart = wend + 1
793 i = i + 1
796 if cword_index == 1 then
797 comptype = "command"
800 local c = io.popen("/usr/bin/env bash -c 'compgen -A " .. comptype .. " " .. words[cword_index] .. "'")
801 local output = {}
802 i = 0
803 while true do
804 local line = c:read("*line")
805 if not line then break end
806 table.insert(output, line)
809 c:close()
811 -- no completion, return
812 if #output == 0 then
813 return command, cur_pos
816 -- cycle
817 while ncomp > #output do
818 ncomp = ncomp - #output
821 local str = command:sub(1, cword_start - 1) .. output[ncomp] .. command:sub(cword_end)
822 cur_pos = cword_end + #output[ncomp] + 1
824 return str, cur_pos
827 --- Draw the prompt text with a cursor.
828 -- @param text The text.
829 -- @param text_color The text color.
830 -- @param cursor_color The cursor color.
831 -- @param cursor_pos The cursor position.
832 local function prompt_text_with_cursor(text, text_color, cursor_color, cursor_pos)
833 local char
834 if not text then text = "" end
835 if #text < cursor_pos then
836 char = " "
837 else
838 char = escape(text:sub(cursor_pos, cursor_pos))
840 local text_start = escape(text:sub(1, cursor_pos - 1))
841 local text_end = escape(text:sub(cursor_pos + 1))
842 return text_start .. "<span background=\"" .. cursor_color .. "\" foreground=\"" .. text_color .. "\">" .. char .. "</span>" .. text_end
845 --- Run a prompt in a box.
846 -- @param args A table with optional arguments: fg_cursor, bg_cursor, prompt.
847 -- @param textbox The textbox to use for the prompt.
848 -- @param exe_callback The callback function to call with command as argument when finished.
849 -- @param completion_callback The callback function to call to get completion.
850 function prompt(args, textbox, exe_callback, completion_callback)
851 if not args then args = {} end
852 local command = ""
853 local command_before_comp
854 local cur_pos_before_comp
855 local prompt = args.prompt or ""
856 local inv_col = args.fg_cursor or theme.fg_focus or "black"
857 local cur_col = args.bg_cursor or theme.bg_focus or "white"
858 -- The cursor position
859 local cur_pos = 1
860 -- The completion element to use on completion request.
861 local ncomp = 1
862 if not textbox or not exe_callback then
863 return
865 textbox.text = prompt .. prompt_text_with_cursor(text, inv_col, cur_col, cur_pos)
866 capi.keygrabber.run(
867 function (mod, key)
868 local has_ctrl = false
870 -- Compute modifiers keys
871 for k, v in ipairs(mod) do
872 if v == "Control" then has_ctrl = true end
875 -- Get out cases
876 if has_ctrl then
877 if key == "c" or key == "g" then
878 textbox.text = ""
879 return false
880 elseif key == "j" or key == "m" then
881 textbox.text = ""
882 exec_callback(command)
883 return false
885 else
886 if key == "Return" then
887 textbox.text = ""
888 exe_callback(command)
889 return false
890 elseif key == "Escape" then
891 textbox.text = ""
892 return false
896 -- Control cases
897 if has_ctrl then
898 if key == "a" then
899 cur_pos = 1
900 elseif key == "b" then
901 if cur_pos > 1 then
902 cur_pos = cur_pos - 1
904 elseif key == "d" then
905 if cur_pos <= #command then
906 command = command:sub(1, cur_pos - 1) .. command:sub(cur_pos + 1)
908 elseif key == "e" then
909 cur_pos = #command + 1
910 elseif key == "f" then
911 if cur_pos <= #command then
912 cur_pos = cur_pos + 1
914 elseif key == "h" then
915 if cur_pos > 1 then
916 command = command:sub(1, cur_pos - 2) .. command:sub(cur_pos)
917 cur_pos = cur_pos - 1
919 elseif key == "k" then
920 command = command:sub(1, cur_pos - 1)
921 elseif key == "u" then
922 command = command:sub(cur_pos, #command)
923 cur_pos = 1
924 elseif key == "w" then
925 local wstart = 1
926 local wend = 1
927 local cword_start = 1
928 local cword_end = 1
929 while wend < cur_pos do
930 wend = command:find(" ", wstart)
931 if not wend then wend = #command + 1 end
932 if cur_pos >= wstart and cur_pos <= wend + 1 then
933 cword_start = wstart
934 cword_end = cur_pos - 1
935 break
937 wstart = wend + 1
939 command = command:sub(1, cword_start - 1) .. command:sub(cword_end + 1)
940 cur_pos = cword_start
942 else
943 if completion_callback then
944 -- That's tab
945 if key:byte() == 9 then
946 if ncomp == 1 then
947 command_before_comp = command
948 cur_pos_before_comp = cur_pos
950 command, cur_pos = completion_callback(command_before_comp, cur_pos_before_comp, ncomp)
951 ncomp = ncomp + 1
952 key = ""
953 else
954 ncomp = 1
958 -- Typin cases
959 if key == "Home" then
960 cur_pos = 1
961 elseif key == "End" then
962 cur_pos = #command + 1
963 elseif key == "BackSpace" then
964 if cur_pos > 1 then
965 command = command:sub(1, cur_pos - 2) .. command:sub(cur_pos)
966 cur_pos = cur_pos - 1
968 -- That's DEL
969 elseif key:byte() == 127 then
970 command = command:sub(1, cur_pos - 1) .. command:sub(cur_pos + 1)
971 elseif key == "Left" then
972 cur_pos = cur_pos - 1
973 elseif key == "Right" then
974 cur_pos = cur_pos + 1
975 else
976 -- len() is UTF-8 aware but #key is not,
977 -- so check that we have one UTF-8 char but advance the cursor of # position
978 if key:len() == 1 then
979 command = command:sub(1, cur_pos - 1) .. key .. command:sub(cur_pos)
980 cur_pos = cur_pos + #key
983 if cur_pos < 1 then
984 cur_pos = 1
985 elseif cur_pos > #command + 1 then
986 cur_pos = #command + 1
990 -- Update textbox
991 textbox.text = prompt .. prompt_text_with_cursor(command, inv_col, cur_col, cur_pos)
993 return true
994 end)
997 --- Escape a string from XML char.
998 -- Useful to set raw text in textbox.
999 -- @param text Text to escape.
1000 -- @return Escape text.
1001 function escape(text)
1002 if text then
1003 text = text:gsub("&", "&amp;")
1004 text = text:gsub("<", "&lt;")
1005 text = text:gsub(">", "&gt;")
1006 text = text:gsub("'", "&apos;")
1007 text = text:gsub("\"", "&quot;")
1009 return text
1012 --- Unescape a string from entities.
1013 -- @param text Text to unescape.
1014 -- @return Unescaped text.
1015 function unescape(text)
1016 if text then
1017 text = text:gsub("&amp;", "&")
1018 text = text:gsub("&lt;", "<")
1019 text = text:gsub("&gt;", ">")
1020 text = text:gsub("&apos;", "'")
1021 text = text:gsub("&quot;", "\"")
1023 return text
1026 --- Return labels for a taglist widget with all tag from screen.
1027 -- It returns the tag name and set a special
1028 -- foreground and background color for selected tags.
1029 -- @param t The tag.
1030 -- @param args The arguments table.
1031 -- bg_focus The background color for selected tag.
1032 -- fg_focus The foreground color for selected tag.
1033 -- bg_urgent The background color for urgent tags.
1034 -- fg_urgent The foreground color for urgent tags.
1035 -- @return A string to print.
1036 function widget.taglist.label.all(t, args)
1037 if not args then args = {} end
1038 local fg_focus = args.fg_focus or theme.fg_focus
1039 local bg_focus = args.bg_focus or theme.bg_focus
1040 local fg_urgent = args.fg_urgent or theme.fg_urgent
1041 local bg_urgent = args.bg_urgent or theme.bg_urgent
1042 local text
1043 local background = ""
1044 local sel = capi.client.focus_get()
1045 local bg_color = nil
1046 local fg_color = nil
1047 if t.selected then
1048 bg_color = bg_focus
1049 fg_color = fg_focus
1051 if sel and sel.tags[t] then
1052 background = "resize=\"true\" image=\"@AWESOME_ICON_PATH@/taglist/squarefw.png\""
1053 elseif bg_urgent and fg_urgent then
1054 for k, c in pairs(t.clients) do
1055 background = "resize=\"true\" image=\"@AWESOME_ICON_PATH@/taglist/squarew.png\""
1056 if c.urgent then
1057 bg_color = bg_urgent
1058 fg_color = fg_urgent
1059 break
1063 if bg_color and fg_color then
1064 text = "<bg "..background.." color='"..bg_color.."'/> <span color='"..fg_color.."'>"..escape(t.name).."</span> "
1065 else
1066 text = " <bg "..background.." />"..escape(t.name).." "
1068 return text
1071 --- Return labels for a taglist widget with all *non empty* tags from screen.
1072 -- It returns the tag name and set a special
1073 -- foreground and background color for selected tags.
1074 -- @param t The tag.
1075 -- @param args The arguments table.
1076 -- bg_focus The background color for selected tag.
1077 -- fg_focus The foreground color for selected tag.
1078 -- bg_urgent The background color for urgent tags.
1079 -- fg_urgent The foreground color for urgent tags.
1080 -- @return A string to print.
1081 function widget.taglist.label.noempty(t, args)
1082 if #t.clients > 0 then
1083 if not args then args = {} end
1084 local fg_focus = args.fg_focus or theme.fg_focus
1085 local bg_focus = args.bg_focus or theme.bg_focus
1086 local fg_urgent = args.fg_urgent or theme.fg_urgent
1087 local bg_urgent = args.bg_urgent or theme.bg_urgent
1088 local bg_color = nil
1089 local fg_color = nil
1090 local text
1092 if t.selected then
1093 bg_color = bg_focus
1094 fg_color = fg_focus
1096 if bg_urgent and fg_urgent then
1097 for k, c in pairs(t.clients) do
1098 if c.urgent then
1099 bg_color = bg_urgent
1100 fg_color = fg_urgent
1101 break
1105 if fg_color and bg_color then
1106 text = "<bg color='" .. bg_color .. "'/> <span color='" .. fg_color .. "'>" .. escape(t.name) .. "</span> "
1107 else
1108 text = " " .. escape(t.name) .. " "
1110 return text
1114 local function widget_tasklist_label_common(c, args)
1115 if not args then args = {} end
1116 local fg_focus = args.fg_focus or theme.fg_focus
1117 local bg_focus = args.bg_focus or theme.bg_focus
1118 local fg_urgent = args.fg_urgent or theme.fg_urgent
1119 local bg_urgent = args.bg_urgent or theme.bg_urgent
1120 local text = ""
1121 local name
1122 if c.floating then
1123 text = "<bg image=\"@AWESOME_ICON_PATH@/tasklist/floatingw.png\" align=\"right\"/>"
1125 if c.hidden then
1126 name = escape(c.icon_name)
1127 else
1128 name = escape(c.name)
1130 if capi.client.focus_get() == c then
1131 text = text .. " <bg color='"..bg_focus.."'/><span color='"..fg_focus.."'>"..name.."</span> "
1132 elseif c.urgent and bg_urgent and fg_urgent then
1133 text = text .. " <bg color='"..bg_urgent.."'/><span color='"..fg_urgent.."'>"..name.."</span> "
1134 else
1135 text = text .. " "..name.." "
1137 return text
1140 --- Return labels for a tasklist widget with clients from all tags and screen.
1141 -- It returns the client name and set a special
1142 -- foreground and background color for focused client.
1143 -- It also puts a special icon for floating windows.
1144 -- @param c The client.
1145 -- @param screen The screen we are drawing on.
1146 -- @param args The arguments table.
1147 -- bg_focus The background color for focused client.
1148 -- fg_focus The foreground color for focused client.
1149 -- bg_urgent The background color for urgent clients.
1150 -- fg_urgent The foreground color for urgent clients.
1151 -- @return A string to print.
1152 function widget.tasklist.label.allscreen(c, screen, args)
1153 return widget_tasklist_label_common(c, args)
1156 --- Return labels for a tasklist widget with clients from all tags.
1157 -- It returns the client name and set a special
1158 -- foreground and background color for focused client.
1159 -- It also puts a special icon for floating windows.
1160 -- @param c The client.
1161 -- @param screen The screen we are drawing on.
1162 -- @param args The arguments table.
1163 -- bg_focus The background color for focused client.
1164 -- fg_focus The foreground color for focused client.
1165 -- bg_urgent The background color for urgent clients.
1166 -- fg_urgent The foreground color for urgent clients.
1167 -- @return A string to print.
1168 function widget.tasklist.label.alltags(c, screen, args)
1169 -- Only print client on the same screen as this widget
1170 if c.screen ~= screen then return end
1171 return widget_tasklist_label_common(c, args)
1174 --- Return labels for a tasklist widget with clients from currently selected tags.
1175 -- It returns the client name and set a special
1176 -- foreground and background color for focused client.
1177 -- It also puts a special icon for floating windows.
1178 -- @param c The client.
1179 -- @param screen The screen we are drawing on.
1180 -- @param args The arguments table.
1181 -- bg_focus The background color for focused client.
1182 -- fg_focus The foreground color for focused client.
1183 -- bg_urgent The background color for urgent clients.
1184 -- fg_urgent The foreground color for urgent clients.
1185 -- @return A string to print.
1186 function widget.tasklist.label.currenttags(c, screen, args)
1187 -- Only print client on the same screen as this widget
1188 if c.screen ~= screen then return end
1189 for k, t in ipairs(capi.screen[screen].tags) do
1190 if t.selected and c.tags[t] then
1191 return widget_tasklist_label_common(c, args)
1196 --- Create a standard titlebar.
1197 -- @param c The client.
1198 -- @param args Arguments.
1199 -- fg: the foreground color.
1200 -- bg: the background color.
1201 -- fg_focus: the foreground color for focused window.
1202 -- fg_focus: the background color for focused window.
1203 function titlebar.add(c, args)
1204 if not args then args = {} end
1205 -- Store colors
1206 titlebar.data[c] = {}
1207 titlebar.data[c].fg = args.fg or theme.fg_normal
1208 titlebar.data[c].bg = args.bg or theme.bg_normal
1209 titlebar.data[c].fg_focus = args.fg_focus or theme.fg_focus
1210 titlebar.data[c].bg_focus = args.bg_focus or theme.bg_focus
1212 -- Built args
1213 local targs = {}
1214 if args.fg then targs.fg = args.fg end
1215 if args.bg then targs.bg = args.bg end
1216 local tb = capi.titlebar(targs)
1218 local title = capi.widget({ type = "textbox", name = "title", align = "flex" })
1219 title:mouse_add(capi.mouse({ }, 1, function (t) t.client:mouse_move() end))
1220 title:mouse_add(capi.mouse({ args.modkey }, 3, function (t) t.client:mouse_resize() end))
1222 local close_button= capi.widget({ type = "textbox", name = "close", align = "right" })
1223 close_button:mouse_add(capi.mouse({ }, 1, function (t) t.client:kill() end))
1225 tb.widgets =
1227 capi.widget({ type = "appicon", name = "appicon", align = "left" }),
1228 title,
1229 close_button
1232 titlebar.update(c)
1234 c.titlebar = tb
1237 --- Update a titlebar. This should be called in some hooks.
1238 -- @param c The client to update.
1239 function titlebar.update(c)
1240 if c.titlebar and titlebar.data[c] then
1241 local widgets = c.titlebar.widgets
1242 local title, close
1243 for k, v in ipairs(widgets) do
1244 if v.name == "title" then title = v end
1245 if v.name == "close" then close = v end
1246 if title and close then break end
1248 if title then
1249 title.text = " " .. escape(c.name)
1251 if capi.client.focus_get() == c then
1252 c.titlebar.fg = titlebar.data[c].fg_focus
1253 c.titlebar.bg = titlebar.data[c].bg_focus
1254 if close then
1255 close.text = "<bg image=\"@AWESOME_ICON_PATH@/titlebar/closer.png\" resize=\"true\"/>"
1257 else
1258 c.titlebar.fg = titlebar.data[c].fg
1259 c.titlebar.bg = titlebar.data[c].bg
1260 if close then
1261 close.text = "<bg image=\"@AWESOME_ICON_PATH@/titlebar/close.png\" resize=\"true\"/>"
1267 --- Remove a titlebar from a client.
1268 -- @param c The client.
1269 function titlebar.remove(c)
1270 c.titlebar = nil
1271 titlebar.data[c] = nil
1274 --- Set the beautiful theme if any.
1275 -- @param The beautiful theme.
1276 function beautiful.register(btheme)
1277 if btheme then
1278 theme = btheme
1279 else
1280 theme = {}
1284 -- Register standards hooks
1285 hooks.arrange.register(tag.history.update)
1287 hooks.focus.register(client.focus.history.add)
1288 hooks.unmanage.register(client.focus.history.delete)
1290 hooks.focus.register(titlebar.update)
1291 hooks.unfocus.register(titlebar.update)
1292 hooks.titleupdate.register(titlebar.update)
1293 hooks.unmanage.register(titlebar.remove)
1295 -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80