--[[ groups.lua - script providing session-like functionality for K-Meleon Copyright (c) 2005-2006 rmn 20051113-20060720 This script is distributed under the GNU General Public License version 2 or later. If you obtained this file through a K-Meleon package, there should be a copy of the license in a file called "License.txt" in the root directory of K-Meleon which contains, among others, the GNU GPL text. Otherwise, you can view the license from http://www.gnu.org/licenses/gpl.html . ]] --[[ Search the file for `function groups.` for functions callable from K-Meleon. ]] require 'kmplus' require 'hook' groups = {} local g = { -- namespace for private members pref = { NAMES = 'kmeleon.plugins.luamacros.groups.names', PREFIX = 'kmeleon.plugins.luamacros.groups.groups.', }, SUBMENU = { {'Load', 'loadaddbyname'}, {'List URLs', 'listurlsbyname'}, nil, {'Add this tab', 'saveaddthisbyname'}, {'Add all tabs', 'saveaddallbyname'}, nil, {'Replace', 'savereplacebyname'}, {'Remove', 'removebyname'}, }, -- Whether to to support the experimental tabs implementation -- using some truly horrible hack. USE_TABS = true, } -------------------- -- Menu functions -- -------------------- function g.insertgroupsubmenuitem(hmenu, gname, pos, label, func) if pos < 0 then pos = hmenu:GetMenuItemCount() + 1 + pos end hmenu:InsertMenu(pos, w32mnu.MF_BYPOSITION + w32mnu.MF_STRING, km.GetID('luamacros(groups.'..func..'('..gname..'))'), label) end function g.buildmenu(hmenu) local count = hmenu:GetMenuItemCount() if g.groupmenus then for k, v in pairs(g.groupmenus) do count = count - 1 v:DestroyMenu() hmenu:DeleteMenu(count, w32mnu.MF_BYPOSITION) end end g.groupmenus = {} local pos = count table.foreachi(g.split(g.pref.names()), function(i, v) local popup = w32mnu.CreatePopupMenu() table.foreachi(g.SUBMENU, function(i2, v2) if v2 then g.insertgroupsubmenuitem(popup, v, i2 - 1, v2[1], v2[2]) else popup:InsertMenu(i2, w32mnu.MF_BYPOSITION + w32mnu.MF_SEPARATOR, 0, "") end end) hmenu:InsertMenu(pos, w32mnu.MF_BYPOSITION + w32mnu.MF_STRING + w32mnu.MF_POPUP, popup, v) table.insert(g.groupmenus, popup) pos = pos + 1 end) end if w32mnu and hook then hook.add(SetupHook, function () g.hmenu = w32mnu.AttachMenu(km.GetMenu('&Groups')) if g.hmenu then g.buildmenu(g.hmenu) end end) end --------------------------------- -- Interface to K-Meleon prefs -- --------------------------------- function g.pref.names(...) --[[ Group names accessor/mutator Does not check for duplicates. Can only add & remove one at a time. Parameters: [ [mode,] value] - mode: ('replace'|'add'|'remove') default: replace ]] if #arg >= 1 then local names if #arg >= 2 then names = arg[2] if arg[1] == 'replace' then elseif arg[1] == 'add' then names = g.pref.names()..names..'\t' elseif arg[1] == 'remove' then names = string.gsub(g.pref.names(), names..'\t', '') else return end else names = arg[1] -- replace end setpref(TYPE_STRING, g.pref.NAMES, names) if w32mnu then g.updatemenu() end else return getpref(TYPE_STRING, g.pref.NAMES) end end function g.pref.group(name, ...) --[[ Group URLs accessor/mutator Does not add name to list of names if it's not there yet. Requires "url url " format. Parameters: name, [ [mode,] value] - mode: ('replace'|'add') default: replace ]] if #arg >= 1 then local urls if #arg >= 2 then urls = arg[2] if arg[1] == 'replace' then elseif arg[1] == 'add' then urls = g.pref.group(name)..urls else return end else urls = arg[1] -- replace end setpref(TYPE_STRING, g.pref.PREFIX..name, urls) else return getpref(TYPE_STRING, g.pref.PREFIX..name) end end function g.pref.innames(name) --[[ Return whether `name` is in the list of group names. ]] local names = g.pref.names() return string.find(names, '\t'..name..'\t') or string.find(names, '^' ..name..'\t') end function g.pref.sort(...) --[[ Sort group names or URLs. Does not check if name exists. Parameters: [name] - name: group to be sorted; default: sort group names ]] local items if #arg >= 1 then items = g.pref.group(arg[1]) else items = g.pref.names() end local t = g.split(items, '\t') table.sort(t) g.pref.names(table.concat(t, '\t')..'\t') end function groups.sortnames(param) g.pref.sort() end function groups.sorturls(param) local names = g.split(g.pref.names()) local idx = choose('', 'Sort group items', unpack(names)) if idx then g.sort(names[idx]) end end -- TODO --~ function g.rename(old, new) --~ g.pref.group(new, g.pref.group(old))) --~ g.pref.group(old, '') --~ g.pref.names('rename', old, new) --~ end -- TODO --~ function groups.rename(param) --~ end --------------------------------- -- Interface to layers or tabs -- --------------------------------- function g.getwindowurls_l() --[[ Return a list of the URLs in this window's layers. The returned string is in the form of "url url ". ]] return pluginmsgex('layers', 'GetLayersInWindow', TYPE_STRING) end function g.getwindowurls_t() --[[ Return a list of the URLs in this window's tabs. For use with the native tabs support. The returned string is in the form of "url url ". Uses a simple heuristics to detect whether the loop has reached the initial tab. It compares last and current URLs with the first two URLs. Note that this is only a crude workaround until a proper tab manipulation interface is available. ]] local t = {} local url url = km.GetGlobalVar(TYPE_STRING, 'URL') table.insert(t, url) km.id('ID_TAB_NEXT') url = km.GetGlobalVar(TYPE_STRING, 'URL') table.insert(t, url) repeat km.id('ID_TAB_NEXT') url = km.GetGlobalVar(TYPE_STRING, 'URL') table.insert(t, url) until t[#t-1] == t[1] and t[#t] == t[2] km.id('ID_TAB_PREV') table.remove(t, #t) table.remove(t, #t) return table.concat(t, '\t')..'\t' end function g.replacewindowurls_l(urls) pluginmsg('layers', 'ReplaceLayersInWindow', urls) end function g.replacewindowurls_t(urls) -- Only works with catchClose. -- Heuristic: wait for two "about:blanks"s in a row. local n = 0 repeat local url = km.GetGlobalVar(TYPE_STRING, 'URL') while url ~= 'about:blank' do km.id('ID_TAB_CLOSE') end n = n + 1 until n >= 2 end function g.addurlstowindow_l(urls) pluginmsg('layers', 'AddLayersToWindow', urls) -- FIXME: not working! end function g.addurlstowindow_t(urls) local t = g.split(urls, '\t') table.foreachi(t, function(i, v) NavigateTo(v, OPEN_BACKGROUND) -- Only works correctly with catchOpen. end) end if g.USE_TABS then g.getwindowurls = g.getwindowurls_t g.replacewindowurls = g.replacewindowurls_t g.addurlstowindow = g.addurlstowindow_t else g.getwindowurls = g.getwindowurls_l g.replacewindowurls = g.replacewindowurls_l g.addurlstowindow = g.addurlstowindow_l end -------------------- -- Main functions -- -------------------- function g.save(name, mode) --[[ Save tab(s) to a group. Adds name to list of names if it's not there yet. Parameters: - name: group name - mode: ('replace'|'addall'|'addthis') or do nothing ]] if not g.pref.innames(name) then g.pref.names('add', name) end local prefval if mode == 'replace' then prefval = g.getwindowurls() elseif mode == 'addall' then local tabs = g.getwindowurls() prefval = g.pref.group(name)..tabs elseif mode == 'addthis' then local tab = km.GetGlobalVar(TYPE_STRING, 'URL') prefval = g.pref.group(name)..tab..'\t' end if prefval then g.pref.group(name, prefval) end end function groups.save(param) --[[ Prompt for group name and save it. param: mode ]] local name, overwrite local names = g.names() repeat if names then name = prompt('Existing groups: '..names, 'Save group') else name = prompt('Group name', 'Save group') end if not name then break end if not g.pref.innames(name) then break end overwrite = confirm('Group "'..name..'" exists. Overwrite?', 'Save group', BUTTONS_YESNOCANCEL) until (overwrite ~= IDNO) if name and (overwrite ~= IDCANCEL) then if param == '' then param = 'replace' end g.save(name, param) end end function groups.save_choose(param) --[[ Prompt for EXISTING group name and save it. param: mode ]] local snames = g.pref.names() if snames then local names = g.split(snames, '\t') local idx = popupmenu('«new»', unpack(names)) - 1 if idx == 0 then groups.save(param) -- new group elseif idx ~= -1 then if param == '' then param = 'replace' end g.save(names[idx], param) end else groups.save(param) -- fallback when there are no existing groups end end function groups.savereplacebyname(param) --[[ TODO: Document! ]] g.save(param, 'replace') end function groups.saveaddthisbyname(param) --[[ TODO: Document! ]] g.save(param, 'addthis') end function groups.saveaddallbynamebyname(param) --[[ TODO: Document! ]] g.save(param, 'addall') end function g.load(name, mode) --[[ Load tabs from a group. Does not check if group exists. Parameters: - name: group name - mode: ('replace'|'add') or do nothing ]] local param if mode == 'replace' then g.replacewindowurls(g.pref.group(name)) elseif mode == 'add' then g.addurlstowindow(g.pref.group(name)) end end function groups.load(param) --[[ Prompt for group name and load it. param: mode ]] local names = g.split(g.pref.names(), '\t') if #names == 0 then alert('No groups.', 'Groups', ICON_EXCLAIM) else local idx = popupmenu(unpack(names)) if idx ~= 0 then if param == '' then param = 'replace' end g.load(names[idx], param) end end end function groups.loadaddbyname(param) --[[ Load a named group, adding tabs to current window. param: group name ]] g.load(param, 'add') end function g.remove(name) --[[ Remove group. Does not check if group exists. ]] g.pref.names('remove', name) g.pref.group(name, '') end function groups.remove(...) --[[ Prompt for group name and remove it. ]] local names = g.split(g.pref.names(), '\t') local idx = popupmenu(unpack(names)) if #names == 0 then alert('No groups.', 'Groups', ICON_EXCLAIM) else if idx ~= 0 then g.remove(names[idx]) end end end function groups.removebyname(param) --[[ TODO: Document! ]] g.remove(param) end function g.names() --[[ Return a comma-separated list of group names, or nil. ]] local s = g.pref.names() if s == '' then return nil else s = string.gsub(s, '\t$', '') s = string.gsub(s, '\t', ', ') return s end end function g.list(...) --[[ List group names or URLs. Does not check if name exists. Parameters: [name] - name: group to be displayed; default: display group names ]] if #arg >= 1 then alert(string.gsub(g.pref.group(arg[1]), '\t', '\r\n'), 'Group "'..arg[1]..'"', ICON_INFO) else local names = g.pref.names() if names == '' then alert('No groups.', 'Groups', ICON_EXCLAIM) else alert(string.gsub(names, '(.-)\t', function(name) local count = 0 for t in string.gfind(g.pref.group(name), '\t') do count = count + 1 end if count == 1 then return name..' — '..count..' item\r\n' else return name..' — '..count..' items\r\n' end end), 'Groups', ICON_INFO) end end end function groups.list(...) --[[ List groups. ]] g.list() end function groups.listurls(...) --[[ Prompt for group name and list URLs. ]] local names = g.split(g.pref.names()) if #names == 0 then alert('No groups.', 'Groups', ICON_EXCLAIM) else local idx = popupmenu(unpack(names)) if idx ~= 0 then g.list(names[idx]) end end end function groups.listurlsbyname(param) --[[ List URLS from a group. param: group name ]] g.list(param) end function g.split(s, ...) --[[ Split a string into a table. Requires a trailing separator. Parameters: string, [separator] - separator: default: '%s' (all space characters) ]] local sep = arg[1] or '%s' local t = {} local w for w in string.gfind(s, '(.-)'..sep) do table.insert(t, w) end return t end