860 lines
27 KiB
Lua
860 lines
27 KiB
Lua
|
------------------
|
||
|
-- Window class --
|
||
|
------------------
|
||
|
|
||
|
require "lfs"
|
||
|
|
||
|
-- Window class table
|
||
|
window = {}
|
||
|
|
||
|
-- List of active windows by window widget
|
||
|
window.bywidget = setmetatable({}, { __mode = "k" })
|
||
|
|
||
|
-- Widget construction aliases
|
||
|
local function entry() return widget{type="entry"} end
|
||
|
local function eventbox() return widget{type="eventbox"} end
|
||
|
local function hbox() return widget{type="hbox"} end
|
||
|
local function label() return widget{type="label"} end
|
||
|
local function notebook() return widget{type="notebook"} end
|
||
|
local function vbox() return widget{type="vbox"} end
|
||
|
|
||
|
-- Build and pack window widgets
|
||
|
function window.build()
|
||
|
-- Create a table for widgets and state variables for a window
|
||
|
local w = {
|
||
|
win = widget{type="window"},
|
||
|
ebox = eventbox(),
|
||
|
layout = vbox(),
|
||
|
paned = widget{type="vpaned"},
|
||
|
tabs = notebook(),
|
||
|
-- Tablist widget
|
||
|
tablist = lousy.widget.tablist(),
|
||
|
-- Status bar widgets
|
||
|
sbar = {
|
||
|
layout = hbox(),
|
||
|
ebox = eventbox(),
|
||
|
-- Left aligned widgets
|
||
|
l = {
|
||
|
layout = hbox(),
|
||
|
ebox = eventbox(),
|
||
|
uri = label(),
|
||
|
hist = label(),
|
||
|
loaded = label(),
|
||
|
},
|
||
|
-- Fills space between the left and right aligned widgets
|
||
|
sep = eventbox(),
|
||
|
-- Right aligned widgets
|
||
|
r = {
|
||
|
layout = hbox(),
|
||
|
ebox = eventbox(),
|
||
|
buf = label(),
|
||
|
ssl = label(),
|
||
|
tabi = label(),
|
||
|
scroll = label(),
|
||
|
},
|
||
|
},
|
||
|
|
||
|
-- Vertical menu window widget (completion results, bookmarks, qmarks, ..)
|
||
|
menu = lousy.widget.menu(),
|
||
|
|
||
|
-- Input bar widgets
|
||
|
ibar = {
|
||
|
layout = hbox(),
|
||
|
ebox = eventbox(),
|
||
|
prompt = label(),
|
||
|
input = entry(),
|
||
|
},
|
||
|
closed_tabs = {}
|
||
|
}
|
||
|
|
||
|
-- Assemble window
|
||
|
w.ebox.child = w.paned
|
||
|
w.paned:pack1(w.layout)
|
||
|
w.win.child = w.ebox
|
||
|
|
||
|
-- Pack tablist
|
||
|
w.layout:pack(w.tablist.widget)
|
||
|
|
||
|
-- Pack notebook
|
||
|
w.layout:pack(w.tabs, { expand = true, fill = true })
|
||
|
|
||
|
-- Pack left-aligned statusbar elements
|
||
|
local l = w.sbar.l
|
||
|
l.layout:pack(l.uri)
|
||
|
l.layout:pack(l.hist)
|
||
|
l.layout:pack(l.loaded)
|
||
|
l.ebox.child = l.layout
|
||
|
|
||
|
-- Pack right-aligned statusbar elements
|
||
|
local r = w.sbar.r
|
||
|
r.layout:pack(r.buf)
|
||
|
r.layout:pack(r.ssl)
|
||
|
r.layout:pack(r.tabi)
|
||
|
r.layout:pack(r.scroll)
|
||
|
r.ebox.child = r.layout
|
||
|
|
||
|
-- Pack status bar elements
|
||
|
local s = w.sbar
|
||
|
s.layout:pack(l.ebox)
|
||
|
s.layout:pack(s.sep, { expand = true, fill = true })
|
||
|
s.layout:pack(r.ebox)
|
||
|
s.ebox.child = s.layout
|
||
|
w.layout:pack(s.ebox)
|
||
|
|
||
|
-- Pack menu widget
|
||
|
w.layout:pack(w.menu.widget)
|
||
|
w.menu:hide()
|
||
|
|
||
|
-- Pack input bar
|
||
|
local i = w.ibar
|
||
|
i.layout:pack(i.prompt)
|
||
|
i.layout:pack(i.input, { expand = true, fill = true })
|
||
|
i.ebox.child = i.layout
|
||
|
w.layout:pack(i.ebox)
|
||
|
|
||
|
-- Other settings
|
||
|
i.input.show_frame = false
|
||
|
w.tabs.show_tabs = false
|
||
|
l.loaded:hide()
|
||
|
l.hist:hide()
|
||
|
l.uri.selectable = true
|
||
|
r.ssl:hide()
|
||
|
|
||
|
-- Allows indexing of window struct by window widget
|
||
|
window.bywidget[w.win] = w
|
||
|
|
||
|
return w
|
||
|
end
|
||
|
|
||
|
-- Table of functions to call on window creation. Normally used to add signal
|
||
|
-- handlers to the new windows widgets.
|
||
|
window.init_funcs = {
|
||
|
-- Attach notebook widget signals
|
||
|
notebook_signals = function (w)
|
||
|
w.tabs:add_signal("page-added", function (nbook, view, idx)
|
||
|
luakit.idle_add(function ()
|
||
|
w:update_tab_count()
|
||
|
w:update_tablist()
|
||
|
return false
|
||
|
end)
|
||
|
end)
|
||
|
w.tabs:add_signal("switch-page", function (nbook, view, idx)
|
||
|
w.view = nil
|
||
|
w:set_mode()
|
||
|
-- Update widgets after tab switch
|
||
|
luakit.idle_add(function ()
|
||
|
w:update_tab_count()
|
||
|
w:update_win_title()
|
||
|
w:update_uri()
|
||
|
w:update_progress()
|
||
|
w:update_tablist()
|
||
|
w:update_buf()
|
||
|
w:update_ssl()
|
||
|
w:update_hist()
|
||
|
return false
|
||
|
end)
|
||
|
end)
|
||
|
w.tabs:add_signal("page-reordered", function (nbook, view, idx)
|
||
|
w:update_tab_count()
|
||
|
w:update_tablist()
|
||
|
end)
|
||
|
end,
|
||
|
|
||
|
last_win_check = function (w)
|
||
|
w.win:add_signal("destroy", function ()
|
||
|
-- call the quit function if this was the last window left
|
||
|
if #luakit.windows == 0 then luakit.quit() end
|
||
|
if w.close_win then w:close_win() end
|
||
|
end)
|
||
|
end,
|
||
|
|
||
|
key_press_match = function (w)
|
||
|
w.win:add_signal("key-press", function (_, mods, key)
|
||
|
-- Match & exec a bind
|
||
|
local success, match = xpcall(
|
||
|
function () return w:hit(mods, key) end,
|
||
|
function (err) w:error(debug.traceback(err, 3)) end)
|
||
|
|
||
|
if success and match then
|
||
|
return true
|
||
|
end
|
||
|
end)
|
||
|
end,
|
||
|
|
||
|
tablist_tab_click = function (w)
|
||
|
w.tablist:add_signal("tab-clicked", function (_, index, mods, button)
|
||
|
if button == 1 then
|
||
|
w.tabs:switch(index)
|
||
|
return true
|
||
|
elseif button == 2 then
|
||
|
w:close_tab(w.tabs[index])
|
||
|
return true
|
||
|
end
|
||
|
end)
|
||
|
end,
|
||
|
|
||
|
apply_window_theme = function (w)
|
||
|
local s, i = w.sbar, w.ibar
|
||
|
|
||
|
-- Set foregrounds
|
||
|
for wi, v in pairs({
|
||
|
[s.l.uri] = theme.uri_sbar_fg,
|
||
|
[s.l.hist] = theme.hist_sbar_fg,
|
||
|
[s.l.loaded] = theme.sbar_loaded_fg,
|
||
|
[s.r.buf] = theme.buf_sbar_fg,
|
||
|
[s.r.tabi] = theme.tabi_sbar_fg,
|
||
|
[s.r.scroll] = theme.scroll_sbar_fg,
|
||
|
[i.prompt] = theme.prompt_ibar_fg,
|
||
|
[i.input] = theme.input_ibar_fg,
|
||
|
}) do wi.fg = v end
|
||
|
|
||
|
-- Set backgrounds
|
||
|
for wi, v in pairs({
|
||
|
[s.l.ebox] = theme.sbar_bg,
|
||
|
[s.r.ebox] = theme.sbar_bg,
|
||
|
[s.sep] = theme.sbar_bg,
|
||
|
[s.ebox] = theme.sbar_bg,
|
||
|
[i.ebox] = theme.ibar_bg,
|
||
|
[i.input] = theme.input_ibar_bg,
|
||
|
}) do wi.bg = v end
|
||
|
|
||
|
-- Set fonts
|
||
|
for wi, v in pairs({
|
||
|
[s.l.uri] = theme.uri_sbar_font,
|
||
|
[s.l.hist] = theme.hist_sbar_font,
|
||
|
[s.l.loaded] = theme.sbar_loaded_font,
|
||
|
[s.r.buf] = theme.buf_sbar_font,
|
||
|
[s.r.ssl] = theme.ssl_sbar_font,
|
||
|
[s.r.tabi] = theme.tabi_sbar_font,
|
||
|
[s.r.scroll] = theme.scroll_sbar_font,
|
||
|
[i.prompt] = theme.prompt_ibar_font,
|
||
|
[i.input] = theme.input_ibar_font,
|
||
|
}) do wi.font = v end
|
||
|
end,
|
||
|
|
||
|
set_default_size = function (w)
|
||
|
local size = globals.default_window_size or "800x600"
|
||
|
if string.match(size, "^%d+x%d+$") then
|
||
|
w.win:set_default_size(string.match(size, "^(%d+)x(%d+)$"))
|
||
|
else
|
||
|
warn("E: window.lua: invalid window size: %q", size)
|
||
|
end
|
||
|
end,
|
||
|
|
||
|
set_window_icon = function (w)
|
||
|
local path = (luakit.dev_paths and os.exists("./extras/luakit.png")) or
|
||
|
os.exists("/usr/share/pixmaps/luakit.png")
|
||
|
if path then w.win.icon = path end
|
||
|
end,
|
||
|
|
||
|
clear_urgency_hint = function (w)
|
||
|
w.win:add_signal("focus", function ()
|
||
|
w.win.urgency_hint = false
|
||
|
end)
|
||
|
end,
|
||
|
}
|
||
|
|
||
|
-- Helper functions which operate on the window widgets or structure.
|
||
|
window.methods = {
|
||
|
-- Wrapper around the bind plugin's hit method
|
||
|
hit = function (w, mods, key, opts)
|
||
|
local opts = lousy.util.table.join(opts or {}, {
|
||
|
enable_buffer = w:is_mode("normal"),
|
||
|
buffer = w.buffer,
|
||
|
})
|
||
|
|
||
|
local caught, newbuf = lousy.bind.hit(w, w.binds, mods, key, opts)
|
||
|
if w.win then -- Check binding didn't cause window to exit
|
||
|
w.buffer = newbuf
|
||
|
w:update_buf()
|
||
|
end
|
||
|
return caught
|
||
|
end,
|
||
|
|
||
|
-- Wrapper around the bind plugin's match_cmd method
|
||
|
match_cmd = function (w, buffer)
|
||
|
return lousy.bind.match_cmd(w, get_mode("command").binds, buffer)
|
||
|
end,
|
||
|
|
||
|
-- enter command or characters into command line
|
||
|
enter_cmd = function (w, cmd, opts)
|
||
|
w:set_mode("command")
|
||
|
w:set_input(cmd, opts)
|
||
|
end,
|
||
|
|
||
|
-- run command as if typed into the command line
|
||
|
run_cmd = function (w, cmd, opts)
|
||
|
w:enter_cmd(cmd, opts)
|
||
|
w:activate()
|
||
|
end,
|
||
|
|
||
|
-- insert a string into the command line at the current cursor position
|
||
|
insert_cmd = function (w, str)
|
||
|
if not str then return end
|
||
|
local i = w.ibar.input
|
||
|
local text = i.text
|
||
|
local pos = i.position
|
||
|
local left, right = string.sub(text, 1, pos), string.sub(text, pos+1)
|
||
|
i.text = left .. str .. right
|
||
|
i.position = pos + #str
|
||
|
end,
|
||
|
|
||
|
-- Emulates pressing the Return key in input field
|
||
|
activate = function (w)
|
||
|
w.ibar.input:emit_signal("activate")
|
||
|
end,
|
||
|
|
||
|
del_word = function (w)
|
||
|
local i = w.ibar.input
|
||
|
local text = i.text
|
||
|
local pos = i.position
|
||
|
if text and #text > 1 and pos > 1 then
|
||
|
local left, right = string.sub(text, 2, pos), string.sub(text, pos+1)
|
||
|
if not string.find(left, "%s") then
|
||
|
left = ""
|
||
|
elseif string.find(left, "%w+%s*$") then
|
||
|
left = string.sub(left, 0, string.find(left, "%w+%s*$") - 1)
|
||
|
elseif string.find(left, "%W+%s*$") then
|
||
|
left = string.sub(left, 0, string.find(left, "%W+%s*$") - 1)
|
||
|
end
|
||
|
i.text = string.sub(text, 1, 1) .. left .. right
|
||
|
i.position = #left + 1
|
||
|
end
|
||
|
end,
|
||
|
|
||
|
del_line = function (w)
|
||
|
local i = w.ibar.input
|
||
|
if not string.match(i.text, "^[:/?]$") then
|
||
|
i.text = string.sub(i.text, 1, 1)
|
||
|
i.position = -1
|
||
|
end
|
||
|
end,
|
||
|
|
||
|
del_backward_char = function (w)
|
||
|
local i = w.ibar.input
|
||
|
local text = i.text
|
||
|
local pos = i.position
|
||
|
|
||
|
if pos > 1 then
|
||
|
i.text = string.sub(text, 0, pos - 1) .. string.sub(text, pos + 1)
|
||
|
i.position = pos - 1
|
||
|
end
|
||
|
end,
|
||
|
|
||
|
del_forward_char = function (w)
|
||
|
local i = w.ibar.input
|
||
|
local text = i.text
|
||
|
local pos = i.position
|
||
|
|
||
|
i.text = string.sub(text, 0, pos) .. string.sub(text, pos + 2)
|
||
|
i.position = pos
|
||
|
end,
|
||
|
|
||
|
beg_line = function (w)
|
||
|
local i = w.ibar.input
|
||
|
i.position = 1
|
||
|
end,
|
||
|
|
||
|
end_line = function (w)
|
||
|
local i = w.ibar.input
|
||
|
i.position = -1
|
||
|
end,
|
||
|
|
||
|
forward_char = function (w)
|
||
|
local i = w.ibar.input
|
||
|
i.position = i.position + 1
|
||
|
end,
|
||
|
|
||
|
backward_char = function (w)
|
||
|
local i = w.ibar.input
|
||
|
local pos = i.position
|
||
|
if pos > 1 then
|
||
|
i.position = pos - 1
|
||
|
end
|
||
|
end,
|
||
|
|
||
|
forward_word = function (w)
|
||
|
local i = w.ibar.input
|
||
|
local text = i.text
|
||
|
local pos = i.position
|
||
|
if text and #text > 1 then
|
||
|
local right = string.sub(text, pos+1)
|
||
|
if string.find(right, "%w+") then
|
||
|
local _, move = string.find(right, "%w+")
|
||
|
i.position = pos + move
|
||
|
end
|
||
|
end
|
||
|
end,
|
||
|
|
||
|
backward_word = function (w)
|
||
|
local i = w.ibar.input
|
||
|
local text = i.text
|
||
|
local pos = i.position
|
||
|
if text and #text > 1 and pos > 1 then
|
||
|
local left = string.reverse(string.sub(text, 2, pos))
|
||
|
if string.find(left, "%w+") then
|
||
|
local _, move = string.find(left, "%w+")
|
||
|
i.position = pos - move
|
||
|
end
|
||
|
end
|
||
|
end,
|
||
|
|
||
|
-- Shows a notification until the next keypress of the user.
|
||
|
notify = function (w, msg, set_mode)
|
||
|
if set_mode ~= false then w:set_mode() end
|
||
|
w:set_prompt(msg, { fg = theme.notif_fg, bg = theme.notif_bg })
|
||
|
end,
|
||
|
|
||
|
warning = function (w, msg, set_mode)
|
||
|
if set_mode ~= false then w:set_mode() end
|
||
|
w:set_prompt(msg, { fg = theme.warning_fg, bg = theme.warning_bg })
|
||
|
end,
|
||
|
|
||
|
error = function (w, msg, set_mode)
|
||
|
if set_mode ~= false then w:set_mode() end
|
||
|
w:set_prompt("Error: "..msg, { fg = theme.error_fg, bg = theme.error_bg })
|
||
|
end,
|
||
|
|
||
|
-- Set and display the prompt
|
||
|
set_prompt = function (w, text, opts)
|
||
|
local prompt, ebox, opts = w.ibar.prompt, w.ibar.ebox, opts or {}
|
||
|
prompt:hide()
|
||
|
-- Set theme
|
||
|
fg, bg = opts.fg or theme.ibar_fg, opts.bg or theme.ibar_bg
|
||
|
if prompt.fg ~= fg then prompt.fg = fg end
|
||
|
if ebox.bg ~= bg then ebox.bg = bg end
|
||
|
-- Set text or remain hidden
|
||
|
if text then
|
||
|
prompt.text = lousy.util.escape(text)
|
||
|
prompt:show()
|
||
|
end
|
||
|
end,
|
||
|
|
||
|
-- Set display and focus the input bar
|
||
|
set_input = function (w, text, opts)
|
||
|
local input, opts = w.ibar.input, opts or {}
|
||
|
input:hide()
|
||
|
-- Set theme
|
||
|
fg, bg = opts.fg or theme.ibar_fg, opts.bg or theme.ibar_bg
|
||
|
if input.fg ~= fg then input.fg = fg end
|
||
|
if input.bg ~= bg then input.bg = bg end
|
||
|
-- Set text or remain hidden
|
||
|
if text then
|
||
|
input.text = ""
|
||
|
input:show()
|
||
|
input:focus()
|
||
|
input.text = text
|
||
|
input.position = opts.pos or -1
|
||
|
end
|
||
|
end,
|
||
|
|
||
|
-- GUI content update functions
|
||
|
update_tab_count = function (w)
|
||
|
w.sbar.r.tabi.text = string.format("[%d/%d]", w.tabs:current(), w.tabs:count())
|
||
|
end,
|
||
|
|
||
|
update_win_title = function (w)
|
||
|
local uri, title = w.view.uri, w.view.title
|
||
|
title = (title or "luakit") .. ((uri and " - " .. uri) or "")
|
||
|
local max = globals.max_title_len or 80
|
||
|
if #title > max then title = string.sub(title, 1, max) .. "..." end
|
||
|
w.win.title = title
|
||
|
end,
|
||
|
|
||
|
update_uri = function (w, link)
|
||
|
w.sbar.l.uri.text = lousy.util.escape((link and "Link: " .. link)
|
||
|
or (w.view and w.view.uri) or "about:blank")
|
||
|
end,
|
||
|
|
||
|
update_progress = function (w)
|
||
|
local p = w.view.progress
|
||
|
local loaded = w.sbar.l.loaded
|
||
|
if not w.view:loading() or p == 1 then
|
||
|
loaded:hide()
|
||
|
else
|
||
|
loaded:show()
|
||
|
loaded.text = string.format("(%d%%)", p * 100)
|
||
|
end
|
||
|
end,
|
||
|
|
||
|
update_scroll = function (w)
|
||
|
local scroll, label = w.view.scroll, w.sbar.r.scroll
|
||
|
local y, max, text = scroll.y, scroll.ymax
|
||
|
if max == 0 then text = "All"
|
||
|
elseif y == 0 then text = "Top"
|
||
|
elseif y == max then text = "Bot"
|
||
|
else text = string.format("%2d%%", (y / max) * 100)
|
||
|
end
|
||
|
if label.text ~= text then label.text = text end
|
||
|
end,
|
||
|
|
||
|
update_ssl = function (w)
|
||
|
local trusted = w.view:ssl_trusted()
|
||
|
local ssl = w.sbar.r.ssl
|
||
|
if trusted ~= nil and not w.checking_ssl then
|
||
|
ssl.fg = theme.notrust_fg
|
||
|
ssl.text = "(nocheck)"
|
||
|
ssl:show()
|
||
|
elseif trusted == true then
|
||
|
ssl.fg = theme.trust_fg
|
||
|
ssl.text = "(trust)"
|
||
|
ssl:show()
|
||
|
elseif trusted == false then
|
||
|
ssl.fg = theme.notrust_fg
|
||
|
ssl.text = "(notrust)"
|
||
|
ssl:show()
|
||
|
else
|
||
|
ssl:hide()
|
||
|
end
|
||
|
end,
|
||
|
|
||
|
update_hist = function (w)
|
||
|
local hist = w.sbar.l.hist
|
||
|
local back, forward = w.view:can_go_back(), w.view:can_go_forward()
|
||
|
local s = (back and "+" or "") .. (forward and "-" or "")
|
||
|
if s ~= "" then
|
||
|
hist.text = '['..s..']'
|
||
|
hist:show()
|
||
|
else
|
||
|
hist:hide()
|
||
|
end
|
||
|
end,
|
||
|
|
||
|
update_buf = function (w)
|
||
|
local buf = w.sbar.r.buf
|
||
|
if w.buffer then
|
||
|
buf.text = lousy.util.escape(string.format(" %-3s", w.buffer))
|
||
|
buf:show()
|
||
|
else
|
||
|
buf:hide()
|
||
|
end
|
||
|
end,
|
||
|
|
||
|
update_binds = function (w, mode)
|
||
|
-- Generate the list of active key & buffer binds for this mode
|
||
|
w.binds = lousy.util.table.join((get_mode(mode) or {}).binds or {}, get_mode('all').binds or {})
|
||
|
-- Clear & hide buffer
|
||
|
w.buffer = nil
|
||
|
w:update_buf()
|
||
|
end,
|
||
|
|
||
|
update_tablist = function (w)
|
||
|
local current = w.tabs:current()
|
||
|
local fg, bg, nfg, snfg = theme.tab_fg, theme.tab_bg, theme.tab_ntheme, theme.selected_ntheme
|
||
|
local lfg, bfg, gfg = theme.tab_loading_fg, theme.tab_notrust_fg, theme.tab_trust_fg
|
||
|
local escape = lousy.util.escape
|
||
|
local tabs, tfmt = {}, ' <span foreground="%s">%s</span> %s'
|
||
|
|
||
|
for i, view in ipairs(w.tabs.children) do
|
||
|
-- Get tab number theme
|
||
|
local ntheme = nfg
|
||
|
if view:loading() then -- Show loading on all tabs
|
||
|
ntheme = lfg
|
||
|
elseif current == i then -- Show ssl trusted/untrusted on current tab
|
||
|
local trusted = view:ssl_trusted()
|
||
|
ntheme = snfg
|
||
|
if trusted == false or (trusted ~= nil and not w.checking_ssl) then
|
||
|
ntheme = bfg
|
||
|
elseif trusted then
|
||
|
ntheme = gfg
|
||
|
end
|
||
|
end
|
||
|
local title = view.title or view.uri or "(Untitled)"
|
||
|
tabs[i] = {
|
||
|
title = string.format(tfmt, ntheme or fg, i, escape(title)),
|
||
|
fg = (current == i and theme.tab_selected_fg) or fg,
|
||
|
bg = (current == i and theme.tab_selected_bg) or bg,
|
||
|
}
|
||
|
end
|
||
|
|
||
|
if #tabs < 2 then tabs, current = {}, 0 end
|
||
|
w.tablist:update(tabs, current)
|
||
|
end,
|
||
|
|
||
|
new_tab = function (w, arg, switch, order)
|
||
|
local view
|
||
|
-- Use blank tab first
|
||
|
if w.has_blank and w.tabs:count() == 1 and w.tabs[1].uri == "about:blank" then
|
||
|
view = w.tabs[1]
|
||
|
end
|
||
|
w.has_blank = nil
|
||
|
-- Make new webview widget
|
||
|
if not view then
|
||
|
view = webview.new(w)
|
||
|
-- Get tab order function
|
||
|
if not order and taborder then
|
||
|
order = (switch == false and taborder.default_bg)
|
||
|
or taborder.default
|
||
|
end
|
||
|
pos = w.tabs:insert((order and order(w, view)) or -1, view)
|
||
|
if switch ~= false then w.tabs:switch(pos) end
|
||
|
end
|
||
|
-- Load uri or webview history table
|
||
|
if type(arg) == "string" then view.uri = arg
|
||
|
elseif type(arg) == "table" then view.history = arg end
|
||
|
-- Update statusbar widgets
|
||
|
w:update_tab_count()
|
||
|
w:update_tablist()
|
||
|
return view
|
||
|
end,
|
||
|
|
||
|
-- close the current tab
|
||
|
close_tab = function (w, view, blank_last)
|
||
|
view = view or w.view
|
||
|
-- Treat a blank last tab as an empty notebook (if blank_last=true)
|
||
|
if blank_last ~= false and w.tabs:count() == 1 then
|
||
|
if not view:loading() and view.uri == "about:blank" then return end
|
||
|
w:new_tab("about:blank", false)
|
||
|
w.has_blank = true
|
||
|
end
|
||
|
-- Save tab history
|
||
|
local tab = {hist = view.history,}
|
||
|
-- And relative location
|
||
|
local index = w.tabs:indexof(view)
|
||
|
if index ~= 1 then tab.after = w.tabs[index-1] end
|
||
|
table.insert(w.closed_tabs, tab)
|
||
|
view:destroy()
|
||
|
w:update_tab_count()
|
||
|
w:update_tablist()
|
||
|
end,
|
||
|
|
||
|
close_win = function (w, force)
|
||
|
-- Ask plugins if it's OK to close last window
|
||
|
if not force and (#luakit.windows == 1) then
|
||
|
local emsg = luakit.emit_signal("can-close", w)
|
||
|
if emsg then
|
||
|
assert(type(emsg) == "string", "invalid exit error message")
|
||
|
w:error(string.format("Can't close luakit: %s (force close "
|
||
|
.. "with :q! or :wq!)", emsg))
|
||
|
return false
|
||
|
end
|
||
|
end
|
||
|
|
||
|
w:emit_signal("close")
|
||
|
|
||
|
-- Close all tabs
|
||
|
while w.tabs:count() ~= 0 do
|
||
|
w:close_tab(nil, false)
|
||
|
end
|
||
|
|
||
|
-- Destroy tablist
|
||
|
w.tablist:destroy()
|
||
|
|
||
|
-- Remove from window index
|
||
|
window.bywidget[w.win] = nil
|
||
|
|
||
|
-- Clear window struct
|
||
|
w = setmetatable(w, {})
|
||
|
|
||
|
-- Recursively remove widgets from window
|
||
|
local children = lousy.util.recursive_remove(w.win)
|
||
|
-- Destroy all widgets
|
||
|
for i, c in ipairs(lousy.util.table.join(children, {w.win})) do
|
||
|
if c.hide then c:hide() end
|
||
|
c:destroy()
|
||
|
end
|
||
|
|
||
|
-- Remove all window table vars
|
||
|
for k, _ in pairs(w) do w[k] = nil end
|
||
|
|
||
|
-- Quit if closed last window
|
||
|
if #luakit.windows == 0 then luakit.quit() end
|
||
|
end,
|
||
|
|
||
|
-- Navigate current view or open new tab
|
||
|
navigate = function (w, uri, view)
|
||
|
view = view or w.view
|
||
|
if view then
|
||
|
local js = string.match(uri, "^javascript:(.+)$")
|
||
|
if js then
|
||
|
return view:eval_js(luakit.uri_decode(js))
|
||
|
end
|
||
|
view.uri = uri
|
||
|
else
|
||
|
return w:new_tab(uri)
|
||
|
end
|
||
|
end,
|
||
|
|
||
|
-- Save, restart luakit and reload session.
|
||
|
restart = function (w)
|
||
|
-- Generate luakit launch command.
|
||
|
local args = {({string.gsub(luakit.execpath, " ", "\\ ")})[1]}
|
||
|
if luakit.verbose then table.insert(args, "-v") end
|
||
|
-- Relaunch without libunique bindings?
|
||
|
if luakit.nounique then table.insert(args, "-U") end
|
||
|
|
||
|
-- Get new config path
|
||
|
local conf
|
||
|
if luakit.confpath ~= "/etc/xdg/luakit/rc.lua" and os.exists(luakit.confpath) then
|
||
|
conf = luakit.confpath
|
||
|
table.insert(args, string.format("-c %q", conf))
|
||
|
end
|
||
|
|
||
|
-- Check config has valid syntax
|
||
|
local cmd = table.concat(args, " ")
|
||
|
if luakit.spawn_sync(cmd .. " -k") ~= 0 then
|
||
|
return w:error("Cannot restart, syntax error in configuration file"..((conf and ": "..conf) or "."))
|
||
|
end
|
||
|
|
||
|
-- Save session.
|
||
|
local wins = {}
|
||
|
for _, w in pairs(window.bywidget) do table.insert(wins, w) end
|
||
|
session.save(wins)
|
||
|
|
||
|
-- Replace current process with new luakit instance.
|
||
|
luakit.exec(cmd)
|
||
|
end,
|
||
|
|
||
|
-- Intelligent open command which can detect a uri or search argument.
|
||
|
search_open = function (w, arg)
|
||
|
local lstring = lousy.util.string
|
||
|
local match, find = string.match, string.find
|
||
|
|
||
|
-- Detect blank uris
|
||
|
if not arg or match(arg, "^%s*$") then return "about:blank" end
|
||
|
|
||
|
-- Strip whitespace and split by whitespace into args table
|
||
|
local args = lstring.split(lstring.strip(arg))
|
||
|
|
||
|
-- Guess if first argument is an address, search engine, file
|
||
|
if #args == 1 then
|
||
|
local uri = args[1]
|
||
|
if uri == "about:blank" then return uri end
|
||
|
|
||
|
-- Check if search engine name
|
||
|
if search_engines[uri] then
|
||
|
return string.format(search_engines[uri], "")
|
||
|
end
|
||
|
|
||
|
-- Navigate if . or / in uri (I.e. domains, IP's, scheme://)
|
||
|
if find(uri, "%.") or find(uri, "/") then return uri end
|
||
|
|
||
|
-- Navigate if this is a javascript-uri
|
||
|
if find(uri, "^javascript:") then return uri end
|
||
|
|
||
|
-- Valid hostnames to check
|
||
|
local hosts = { "localhost" }
|
||
|
if globals.load_etc_hosts ~= false then
|
||
|
hosts = lousy.util.get_etc_hosts()
|
||
|
end
|
||
|
|
||
|
-- Check hostnames
|
||
|
for _, h in pairs(hosts) do
|
||
|
if h == uri or match(uri, "^"..h..":%d+$") then return uri end
|
||
|
end
|
||
|
|
||
|
-- Check for file in filesystem
|
||
|
if globals.check_filepath ~= false then
|
||
|
if lfs.attributes(uri) then return "file://" .. uri end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- Find search engine (or use search_engines.default)
|
||
|
local engine = "default"
|
||
|
if args[1] and search_engines[args[1]] then
|
||
|
engine = args[1]
|
||
|
table.remove(args, 1)
|
||
|
end
|
||
|
|
||
|
-- URI encode search terms
|
||
|
local terms = luakit.uri_encode(table.concat(args, " "))
|
||
|
return string.format(search_engines[engine], terms)
|
||
|
end,
|
||
|
|
||
|
-- Increase (or decrease) the last found number in the current uri
|
||
|
inc_uri = function (w, arg)
|
||
|
local uri = string.gsub(w.view.uri, "(%d+)([^0-9]*)$", function (num, rest)
|
||
|
return string.format("%0"..#num.."d", tonumber(num) + (arg or 1)) .. rest
|
||
|
end)
|
||
|
return uri
|
||
|
end,
|
||
|
|
||
|
-- Tab traversing functions
|
||
|
next_tab = function (w, n)
|
||
|
w.tabs:switch((((n or 1) + w.tabs:current() -1) % w.tabs:count()) + 1)
|
||
|
end,
|
||
|
|
||
|
prev_tab = function (w, n)
|
||
|
w.tabs:switch(((w.tabs:current() - (n or 1) -1) % w.tabs:count()) + 1)
|
||
|
end,
|
||
|
|
||
|
goto_tab = function (w, n)
|
||
|
if n and (n == -1 or n > 0) then
|
||
|
return w.tabs:switch((n <= w.tabs:count() and n) or -1)
|
||
|
end
|
||
|
end,
|
||
|
|
||
|
-- For each tab, switches to that tab and calls the given function passing
|
||
|
-- it the view contained in the tab.
|
||
|
each_tab = function (w, fn)
|
||
|
for index = 1, w.tabs:count() do
|
||
|
w:goto_tab(index)
|
||
|
fn(w.tabs[index])
|
||
|
end
|
||
|
end,
|
||
|
|
||
|
-- If argument is form-active or root-active, emits signal. Ignores all
|
||
|
-- other signals.
|
||
|
emit_form_root_active_signal = function (w, s)
|
||
|
if s == "form-active" then
|
||
|
w.view:emit_signal("form-active")
|
||
|
elseif s == "root-active" then
|
||
|
w.view:emit_signal("root-active")
|
||
|
end
|
||
|
end,
|
||
|
}
|
||
|
|
||
|
-- Ordered list of class index functions. Other classes (E.g. webview) are able
|
||
|
-- to add their own index functions to this list.
|
||
|
window.indexes = {
|
||
|
-- Find function in window.methods first
|
||
|
function (w, k) return window.methods[k] end
|
||
|
}
|
||
|
|
||
|
-- Create new window
|
||
|
function window.new(uris)
|
||
|
local w = window.build()
|
||
|
|
||
|
-- Set window metatable
|
||
|
setmetatable(w, {
|
||
|
__index = function (_, k)
|
||
|
-- Check widget structure first
|
||
|
local v = rawget(w, k)
|
||
|
if v then return v end
|
||
|
-- Call each window index function
|
||
|
for _, index in ipairs(window.indexes) do
|
||
|
v = index(w, k)
|
||
|
if v then return v end
|
||
|
end
|
||
|
end,
|
||
|
})
|
||
|
|
||
|
-- Setup window widget for signals
|
||
|
lousy.signal.setup(w)
|
||
|
|
||
|
-- Call window init functions
|
||
|
for _, func in pairs(window.init_funcs) do
|
||
|
func(w)
|
||
|
end
|
||
|
|
||
|
-- Populate notebook with tabs
|
||
|
for _, uri in ipairs(uris or {}) do
|
||
|
w:new_tab(w:search_open(uri), false)
|
||
|
end
|
||
|
|
||
|
-- Make sure something is loaded
|
||
|
if w.tabs:count() == 0 then
|
||
|
w:new_tab(w:search_open(globals.homepage), false)
|
||
|
end
|
||
|
|
||
|
-- Set initial mode
|
||
|
w:set_mode()
|
||
|
|
||
|
-- Show window
|
||
|
w.win:show()
|
||
|
|
||
|
return w
|
||
|
end
|
||
|
|
||
|
-- vim: et:sw=4:ts=8:sts=4:tw=80
|