<commit_before>--- === plugins.core.tangent.manager ===
---
--- Tangent Control Surface Manager
---
--- This plugin allows CommandPost to communicate with Tangent's range of
--- panels (Element, Virtual Element Apps, Wave, Ripple and any future panels).
---
--- Download the Tangent Developer Support Pack & Tangent Hub Installer for Mac
--- here: http://www.tangentwave.co.uk/developer-support/

--------------------------------------------------------------------------------
--
-- EXTENSIONS:
--
--------------------------------------------------------------------------------
local require = require

--------------------------------------------------------------------------------
-- Logger:
--------------------------------------------------------------------------------
local log                                       = require("hs.logger").new("tangentMan")

--------------------------------------------------------------------------------
-- Hammerspoon Extensions:
--------------------------------------------------------------------------------
local application                               = require("hs.application")
local fs                                        = require("hs.fs")
local inspect                                   = require("hs.inspect")
local tangent                                   = require("hs.tangent")
local timer                                     = require("hs.timer")

--------------------------------------------------------------------------------
-- CommandPost Extensions:
--------------------------------------------------------------------------------
local config                                    = require("cp.config")
local fcp                                       = require("cp.apple.finalcutpro")
local is                                        = require("cp.is")
local prop                                      = require("cp.prop")
local tools                                     = require("cp.tools")
local x                                         = require("cp.web.xml")

--------------------------------------------------------------------------------
-- Local Requires:
--------------------------------------------------------------------------------
local action                                    = require("action")
local controls                                  = require("controls")
local menu                                      = require("menu")
local mode                                      = require("mode")
local parameter                                 = require("parameter")

--------------------------------------------------------------------------------
-- Local Lua Functions:
--------------------------------------------------------------------------------
local insert, sort                              = table.insert, table.sort
local format                                    = string.format

--------------------------------------------------------------------------------
--
-- THE MODULE:
--
--------------------------------------------------------------------------------
local mod = {}

--- plugins.core.tangent.manager.TANGENT_MAPPER_BUNDLE_ID -> string
--- Constant
--- Tangent Mapper Bundle ID.
mod.TANGENT_MAPPER_BUNDLE_ID = "uk.co.tangentwave.tangentmapper"

--- plugins.core.tangent.manager.HIDE_FILE_PATH -> string
--- Constant
--- Tangent Mapper Hide File Path.
mod.HIDE_FILE_PATH = "/Library/Application Support/Tangent/Hub/KeypressApps/hide.txt"

--- plugins.core.tangent.manager.FCP_KEYPRESS_APPS_PATH -> string
--- Constant
--- Final Cut Pro Keypress Apps Path for Tangent Mapper.
mod.FCP_KEYPRESS_APPS_PATH = "/Library/Application Support/Tangent/Hub/KeypressApps/Final Cut Pro"

-- plugins.core.tangent.manager._modes -> table
-- Variable
-- Modes
mod._modes = {}

-- plugins.core.tangent.manager._connectionConfirmed -> boolean
-- Variable
-- Connection Confirmed.
mod._connectionConfirmed = false

--- plugins.core.tangent.manager.controls
--- Constant
--- The set of controls currently registered.
mod.controls = controls.new()

local controlsXML

function mod.getControlsXML()
    if not controlsXML then
        controlsXML = x._xml() .. x.TangentWave {fileType = "ControlSystem", fileVersion="3.0"} (
            --------------------------------------------------------------------------------
            -- Capabilities:
            --------------------------------------------------------------------------------
            x.Capabilities (
                x.Jog { enabled = true } ..
                x.Shuttle { enabled = false } ..
                x.StatusDisplay { lineCount = 3 }
            ) ..

            --------------------------------------------------------------------------------
            -- Default Global Settings:
            --------------------------------------------------------------------------------
            x.DefaultGlobalSettings (
                x.KnobSensitivity { std = 3, alt = 5 } ..
                x.JogDialSensitivity { std = 1, alt = 5 } ..
                x.TrackerballSensitivity { std = 1, alt = 5 } ..
                x.TrackerballDialSensitivity { std = 1, alt = 5 } ..
                x.IndependentPanelBanks { enabled = false }
            ) ..

            --------------------------------------------------------------------------------
            -- Modes:
            --------------------------------------------------------------------------------
            x.Modes (function()
                local modes = x()

                for _,m in ipairs(mod._modes) do
                    modes = modes .. m:xml()
                end

                return modes
            end) ..

            mod.controls:xml()

        )
    end
    return controlsXML
