237 lines
6.8 KiB
Lua
237 lines
6.8 KiB
Lua
--- === Seal.plugins.apps ===
|
|
---
|
|
--- A plugin to add launchable apps/scripts, making Seal act as a launch bar
|
|
local obj = {}
|
|
obj.__index = obj
|
|
obj.__name = "seal_apps"
|
|
obj.appCache = {}
|
|
|
|
--- Seal.plugins.apps.appSearchPaths
|
|
--- Variable
|
|
--- Table containing the paths to search for launchable items
|
|
---
|
|
--- Notes:
|
|
--- * If you change this, you will need to call `spoon.Seal.plugins.apps:restart()` to force Spotlight to search for new items.
|
|
obj.appSearchPaths = {
|
|
"/Applications",
|
|
"/System/Applications",
|
|
"~/Applications",
|
|
"/Developer/Applications",
|
|
"/Applications/Xcode.app/Contents/Applications",
|
|
"/System/Library/PreferencePanes",
|
|
"/Library/PreferencePanes",
|
|
"~/Library/PreferencePanes",
|
|
"/System/Library/CoreServices/Applications",
|
|
"/System/Library/CoreServices/",
|
|
"/usr/local/Cellar",
|
|
"/Library/Scripts",
|
|
"~/Library/Scripts"
|
|
}
|
|
|
|
local modifyNameMap = function(info, add)
|
|
for _, item in ipairs(info) do
|
|
icon = nil
|
|
local displayname = item.kMDItemDisplayName or hs.fs.displayName(item.kMDItemPath)
|
|
displayname = displayname:gsub("%.app$", "", 1)
|
|
if string.find(item.kMDItemPath, "%.prefPane$") then
|
|
displayname = displayname .. " preferences"
|
|
if add then
|
|
icon = hs.image.iconForFile(item.kMDItemPath)
|
|
end
|
|
end
|
|
if add then
|
|
bundleID = item.kMDItemCFBundleIdentifier
|
|
if (not icon) and (bundleID) then
|
|
icon = hs.image.imageFromAppBundle(bundleID)
|
|
end
|
|
obj.appCache[displayname] = {
|
|
path = item.kMDItemPath,
|
|
bundleID = bundleID,
|
|
icon = icon
|
|
}
|
|
else
|
|
obj.appCache[displayname] = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
local updateNameMap = function(obj, msg, info)
|
|
if info then
|
|
-- all three can occur in either message, so check them all!
|
|
if info.kMDQueryUpdateAddedItems then modifyNameMap(info.kMDQueryUpdateAddedItems, true) end
|
|
if info.kMDQueryUpdateChangedItems then modifyNameMap(info.kMDQueryUpdateChangedItems, true) end
|
|
if info.kMDQueryUpdateRemovedItems then modifyNameMap(info.kMDQueryUpdateRemovedItems, false) end
|
|
else
|
|
-- shouldn't happen for didUpdate or inProgress
|
|
print("~~~ userInfo from SpotLight was empty for " .. msg)
|
|
end
|
|
end
|
|
|
|
--- Seal.plugins.apps:start()
|
|
--- Method
|
|
--- Starts the Spotlight app searcher
|
|
---
|
|
--- Parameters:
|
|
--- * None
|
|
---
|
|
--- Returns:
|
|
--- * None
|
|
---
|
|
--- Notes:
|
|
--- * This is called automatically when the plugin is loaded
|
|
function obj:start()
|
|
obj.spotlight = hs.spotlight.new():queryString([[ (kMDItemContentType = "com.apple.application-bundle") || (kMDItemContentType = "com.apple.systempreference.prefpane") || (kMDItemContentType = "com.apple.applescript.text") || (kMDItemContentType = "com.apple.applescript.script") ]])
|
|
:callbackMessages("didUpdate", "inProgress")
|
|
:setCallback(updateNameMap)
|
|
:searchScopes(obj.appSearchPaths)
|
|
:start()
|
|
end
|
|
|
|
--- Seal.plugins.apps:stop()
|
|
--- Method
|
|
--- Stops the Spotlight app searcher
|
|
---
|
|
--- Parameters:
|
|
--- * None
|
|
---
|
|
--- Returns:
|
|
--- * None
|
|
function obj:stop()
|
|
obj.spotlight:stop()
|
|
obj.spotlight = nil
|
|
obj.appCache = {}
|
|
end
|
|
|
|
--- Seal.plugins.apps:restart()
|
|
--- Method
|
|
--- Restarts the Spotlight app searcher
|
|
---
|
|
--- Parameters:
|
|
--- * None
|
|
---
|
|
--- Returns:
|
|
--- * None
|
|
function obj:restart()
|
|
self:stop()
|
|
self:start()
|
|
end
|
|
|
|
hs.application.enableSpotlightForNameSearches(true)
|
|
obj:start()
|
|
|
|
function obj:commands()
|
|
return {kill = {
|
|
cmd = "kill",
|
|
fn = obj.choicesKillCommand,
|
|
plugin = obj.__name,
|
|
name = "Kill",
|
|
description = "Kill an application"
|
|
},
|
|
reveal = {
|
|
cmd = "reveal",
|
|
fn = obj.choicesRevealCommand,
|
|
plugin = obj.__name,
|
|
name = "Reveal",
|
|
description = "Reveal an application in the Finder"
|
|
}
|
|
}
|
|
end
|
|
|
|
function obj:bare()
|
|
return self.choicesApps
|
|
end
|
|
|
|
function obj.choicesApps(query)
|
|
local choices = {}
|
|
if query == nil or query == "" then
|
|
return choices
|
|
end
|
|
for name,app in pairs(obj.appCache) do
|
|
if string.match(name:lower(), query:lower()) then
|
|
local choice = {}
|
|
local instances = {}
|
|
if app["bundleID"] then
|
|
instances = hs.application.applicationsForBundleID(app["bundleID"])
|
|
end
|
|
if #instances > 0 then
|
|
choice["text"] = name .. " (Running)"
|
|
else
|
|
choice["text"] = name
|
|
end
|
|
choice["subText"] = app["path"]
|
|
if app["icon"] then
|
|
choice["image"] = app["icon"]
|
|
end
|
|
choice["path"] = app["path"]
|
|
choice["uuid"] = obj.__name.."__"..(app["bundleID"] or name)
|
|
choice["plugin"] = obj.__name
|
|
choice["type"] = "launchOrFocus"
|
|
table.insert(choices, choice)
|
|
end
|
|
end
|
|
return choices
|
|
end
|
|
|
|
function obj.choicesKillCommand(query)
|
|
local choices = {}
|
|
if query == nil then
|
|
return choices
|
|
end
|
|
local apps = hs.application.runningApplications()
|
|
for k, app in pairs(apps) do
|
|
local name = app:name()
|
|
if string.match(name:lower(), query:lower()) and app:mainWindow() then
|
|
local choice = {}
|
|
choice["text"] = "Kill "..name
|
|
choice["subText"] = app:path().." PID: "..app:pid()
|
|
choice["pid"] = app:pid()
|
|
choice["plugin"] = obj.__name
|
|
choice["type"] = "kill"
|
|
choice["image"] = hs.image.imageFromAppBundle(app:bundleID())
|
|
table.insert(choices, choice)
|
|
end
|
|
end
|
|
return choices
|
|
end
|
|
|
|
function obj.choicesRevealCommand(query)
|
|
local choices = {}
|
|
if query == nil then
|
|
return choices
|
|
end
|
|
local apps = obj.choicesApps(query)
|
|
for k, app in pairs(apps) do
|
|
local name = app.text
|
|
if string.match(name:lower(), query:lower()) then
|
|
local choice = {}
|
|
choice["text"] = "Reveal "..name
|
|
choice["path"] = app.path
|
|
choice["subText"] = app.path
|
|
choice["plugin"] = obj.__name
|
|
choice["type"] = "reveal"
|
|
if app.image then
|
|
choice["image"] = app.image
|
|
end
|
|
table.insert(choices, choice)
|
|
end
|
|
end
|
|
return choices
|
|
end
|
|
|
|
function obj.completionCallback(rowInfo)
|
|
if rowInfo["type"] == "launchOrFocus" then
|
|
if string.find(rowInfo["path"], "%.applescript$") or string.find(rowInfo["path"], "%.scpt$") then
|
|
hs.task.new("/usr/bin/osascript", nil, { rowInfo["path"] }):start()
|
|
else
|
|
hs.task.new("/usr/bin/open", nil, { rowInfo["path"] }):start()
|
|
end
|
|
elseif rowInfo["type"] == "kill" then
|
|
hs.application.get(rowInfo["pid"]):kill()
|
|
elseif rowInfo["type"] == "reveal" then
|
|
hs.osascript.applescript(string.format([[tell application "Finder" to reveal (POSIX file "%s")]], rowInfo["path"]))
|
|
hs.application.launchOrFocus("Finder")
|
|
end
|
|
end
|
|
|
|
return obj
|