1 -- Luakit configuration file, more information at http://luakit.org/
7 -- Widget construction aliases
8 function eventbox() return widget
{type="eventbox"} end
9 function hbox() return widget
{type="hbox"} end
10 function label() return widget
{type="label"} end
11 function notebook() return widget
{type="notebook"} end
12 function vbox() return widget
{type="vbox"} end
13 function webview() return widget
{type="webview"} end
14 function window() return widget
{type="window"} end
15 function entry() return widget
{type="entry"} end
18 -- Variable definitions
19 HOMEPAGE
= "http://luakit.org/"
20 --HOMEPAGE = "http://github.com/mason-larobina/luakit"
23 MAX_SRCH_HISTORY
= 100
24 --HTTPPROXY = "http://example.com:3128"
26 -- Setup download directory
27 DOWNLOAD_DIR
= luakit
.get_special_dir("DOWNLOAD") or (os
.getenv("HOME") .. "/downloads")
29 -- Per-domain webview properties
32 ["enable-scripts"] = false,
33 ["enable-plugins"] = false,
34 ["enable-private-browsing"] = false,
35 ["user-stylesheet-uri"] = "",
38 ["enable-scripts"] = true,
39 ["enable-plugins"] = true,
41 ["forums.archlinux.org"] = {
42 ["user-stylesheet-uri"] = luakit.data_dir .. "/styles/dark.css",
43 ["enable-private-browsing"] = true,
50 font
= "monospace normal 9",
55 statusbar_fg
= "#fff",
56 statusbar_bg
= "#000",
61 loaded_fg
= "#33AADD",
64 selected_tablabel_fg
= "#fff",
65 selected_tablabel_bg
= "#000",
67 -- Enforce a minimum tab width of 30 characters to prevent longer tab
68 -- titles overshadowing small tab titles when things get crowded.
69 tablabel_format
= "%-30s",
72 -- Small util functions
73 function info(...) if luakit
.verbose
then print(string.format(...)) end end
75 widget
.add_signal("new", function (wi
)
76 wi
:add_signal("init", function (wi
)
77 if wi
.type == "window" then
78 wi
:add_signal("destroy", function ()
79 -- Call the quit function if this was the last window left
80 if #luakit
.windows
== 0 then luakit
.quit() end
88 luakit
= "http://luakit.org/search/index/luakit?q={0}",
89 google
= "http://google.com/search?q={0}",
90 wikipedia
= "http://en.wikipedia.org/wiki/Special:Search?search={0}",
91 debbugs
= "http://bugs.debian.org/{0}",
92 imdb
= "http://imdb.com/find?s=all&q={0}",
93 sourceforge
= "http://sf.net/search/?words={0}",
96 -- Add key bindings to be used across all windows
98 -- bind.buf(Pattern, function (w, buffer, opts) .. end, opts),
99 -- bind.key({Modifiers}, Key name, function (w, opts) .. end, opts),
100 -- bind.but({Modifiers}, Button num, function (w, opts) .. end, opts),
102 bind
.key({}, "Escape", function (w
) w
:set_mode() end),
103 bind
.key({"Control"}, "[", function (w
) w
:set_mode() end),
106 bind
.but({}, 2, function (w
)
107 -- Open hovered uri in new tab
108 local uri
= w
:get_current().hovered_uri
109 if uri
then w
:new_tab(uri
)
110 else -- Open selection in current tab
111 uri
= luakit
.get_selection()
112 if uri
then w
:get_current().uri
= uri
end
115 bind
.but({}, 8, function (w
) w
:back() end),
116 bind
.but({}, 9, function (w
) w
:forward() end),
119 bind
.key({}, "i", function (w
) w
:set_mode("insert") end),
120 bind
.key({}, ":", function (w
) w
:set_mode("command") end),
123 bind
.key({}, "h", function (w
) w
:scroll_horiz("-"..SCROLL_STEP
.."px") end),
124 bind
.key({}, "j", function (w
) w
:scroll_vert ("+"..SCROLL_STEP
.."px") end),
125 bind
.key({}, "k", function (w
) w
:scroll_vert ("-"..SCROLL_STEP
.."px") end),
126 bind
.key({}, "l", function (w
) w
:scroll_horiz("+"..SCROLL_STEP
.."px") end),
127 bind
.key({}, "Left", function (w
) w
:scroll_horiz("-"..SCROLL_STEP
.."px") end),
128 bind
.key({}, "Down", function (w
) w
:scroll_vert ("+"..SCROLL_STEP
.."px") end),
129 bind
.key({}, "Up", function (w
) w
:scroll_vert ("-"..SCROLL_STEP
.."px") end),
130 bind
.key({}, "Right", function (w
) w
:scroll_horiz("+"..SCROLL_STEP
.."px") end),
131 bind
.key({"Control"}, "d", function (w
) w
:scroll_page(0.5) end),
132 bind
.key({"Control"}, "u", function (w
) w
:scroll_page(-0.5) end),
133 bind
.key({"Control"}, "f", function (w
) w
:scroll_page(1.0) end),
134 bind
.key({"Control"}, "b", function (w
) w
:scroll_page(-1.0) end),
135 bind
.buf("^gg$", function (w
) w
:scroll_vert("0%") end),
136 bind
.buf("^G$", function (w
) w
:scroll_vert("100%") end),
137 bind
.buf("^[\-\+]?[0-9]+[%%G]$", function (w
, b
) w
:scroll_vert(string.match(b
, "^([\-\+]?%d+)[%%G]$") .. "%") end),
140 bind
.key({}, "p", function (w
) w
:navigate(luakit
.get_selection()) end),
141 bind
.key({}, "P", function (w
) w
:new_tab(luakit
.get_selection()) end),
142 bind
.buf("^yy$", function (w
) luakit
.set_selection(w
:get_current().uri
) end),
143 bind
.buf("^yt$", function (w
) luakit
.set_selection(w
.win
.title
) end),
146 bind
.buf("^o$", function (w
, c
) w
:enter_cmd(":open ") end),
147 bind
.buf("^t$", function (w
, c
) w
:enter_cmd(":tabopen ") end),
148 bind
.buf("^,g$", function (w
, c
) w
:enter_cmd(":websearch google ") end),
151 bind
.key({}, "/", function (w
) w
:start_search(true) end),
152 bind
.key({}, "?", function (w
) w
:start_search(false) end),
153 bind
.key({}, "n", function (w
) w
:search(nil, true) end),
154 bind
.key({}, "N", function (w
) w
:search(nil, false) end),
157 bind
.buf("^[0-9]*H$", function (w
, b
) w
:back (tonumber(string.match(b
, "^(%d*)H$") or 1)) end),
158 bind
.buf("^[0-9]*L$", function (w
, b
) w
:forward(tonumber(string.match(b
, "^(%d*)L$") or 1)) end),
161 bind
.buf("^[0-9]*gT$", function (w
, b
) w
:prev_tab(tonumber(string.match(b
, "^(%d*)gT$") or 1)) end),
162 bind
.buf("^[0-9]*gt$", function (w
, b
) w
:next_tab(tonumber(string.match(b
, "^(%d*)gt$") or 1)) end),
163 bind
.buf("^gH$", function (w
) w
:new_tab(HOMEPAGE
) end),
164 bind
.buf("^d$", function (w
) w
:close_tab() end),
166 bind
.key({}, "r", function (w
) w
:reload() end),
167 bind
.buf("^gh$", function (w
) w
:navigate(HOMEPAGE
) end),
168 bind
.buf("^ZZ$", function (w
) luakit
.quit() end),
171 bind
.key({}, "f", function (w
) w
:set_mode("follow") end),
175 bind
.key({"Shift"}, "Insert", function (w
) w
:insert_cmd(luakit
.get_selection()) end),
176 bind
.key({}, "Up", function (w
) w
:cmd_hist_prev() end),
177 bind
.key({}, "Down", function (w
) w
:cmd_hist_next() end),
178 bind
.key({}, "Tab", function (w
) w
:cmd_completion() end),
179 bind
.key({"Control"}, "w", function (w
) w
:del_word() end),
180 bind
.key({"Control"}, "u", function (w
) w
:del_line() end),
183 bind
.key({}, "Up", function (w
) w
:srch_hist_prev() end),
184 bind
.key({}, "Down", function (w
) w
:srch_hist_next() end),
191 -- bind.cmd({Command, Alias1, ...}, function (w, arg, opts) .. end, opts),
192 bind
.cmd({"open", "o" }, function (w
, a
) w
:navigate(a
) end),
193 bind
.cmd({"tabopen", "t" }, function (w
, a
) w
:new_tab(a
) end),
194 bind
.cmd({"back" }, function (w
, a
) w
:back(tonumber(a
) or 1) end),
195 bind
.cmd({"forward", "f" }, function (w
, a
) w
:forward(tonumber(a
) or 1) end),
196 bind
.cmd({"scroll" }, function (w
, a
) w
:scroll_vert(a
) end),
197 bind
.cmd({"quit", "q" }, function (w
) luakit
.quit() end),
198 bind
.cmd({"close", "c" }, function (w
) w
:close_tab() end),
199 bind
.cmd({"websearch", "ws" }, function (w
, e
, s
) w
:websearch(e
, s
) end),
200 bind
.cmd({"reload", }, function (w
) w
:reload() end),
201 bind
.cmd({"viewsource", "vs" }, function (w
) w
:toggle_source(true) end),
202 bind
.cmd({"viewsource!", "vs!"}, function (w
) w
:toggle_source() end),
205 function set_http_options(w
)
206 local proxy
= HTTPPROXY
or os
.getenv("http_proxy")
207 if proxy
then w
:set('proxy-uri', proxy
) end
208 w
:set('user-agent', 'luakit')
209 -- Uncomment the following options if you want to enable SSL certs validation.
210 -- w:set('ssl-ca-file', '/etc/certs/ca-certificates.crt')
211 -- w:set('ssl-strict', true)
214 -- Build and pack window widgets
215 function build_window()
216 -- Create a table for widgets and state variables for a window
228 -- Status bar widgets
232 -- Left aligned widgets
239 -- Fills space between the left and right aligned widgets
241 -- Right aligned widgets
260 w
.ebox
:set_child(w
.layout
)
261 w
.win
:set_child(w
.ebox
)
265 t
.ebox
:set_child(t
.layout
, false, false, 0)
266 w
.layout
:pack_start(t
.ebox
, false, false, 0)
269 w
.layout
:pack_start(w
.tabs
, true, true, 0)
271 -- Pack left-aligned statusbar elements
273 l
.layout
:pack_start(l
.uri
, false, false, 0)
274 l
.layout
:pack_start(l
.loaded
, false, false, 0)
275 l
.ebox
:set_child(l
.layout
)
277 -- Pack right-aligned statusbar elements
279 r
.layout
:pack_start(r
.buf
, false, false, 0)
280 r
.layout
:pack_start(r
.tabi
, false, false, 0)
281 r
.layout
:pack_start(r
.scroll
, false, false, 0)
282 r
.ebox
:set_child(r
.layout
)
284 -- Pack status bar elements
286 s
.layout
:pack_start(l
.ebox
, false, false, 0)
287 s
.layout
:pack_start(s
.filler
, true, true, 0)
288 s
.layout
:pack_start(r
.ebox
, false, false, 0)
289 s
.ebox
:set_child(s
.layout
)
290 w
.layout
:pack_start(s
.ebox
, false, false, 0)
294 i
.layout
:pack_start(i
.prompt
, false, false, 0)
295 i
.layout
:pack_start(i
.input
, true, true, 0)
296 i
.ebox
:set_child(i
.layout
)
297 w
.layout
:pack_start(i
.ebox
, false, false, 0)
300 i
.input
.show_frame
= false
301 w
.tabs
.show_tabs
= false
303 l
.uri
.selectable
= true
308 function attach_window_signals(w
)
309 -- Attach notebook widget signals
310 w
.tabs
:add_signal("page-added", function (nbook
, view
, idx
)
311 w
:update_tab_count(idx
)
312 w
:update_tab_labels()
315 w
.tabs
:add_signal("switch-page", function (nbook
, view
, idx
)
316 w
:update_tab_count(idx
)
317 w
:update_win_title(view
)
319 w
:update_progress(view
)
320 w
:update_tab_labels(idx
)
323 -- Attach window widget signals
324 w
.win
:add_signal("key-press", function (win
, mods
, key
)
325 -- Reset command line completion
326 if w
:get_mode() == "command" and key
~= "Tab" and w
.compl_start
then
331 if w
:hit(mods
, key
) then
336 w
.win
:add_signal("mode-changed", function (win
, mode
)
337 local i
, p
= w
.ibar
.input
, w
.ibar
.prompt
340 w
.cmd_hist_cursor
= nil
342 -- Clear following hints if the user exits follow mode
343 if w
.showing_hints
then
344 w
:eval_js("clear();");
345 w
.showing_hints
= false
348 -- If a user aborts a search return to the original position
349 if w
.search_start_marker
then
350 w
:get_current():set_scroll_vert(w
.search_start_marker
)
351 w
.search_start_marker
= nil
354 if mode
== "normal" then
357 elseif mode
== "insert" then
360 p
.text
= "-- INSERT --"
362 elseif mode
== "command" then
368 elseif mode
== "search" then
371 elseif mode
== "follow" then
372 w
:eval_js_from_file(util
.find_data("scripts/follow.js"))
373 w
:eval_js("clear(); show_hints();")
374 w
.showing_hints
= true
382 w
.ibar
.prompt
.text
= ""
383 w
.ibar
.input
.text
= ""
387 -- Attach inputbar widget signals
388 w
.ibar
.input
:add_signal("changed", function()
389 local text
= w
.ibar
.input
.text
390 -- Auto-exit "command" mode if you backspace or delete the ":"
391 -- character at the start of the input box when in "command" mode.
392 if w
:is_mode("command") and not string.match(text
, "^:") then
394 elseif w
:is_mode("search") then
395 if string.match(text
, "^[\?\/]") then
396 w
:search(string.sub(text
, 2), (string.sub(text
, 1, 1) == "/"))
401 elseif w
:is_mode("follow") then
402 w
:eval_js(string.format("update(%q)", w
.ibar
.input
.text
))
406 w
.ibar
.input
:add_signal("activate", function()
407 local text
= w
.ibar
.input
.text
408 if w
:is_mode("command") then
410 w
:match_cmd(string.sub(text
, 2))
412 elseif w
:is_mode("search") then
413 w
:srch_hist_add(text
)
414 w
:search(string.sub(text
, 2), string.sub(text
, 1, 1) == "/")
415 -- User doesn't want to return to start position
416 w
.search_start_marker
= nil
418 w
.ibar
.prompt
.text
= util
.escape(text
)
424 -- Attach signal handlers to a new tab's webview
425 function attach_webview_signals(w
, view
)
426 view
:add_signal("property::title", function (v
)
427 w
:update_tab_labels()
428 if w
:is_current(v
) then
429 w
:update_win_title(v
)
433 view
:add_signal("property::uri", function (v
)
434 w
:update_tab_labels()
435 if w
:is_current(v
) then
440 view
:add_signal("link-hover", function (v
, link
)
441 if w
:is_current(v
) and link
then
442 w
.sbar
.l
.uri
.text
= "Link: " .. util
.escape(link
)
446 view
:add_signal("link-unhover", function (v
)
447 if w
:is_current(v
) then
452 view
:add_signal("form-active", function ()
456 view
:add_signal("root-active", function ()
460 view
:add_signal("key-press", function ()
461 -- Only allow key press events to hit the webview if the user is in
463 if not w
:is_mode("insert") then
468 view
:add_signal("button-release", function (v
, mods
, button
)
469 if w
:hit(mods
, button
) then
474 -- Update progress widgets & set default mode on navigate
475 view
:add_signal("load-status", function (v
, status
)
476 if w
:is_current(v
) then
478 if status
== "provisional" then
485 view
:add_signal("load-status", function (v
, status
)
486 if status
== "committed" then
487 local domain
= string.match(v
.uri
, "^%a+://([^/]*)/?")
488 if string.match(domain
, "^www.") then domain
= string.sub(domain
, 5) end
489 local props
= util
.table.join(domain_props
.all
or {}, domain_props
[domain
] or {})
490 for k
, v
in pairs(props
) do
491 info("Domain prop: %s = %s (%s)", k
, tostring(v
), domain
)
497 -- 'link' contains the download link
498 -- 'mime' contains the mime type that is requested
499 -- return TRUE to accept or FALSE to reject
500 view
:add_signal("mime-type-decision", function (v
, link
, mime
)
501 info("Requested link: %s (%s)", link
, mime
)
502 -- i.e. block binary files like *.exe
503 --if mime == "application/octet-stream" then
508 -- 'link' contains the download link
509 -- 'filename' contains the suggested filename (from server or webkit)
510 view
:add_signal("download-request", function (v
, link
, filename
)
511 if not filename
then return end
513 os
.execute(string.format("mkdir -p %q", DOWNLOAD_DIR
))
514 local dl
= DOWNLOAD_DIR
.. "/" .. filename
515 local wget
= string.format("wget -q %q -O %q", link
, dl
)
516 info("Launching: %s", wget
)
520 -- 'link' contains the download link
521 -- 'reason' contains the reason of the request (i.e. "link-clicked")
522 -- return TRUE to handle the request by yourself or FALSE to proceed
523 -- with default behaviour
524 view
:add_signal("new-window-decision", function (v
, link
, reason
)
525 info("New window decision: %s (%s)", link
, reason
)
526 if reason
== "link-clicked" then
533 view
:add_signal("create-web-view", function (v
)
534 w
:new_tab(v
.hovered_uri
)
535 -- new_window({ w.hovered_url })
538 view
:add_signal("property::progress", function (v
)
539 if w
:is_current(v
) then
544 view
:add_signal("expose", function (v
)
545 if w
:is_current(v
) then
551 -- Parses scroll amounts of the form:
552 -- Relative: "+20%", "-20%", "+20px", "-20px"
553 -- Absolute: 20, "20%", "20px"
554 -- And returns an absolute value.
555 function parse_scroll(current
, max, value
)
556 if string.match(value
, "^%d+px$") then
557 return tonumber(string.match(value
, "^(%d+)px$"))
558 elseif string.match(value
, "^%d+%%$") then
559 return math
.ceil(max * (tonumber(string.match(value
, "^(%d+)%%$")) / 100))
560 elseif string.match(value
, "^[\-\+]%d+px") then
561 return current
+ tonumber(string.match(value
, "^([\-\+]%d+)px"))
562 elseif string.match(value
, "^[\-\+]%d+%%$") then
563 return math
.ceil(current
+ (max * (tonumber(string.match(value
, "^([\-\+]%d+)%%$")) / 100)))
565 print("E: unable to parse scroll amount:", value
)
569 -- Helper functions which operate on a windows widget structure
571 -- Return the widget in the currently active tab
572 get_current
= function (w
) return w
.tabs
:atindex(w
.tabs
:current()) end,
573 -- Check if given widget is the widget in the currently active tab
574 is_current
= function (w
, wi
) return w
.tabs
:indexof(wi
) == w
.tabs
:current() end,
576 -- Wrappers around the mode plugin
577 set_mode
= function (w
, name
) mode
.set(w
.win
, name
) end,
578 get_mode
= function (w
) return mode
.get(w
.win
) end,
579 is_mode
= function (w
, name
) return name
== w
:get_mode() end,
580 is_any_mode
= function (w
, t
, name
) return util
.table.hasitem(t
, name
or w
:get_mode()) end,
582 -- Wrappers around the view:get_prop & view:set_prop methods
583 get
= function (w
, prop
, view
)
584 if not view
then view
= w
:get_current() end
585 return view
:get_prop(prop
)
588 set
= function (w
, prop
, val
, view
)
589 if not view
then view
= w
:get_current() end
590 view
:set_prop(prop
, val
)
593 get_tab_title
= function (w
, view
)
594 if not view
then view
= w
:get_current() end
595 return view
:get_prop("title") or view
.uri
or "(Untitled)"
598 navigate
= function (w
, uri
, view
)
599 local v
= view
or w
:get_current()
603 return w
:new_tab(uri
)
607 reload
= function (w
, view
)
608 if not view
then view
= w
:get_current() end
612 new_tab
= function (w
, uri
)
613 local view
= webview()
616 attach_webview_signals(w
, view
)
617 if uri
then view
.uri
= uri
end
618 view
.show_scrollbars
= false
622 -- close the current tab
623 close_tab
= function (w
, view
)
624 if not view
then view
= w
:get_current() end
625 if not view
then return end
631 -- evaluate javascript code and return string result
632 eval_js
= function (w
, script
, file
, view
)
633 if not view
then view
= w
:get_current() end
634 return view
:eval_js(script
, file
or "(buffer)")
637 -- evaluate javascript code from file and return string result
638 eval_js_from_file
= function (w
, file
, view
)
639 local fh
, err
= io
.open(file
)
640 if not fh
then return error(err
) end
641 local script
= fh
:read("*a")
643 return w
:eval_js(script
, file
, view
)
646 -- Wrapper around the bind plugin's hit method
647 hit
= function (w
, mods
, key
)
648 local caught
, newbuf
= bind
.hit(w
.binds
or {}, mods
, key
, w
.buffer
, w
:is_mode("normal"), w
)
654 -- Wrapper around the bind plugin's match_cmd method
655 match_cmd
= function (w
, buffer
)
656 return bind
.match_cmd(commands
, buffer
, w
)
659 -- Toggle source view
660 toggle_source
= function (w
, show
, view
)
661 if not view
then view
= w
:get_current() end
662 if show
== nil then show
= not view
:get_view_source() end
663 view
:set_view_source(show
)
666 -- enter command or characters into command line
667 enter_cmd
= function (w
, cmd
)
668 local i
= w
.ibar
.input
669 w
:set_mode("command")
674 -- insert a string into the command line at the current cursor position
675 insert_cmd
= function (w
, str
)
676 if not str
then return nil end
677 local i
= w
.ibar
.input
679 local pos
= i
:get_position()
680 local left
, right
= string.sub(text
, 1, pos
), string.sub(text
, pos
+1)
681 i
.text
= left
.. str
.. right
682 i
:set_position(pos
+ #str
+ 1)
685 -- search engine wrapper
686 websearch
= function (w
, args
)
687 local sep
= string.find(args
, " ")
688 local engine
= string.sub(args
, 1, sep
-1)
689 local search
= string.sub(args
, sep
+1)
690 if not search_engines
[engine
] then
691 print("E: No matching search engine found:", engine
)
694 local uri
= string.gsub(search_engines
[engine
], "{%d}", search
)
695 return w
:navigate(uri
)
698 -- Command line completion of available commands
699 cmd_completion
= function (w
)
700 local i
= w
.ibar
.input
701 local s
= w
.sbar
.l
.uri
704 -- Get last completion (is reset on key press other than <Tab>)
705 if not w
.compl_start
or w
.compl_index
== 0 then
706 w
.compl_start
= "^" .. string.sub(i
.text
, 2)
710 -- Get suitable commands
711 for _
, b
in ipairs(commands
) do
712 for _
, c
in pairs(b
.commands
) do
713 if c
and string.match(c
, w
.compl_start
) then
714 table.insert(cmpl
, c
)
723 for index
, comp
in pairs(cmpl
) do
724 if index
== w
.compl_index
then
725 i
.text
= ":" .. comp
.. " "
734 -- cycle through all possible completions
735 if w
.compl_index
== #cmpl
then
738 w
.compl_index
= w
.compl_index
+ 1
740 s
.text
= util
.escape(text
)
744 del_word
= function (w
)
745 local i
= w
.ibar
.input
747 local pos
= i
:get_position()
748 if text
and #text
> 1 and pos
> 1 then
749 local left
, right
= string.sub(text
, 2, pos
), string.sub(text
, pos
+1)
750 if not string.find(left
, "%s") then
752 elseif string.find(left
, "%w+%s*$") then
753 left
= string.sub(left
, 0, string.find(left
, "%w+%s*$") - 1)
754 elseif string.find(left
, "%W+%s*$") then
755 left
= string.sub(left
, 0, string.find(left
, "%W+%s*$") - 1)
757 i
.text
= string.sub(text
, 1, 1) .. left
.. right
758 i
:set_position(#left
+ 2)
762 del_line
= function (w
)
763 local i
= w
.ibar
.input
764 if i
.text
~= ":" then
770 -- Search history adding
771 srch_hist_add
= function (w
, srch
)
772 if not w
.srch_hist
then w
.srch_hist
= {} end
774 if #w
.srch_hist
> ((MAX_SRCH_HISTORY
or 100) + 5) then
775 while #w
.srch_hist
> (MAX_SRCH_HISTORY
or 100) do
776 table.remove(w
.srch_hist
, 1)
779 table.insert(w
.srch_hist
, srch
)
782 -- Search history traversing
783 srch_hist_prev
= function (w
)
784 if not w
.srch_hist
then w
.srch_hist
= {} end
785 if not w
.srch_hist_cursor
then
786 w
.srch_hist_cursor
= #w
.srch_hist
+ 1
787 w
.srch_hist_current
= w
.ibar
.input
.text
789 local c
= w
.srch_hist_cursor
- 1
790 if w
.srch_hist
[c
] then
791 w
.srch_hist_cursor
= c
792 w
.ibar
.input
.text
= w
.srch_hist
[c
]
793 w
.ibar
.input
:set_position(-1)
797 srch_hist_next
= function (w
)
798 if not w
.srch_hist
then w
.srch_hist
= {} end
799 local c
= (w
.srch_hist_cursor
or #w
.srch_hist
) + 1
800 if w
.srch_hist
[c
] then
801 w
.srch_hist_cursor
= c
802 w
.ibar
.input
.text
= w
.srch_hist
[c
]
803 w
.ibar
.input
:set_position(-1)
804 elseif w
.srch_hist_current
then
805 w
.srch_hist_cursor
= nil
806 w
.ibar
.input
.text
= w
.srch_hist_current
807 w
.ibar
.input
:set_position(-1)
811 -- Command history adding
812 cmd_hist_add
= function (w
, cmd
)
813 if not w
.cmd_hist
then w
.cmd_hist
= {} end
814 -- Make sure history doesn't overflow
815 if #w
.cmd_hist
> ((MAX_CMD_HISTORY
or 100) + 5) then
816 while #w
.cmd_hist
> (MAX_CMD_HISTORY
or 100) do
817 table.remove(w
.cmd_hist
, 1)
820 table.insert(w
.cmd_hist
, cmd
)
823 -- Command history traversing
824 cmd_hist_prev
= function (w
)
825 if not w
.cmd_hist
then w
.cmd_hist
= {} end
826 if not w
.cmd_hist_cursor
then
827 w
.cmd_hist_cursor
= #w
.cmd_hist
+ 1
828 w
.cmd_hist_current
= w
.ibar
.input
.text
830 local c
= w
.cmd_hist_cursor
- 1
831 if w
.cmd_hist
[c
] then
832 w
.cmd_hist_cursor
= c
833 w
.ibar
.input
.text
= w
.cmd_hist
[c
]
834 w
.ibar
.input
:set_position(-1)
838 cmd_hist_next
= function (w
)
839 if not w
.cmd_hist
then w
.cmd_hist
= {} end
840 local c
= (w
.cmd_hist_cursor
or #w
.cmd_hist
) + 1
841 if w
.cmd_hist
[c
] then
842 w
.cmd_hist_cursor
= c
843 w
.ibar
.input
.text
= w
.cmd_hist
[c
]
844 w
.ibar
.input
:set_position(-1)
845 elseif w
.cmd_hist_current
then
846 w
.cmd_hist_cursor
= nil
847 w
.ibar
.input
.text
= w
.cmd_hist_current
848 w
.ibar
.input
:set_position(-1)
852 -- Searching functions
853 start_search
= function (w
, forward
)
854 -- Clear previous search results
857 local i
= w
.ibar
.input
867 search
= function (w
, text
, forward
)
868 local view
= w
:get_current()
869 local text
= text
or w
.last_search
870 if forward
== nil then forward
= true end
871 local case_sensitive
= false
874 if not text
or #text
== 0 then
880 if w
.searching_forward
== nil then
881 w
.searching_forward
= forward
882 w
.search_start_marker
= view
:get_scroll_vert()
884 -- Invert the direction if originally searching in reverse
885 forward
= (w
.searching_forward
== forward
)
888 view
:search(text
, case_sensitive
, forward
, wrap
);
891 clear_search
= function (w
)
892 w
:get_current():clear_search()
893 -- Clear search state
895 w
.searching_forward
= nil
896 w
.search_start_marker
= nil
899 -- Webview scroll functions
900 scroll_vert
= function (w
, value
, view
)
901 if not view
then view
= w
:get_current() end
902 local cur
, max = view
:get_scroll_vert()
903 if type(value
) == "string" then
904 value
= parse_scroll(cur
, max, value
)
906 view
:set_scroll_vert(value
)
909 scroll_horiz
= function (w
, value
, view
)
910 if not view
then view
= w
:get_current() end
911 local cur
, max = view
:get_scroll_horiz()
912 if type(value
) == "string" then
913 value
= parse_scroll(cur
, max, value
)
915 view
:set_scroll_horiz(value
)
918 -- vertical scroll of a multiple of the view_size
919 scroll_page
= function (w
, value
, view
)
920 if not view
then view
= w
:get_current() end
921 local cur
, max, size
= view
:get_scroll_vert()
922 view
:set_scroll_vert(cur
+ size
* value
)
925 -- Tab traversing functions
926 next_tab
= function (w
, n
)
927 w
.tabs
:switch((((n
or 1) + w
.tabs
:current() -1) % w
.tabs
:count()) + 1)
929 prev_tab
= function (w
, n
)
930 w
.tabs
:switch(((w
.tabs
:current() - (n
or 1) -1) % w
.tabs
:count()) + 1)
932 goto_tab
= function (w
, n
)
936 -- History traversing functions
937 back
= function (w
, n
, view
)
938 (view
or w
:get_current()):go_back(n
or 1)
940 forward
= function (w
, n
, view
)
941 (view
or w
:get_current()):go_forward(n
or 1)
944 -- GUI content update functions
945 update_tab_count
= function (w
, i
, t
)
946 w
.sbar
.r
.tabi
.text
= string.format("[%d/%d]", i
or w
.tabs
:current(), t
or w
.tabs
:count())
949 update_win_title
= function (w
, view
)
950 if not view
then view
= w
:get_current() end
951 local title
= view
:get_prop("title")
953 if not title
and not uri
then
954 w
.win
.title
= "luakit"
956 w
.win
.title
= (title
or "luakit") .. " - " .. (uri
or "about:blank")
960 update_uri
= function (w
, view
, uri
)
961 if not view
then view
= w
:get_current() end
962 w
.sbar
.l
.uri
.text
= util
.escape((uri
or (view
and view
.uri
) or "about:blank"))
965 update_progress
= function (w
, view
, p
)
966 if not view
then view
= w
:get_current() end
967 if not p
then p
= view
:get_prop("progress") end
968 if not view
:loading() or p
== 1 then
969 w
.sbar
.l
.loaded
:hide()
971 w
.sbar
.l
.loaded
:show()
972 w
.sbar
.l
.loaded
.text
= string.format("(%d%%)", p
* 100)
976 update_scroll
= function (w
, view
)
977 if not view
then view
= w
:get_current() end
978 local val
, max = view
:get_scroll_vert()
979 if max == 0 then val
= "All"
980 elseif val
== 0 then val
= "Top"
981 elseif val
== max then val
= "Bot"
982 else val
= string.format("%2d%%", (val
/max) * 100)
984 w
.sbar
.r
.scroll
.text
= val
987 update_buf
= function (w
)
989 w
.sbar
.r
.buf
.text
= util
.escape(string.format(" %-3s", w
.buffer
))
996 update_binds
= function (w
, mode
)
997 -- Generate the list of active key & buffer binds for this mode
998 w
.binds
= util
.table.join(mode_binds
[mode
], mode_binds
.all
)
999 -- Clear & hide buffer
1004 -- Tab label functions
1005 make_tab_label
= function (w
, pos
)
1012 t
.label
.font
= theme
.tablabel_font
or theme
.font
1013 t
.layout
:pack_start(t
.label
, true, true, 0)
1014 t
.layout
:pack_start(t
.sep
, false, false, 0)
1015 t
.ebox
:set_child(t
.layout
)
1016 t
.ebox
:add_signal("button-release", function (e
, m
, b
)
1021 w
:close_tab(w
.tabs
:atindex(pos
))
1028 destroy_tab_label
= function (w
, t
)
1029 if not t
then t
= table.remove(w
.tbar
.titles
) end
1030 for _
, wi
in pairs(t
) do
1035 update_tab_labels
= function (w
, current
)
1037 local count
, current
= w
.tabs
:count(), current
or w
.tabs
:current()
1040 -- Leave the tablist hidden if there is only one tab open
1045 if count
~= #tb
.titles
then
1046 -- Grow the number of labels
1047 while count
> #tb
.titles
do
1048 local t
= w
:make_tab_label(#tb
.titles
+ 1)
1049 tb
.layout
:pack_start(t
.ebox
, true, true, 0)
1050 table.insert(tb
.titles
, t
)
1052 -- Prune number of labels
1053 while count
< #tb
.titles
do
1054 w
:destroy_tab_label()
1060 local t
= tb
.titles
[i
]
1061 local title
= " " ..i
.. " "..w
:get_tab_title(w
.tabs
:atindex(i
))
1062 t
.label
.text
= util
.escape(string.format(theme
.tablabel_format
or "%s", title
))
1063 w
:apply_tablabel_theme(t
, i
== current
)
1070 apply_tablabel_theme
= function (w
, t
, selected
, atheme
)
1071 local theme
= atheme
or theme
1073 t
.label
.fg
= theme
.selected_tablabel_fg
or theme
.tablabel_fg
or theme
.fg
1074 t
.ebox
.bg
= theme
.selected_tablabel_bg
or theme
.tablabel_bg
or theme
.bg
1076 t
.label
.fg
= theme
.tablabel_fg
or theme
.fg
1077 t
.ebox
.bg
= theme
.tablabel_bg
or theme
.bg
1081 apply_window_theme
= function (w
, atheme
)
1082 local theme
= atheme
or theme
1083 local s
, i
, t
= w
.sbar
, w
.ibar
, w
.tbar
1084 local fg
, bg
, font
= theme
.fg
, theme
.bg
, theme
.font
1087 for wi
, v
in pairs({
1088 [s
.l
.uri
] = theme
.uri_fg
or theme
.statusbar_fg
or fg
,
1089 [s
.l
.loaded
] = theme
.loaded_fg
or theme
.statusbar_fg
or fg
,
1090 [s
.r
.buf
] = theme
.buf_fg
or theme
.statusbar_fg
or fg
,
1091 [s
.r
.tabi
] = theme
.tabi_fg
or theme
.statusbar_fg
or fg
,
1092 [s
.r
.scroll
] = theme
.scroll_fg
or theme
.statusbar_fg
or fg
,
1093 [i
.prompt
] = theme
.prompt_fg
or theme
.inputbar_fg
or fg
,
1094 [i
.input
] = theme
.input_fg
or theme
.inputbar_fg
or fg
,
1098 for wi
, v
in pairs({
1099 [s
.l
.ebox
] = theme
.statusbar_bg
or bg
,
1100 [s
.r
.ebox
] = theme
.statusbar_bg
or bg
,
1101 [s
.ebox
] = theme
.statusbar_bg
or bg
,
1102 [i
.ebox
] = theme
.inputbar_bg
or bg
,
1103 [i
.input
] = theme
.input_bg
or theme
.inputbar_bg
or bg
,
1107 for wi
, v
in pairs({
1108 [s
.l
.uri
] = theme
.uri_font
or theme
.statusbar_font
or font
,
1109 [s
.l
.loaded
] = theme
.loaded_font
or theme
.statusbar_font
or font
,
1110 [s
.r
.buf
] = theme
.buf_font
or theme
.statusbar_font
or font
,
1111 [s
.r
.tabi
] = theme
.tabi_font
or theme
.statusbar_font
or font
,
1112 [s
.r
.scroll
] = theme
.scroll_font
or theme
.statusbar_font
or font
,
1113 [i
.prompt
] = theme
.prompt_font
or theme
.inputbar_font
or font
,
1114 [i
.input
] = theme
.input_font
or theme
.inputbar_font
or font
,
1115 }) do wi
.font
= v
end
1119 -- Create new window
1120 function new_window(uris
)
1121 local w
= build_window()
1123 -- Pack the window table full of the common helper functions
1124 for k
, v
in pairs(window_helpers
) do w
[k
] = v
end
1126 attach_window_signals(w
)
1128 -- Apply window theme
1129 w
:apply_window_theme()
1131 -- Populate notebook with tabs
1132 for _
, uri
in ipairs(uris
or {}) do
1136 -- Make sure something is loaded
1137 if w
.tabs
:count() == 0 then
1149 -- vim: ft=lua:et:sw=4:ts=8:sts=4:tw=80