end

--- plugins.core.tangent.manager.writeControlsXML() -> boolean, string
--- Function
--- Writes the Tangent controls.xml File to the User's Application Support folder.
---
--- Parameters:
---  * None
---
--- Returns:
---  *  `true` if successfully created otherwise `false` if an error occurred.
---  *  If an error occurs an error message will also be returned as a string.
function mod.writeControlsXML()

    --------------------------------------------------------------------------------
    -- Create folder if it doesn't exist:
    --------------------------------------------------------------------------------
    if not tools.doesDirectoryExist(mod.configPath) then
        --log.df("Tangent Settings folder did not exist, so creating one.")
        fs.mkdir(mod.configPath)
    end

    --------------------------------------------------------------------------------
    -- Copy existing XML files from Application Bundle to local Application Support:
    --------------------------------------------------------------------------------
    local _, status = hs.execute(format("cp -a %q/. %q/", mod._pluginPath, mod.configPath))
    if not status then
        log.ef("Failed to copy XML files.")
        return false, "Failed to copy XML files."
    end

    --------------------------------------------------------------------------------
    -- Create "controls.xml" file:
    --------------------------------------------------------------------------------
    local controlsFile = io.open(mod.configPath .. "/controls.xml", "w")
    if controlsFile then
        --------------------------------------------------------------------------------
        -- Write to File & Close:
        --------------------------------------------------------------------------------
        io.output(controlsFile)
        io.write(tostring(mod.getControlsXML()))
        io.close(controlsFile)
    else
        log.ef("Failed to open controls.xml file in write mode")
        return false, "Failed to open controls.xml file in write mode"
    end
end

--- plugins.core.tangent.manager.updateControls() -> none
--- Function
--- Update Controls.
---
--- Parameters:
---  * None
---
--- Returns:
---  * None
function mod.updateControls()
    if mod.connected() then
        mod.connected(false)
    end
end

--- plugins.core.tangent.manager.addMode(id, name) -> plugins.core.tangent.manager.mode
--- Function
--- Adds a new `mode` with the specified details and returns it.
---
--- Parameters:
--- * id            - The id number of the Mode.
--- * name          - The name of the Mode.
---
--- Returns:
--- * The new `mode`
function mod.addMode(id, name)
    local m = mode.new(id, name, mod)
    insert(mod._modes, m)
    sort(mod._modes, function(a,b) return a.id < b.id end)
    return m
end

--- plugins.core.tangent.manager.getMode(id) -> plugins.core.tangent.manager.mode
--- Function
--- Returns the `mode` with the specified ID, or `nil`.
---
--- Parameters:
--- * id    - The ID to find.
---
--- Returns:
--- * The `mode`, or `nil`.
function mod.getMode(id)
    for _,m in ipairs(mod._modes) do
        if m.id == id then
            return m
        end
    end
    return nil
end

--- plugins.core.tangent.manager.activeModeID <cp.prop: string>
--- Field
--- The current active mode.
mod.activeModeID = config.prop("tangent.activeModeID")

--- plugins.core.tangent.manager.activeMode <cp.prop: mode>
--- Constant
--- Represents the currently active `mode`.
mod.activeMode = mod.activeModeID:mutate(
    function(original)
        local id = original()
        return id and mod.getMode(id)
    end,
    function(newMode, original)
        local m = mode.is(newMode) and newMode or mod.getMode(newMode)
        if m then
            local oldMode = mod._activeMode
            if oldMode and oldMode._deactivate then<commit_msg>#1499 * Documentation update<commit_after>--- === plugins.core.tangent.manager ===
---
--- Tangent Control Surface Manager
---
--- This plugin allows CommandPost to communicate with Tangent's range of
--- panels (Element, Virtual Element Apps, Wave, Ripple and any future panels).
---
--- Download the Tangent Developer Support Pack & Tangent Hub Installer for Mac
--- here: http://www.tangentwave.co.uk/developer-support/

--------------------------------------------------------------------------------
--
-- EXTENSIONS:
--
--------------------------------------------------------------------------------
local require = require

--------------------------------------------------------------------------------
-- Logger:
--------------------------------------------------------------------------------
local log                                       = require("hs.logger").new("tangentMan")

--------------------------------------------------------------------------------
-- Hammerspoon Extensions:
--------------------------------------------------------------------------------
local application                               = require("hs.application")
local fs                                        = require("hs.fs")
local inspect                                   = require("hs.inspect")
local tangent                                   = require("hs.tangent")
local timer                                     = require("hs.timer")

--------------------------------------------------------------------------------
-- CommandPost Extensions:
--------------------------------------------------------------------------------
local config                                    = require("cp.config")
local fcp                                       = require("cp.apple.finalcutpro")
local is                                        = require("cp.is")
local prop                                      = require("cp.prop")
local tools                                     = require("cp.tools")
local x                                         = require("cp.web.xml")

--------------------------------------------------------------------------------
-- Local Requires:
--------------------------------------------------------------------------------
local action                                    = require("action")
local controls                                  = require("controls")
local menu                                      = require("menu")
local mode                                      = require("mode")
local parameter                                 = require("parameter")

--------------------------------------------------------------------------------
-- Local Lua Functions:
--------------------------------------------------------------------------------
local insert, sort                              = table.insert, table.sort
local format                                    = string.format

--------------------------------------------------------------------------------
--
-- THE MODULE:
--
--------------------------------------------------------------------------------
local mod = {}

--- plugins.core.tangent.manager.TANGENT_MAPPER_BUNDLE_ID -> string
--- Constant
--- Tangent Mapper Bundle ID.
mod.TANGENT_MAPPER_BUNDLE_ID = "uk.co.tangentwave.tangentmapper"

--- plugins.core.tangent.manager.HIDE_FILE_PATH -> string
--- Constant
--- Tangent Mapper Hide File Path.
mod.HIDE_FILE_PATH = "/Library/Application Support/Tangent/Hub/KeypressApps/hide.txt"

--- plugins.core.tangent.manager.FCP_KEYPRESS_APPS_PATH -> string
--- Constant
--- Final Cut Pro Keypress Apps Path for Tangent Mapper.
mod.FCP_KEYPRESS_APPS_PATH = "/Library/Application Support/Tangent/Hub/KeypressApps/Final Cut Pro"

-- plugins.core.tangent.manager._modes -> table
-- Variable
-- Modes
mod._modes = {}

-- plugins.core.tangent.manager._connectionConfirmed -> boolean
-- Variable
-- Connection Confirmed.
mod._connectionConfirmed = false

--- plugins.core.tangent.manager.controls
--- Constant
--- The set of controls currently registered.
mod.controls = controls.new()

local controlsXML

function mod.getControlsXML()
    if not controlsXML then
        controlsXML = x._xml() .. x.TangentWave {fileType = "ControlSystem", fileVersion="3.0"} (
            --------------------------------------------------------------------------------
            -- Capabilities:
            --------------------------------------------------------------------------------
            x.Capabilities (
                x.Jog { enabled = true } ..
                x.Shuttle { enabled = false } ..
                x.StatusDisplay { lineCount = 3 }
            ) ..

            --------------------------------------------------------------------------------
            -- Default Global Settings:
            --------------------------------------------------------------------------------
            x.DefaultGlobalSettings (
                x.KnobSensitivity { std = 3, alt = 5 } ..
                x.JogDialSensitivity { std = 1, alt = 5 } ..
                x.TrackerballSensitivity { std = 1, alt = 5 } ..
                x.TrackerballDialSensitivity { std = 1, alt = 5 } ..
                x.IndependentPanelBanks { enabled = false }
            ) ..

            --------------------------------------------------------------------------------
            -- Modes:
            --------------------------------------------------------------------------------
            x.Modes (function()
                local modes = x()

                for _,m in ipairs(mod._modes) do
                    modes = modes .. m:xml()
                end

                return modes
            end) ..

            mod.controls:xml()

        )
    end
    return controlsXML
end

--- plugins.core.tangent.manager.writeControlsXML() -> boolean, string
--- Function
--- Writes the Tangent controls.xml File to the User's Application Support folder.
---
--- Parameters:
---  * None
---
--- Returns:
---  *  `true` if successfully created otherwise `false` if an error occurred.
---  *  If an error occurs an error message will also be returned as a string.
function mod.writeControlsXML()

    --------------------------------------------------------------------------------
    -- Create folder if it doesn't exist:
    --------------------------------------------------------------------------------
    if not tools.doesDirectoryExist(mod.configPath) then
        --log.df("Tangent Settings folder did not exist, so creating one.")
        fs.mkdir(mod.configPath)
    end

    --------------------------------------------------------------------------------
    -- Copy existing XML files from Application Bundle to local Application Support:
    --------------------------------------------------------------------------------
    local _, status = hs.execute(format("cp -a %q/. %q/", mod._pluginPath, mod.configPath))
    if not status then
        log.ef("Failed to copy XML files.")
        return false, "Failed to copy XML files."
    end

    --------------------------------------------------------------------------------
    -- Create "controls.xml" file:
    --------------------------------------------------------------------------------
    local controlsFile = io.open(mod.configPath .. "/controls.xml", "w")
    if controlsFile then
        --------------------------------------------------------------------------------
        -- Write to File & Close:
        --------------------------------------------------------------------------------
        io.output(controlsFile)
        io.write(tostring(mod.getControlsXML()))
        io.close(controlsFile)
    else
        log.ef("Failed to open controls.xml file in write mode")
        return false, "Failed to open controls.xml file in write mode"
    end
end

--- plugins.core.tangent.manager.updateControls() -> none
--- Function
--- Update Controls.
---
--- Parameters:
---  * None
---
--- Returns:
---  * None
function mod.updateControls()
    if mod.connected() then
        mod.connected(false)
    end
end

--- plugins.core.tangent.manager.addMode(id, name) -> plugins.core.tangent.manager.mode
--- Function
--- Adds a new `mode` with the specified details and returns it.
---
--- Parameters:
--- * id            - The id number of the Mode.
--- * name          - The name of the Mode.
---
--- Returns:
--- * The new `mode`
function mod.addMode(id, name)
    local m = mode.new(id, name, mod)
    insert(mod._modes, m)
    sort(mod._modes, function(a,b) return a.id < b.id end)
    return m
end

--- plugins.core.tangent.manager.getMode(id) -> plugins.core.tangent.manager.mode
--- Function
--- Returns the `mode` with the specified ID, or `nil`.
---
--- Parameters:
--- * id    - The ID to find.
---
--- Returns:
--- * The `mode`, or `nil`.
function mod.getMode(id)
    for _,m in ipairs(mod._modes) do
        if m.id == id then
            return m
        end
    end
    return nil
end

--- plugins.core.tangent.manager.activeModeID <cp.prop: string>
--- Field
--- The current active mode ID.
mod.activeModeID = config.prop("tangent.activeModeID")

--- plugins.core.tangent.manager.activeMode <cp.prop: mode>
--- Constant
--- Represents the currently active `mode`.
mod.activeMode = mod.activeModeID:mutate(
    function(original)
        local id = original()
        return id and mod.getMode(id)
    end,
    function(newMode, original)
        local m = mode.is(newMode) and newMode or mod.getMode(newMode)
        if m then
            local oldMode = mod._activeMode
            if oldMode and oldMode._deactivate then
{'fraction_non_alphanumeric': 0.3248095188393696, 'fraction_numerical': 0.0013568521031207597, 'mean_word_length': 0.9253491409625239, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 18}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 56, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2638134', 'n_tokens_mistral': 4214, 'n_tokens_neox': 4045, 'n_words': 1104}