mirror of
https://github.com/asherao/dcs-scratchpad.git
synced 2025-10-29 16:56:22 +00:00
497 lines
16 KiB
Lua
497 lines
16 KiB
Lua
function scratchpad_load()
|
|
package.path = package.path .. ";.\\Scripts\\?.lua;.\\Scripts\\UI\\?.lua;"
|
|
|
|
local lfs = require("lfs")
|
|
local U = require("me_utilities")
|
|
local Skin = require("Skin")
|
|
local DialogLoader = require("DialogLoader")
|
|
local Tools = require("tools")
|
|
local Input = require("Input")
|
|
|
|
local isHidden = true
|
|
local keyboardLocked = false
|
|
local window = nil
|
|
local windowDefaultSkin = nil
|
|
local windowSkinHidden = Skin.windowSkinChatMin()
|
|
local panel = nil
|
|
local textarea = nil
|
|
|
|
local getCoordsLua =
|
|
[[
|
|
-- thanks MIST! https://github.com/mrSkortch/MissionScriptingTools/blob/master/mist.lua
|
|
|
|
local round = function (num, idp)
|
|
local mult = 10^(idp or 0)
|
|
return math.floor(num * mult + 0.5) / mult
|
|
end
|
|
|
|
local tostringLL = function (lat, lon, acc, DMS)
|
|
local latHemi, lonHemi
|
|
if lat > 0 then
|
|
latHemi = 'N'
|
|
else
|
|
latHemi = 'S'
|
|
end
|
|
|
|
if lon > 0 then
|
|
lonHemi = 'E'
|
|
else
|
|
lonHemi = 'W'
|
|
end
|
|
|
|
lat = math.abs(lat)
|
|
lon = math.abs(lon)
|
|
|
|
local latDeg = math.floor(lat)
|
|
local latMin = (lat - latDeg)*60
|
|
|
|
local lonDeg = math.floor(lon)
|
|
local lonMin = (lon - lonDeg)*60
|
|
|
|
if DMS then -- degrees, minutes, and seconds.
|
|
local oldLatMin = latMin
|
|
latMin = math.floor(latMin)
|
|
local latSec = round((oldLatMin - latMin)*60, acc)
|
|
|
|
local oldLonMin = lonMin
|
|
lonMin = math.floor(lonMin)
|
|
local lonSec = round((oldLonMin - lonMin)*60, acc)
|
|
|
|
if latSec == 60 then
|
|
latSec = 0
|
|
latMin = latMin + 1
|
|
end
|
|
|
|
if lonSec == 60 then
|
|
lonSec = 0
|
|
lonMin = lonMin + 1
|
|
end
|
|
|
|
local secFrmtStr -- create the formatting string for the seconds place
|
|
if acc <= 0 then -- no decimal place.
|
|
secFrmtStr = '%02d'
|
|
else
|
|
local width = 3 + acc -- 01.310 - that's a width of 6, for example.
|
|
secFrmtStr = '%0' .. width .. '.' .. acc .. 'f'
|
|
end
|
|
|
|
return string.format('%02d', latDeg) .. ' ' .. string.format('%02d', latMin) .. '\' ' .. string.format(secFrmtStr, latSec) .. '"' .. latHemi .. ' '
|
|
.. string.format('%02d', lonDeg) .. ' ' .. string.format('%02d', lonMin) .. '\' ' .. string.format(secFrmtStr, lonSec) .. '"' .. lonHemi
|
|
|
|
else -- degrees, decimal minutes.
|
|
latMin = round(latMin, acc)
|
|
lonMin = round(lonMin, acc)
|
|
|
|
if latMin == 60 then
|
|
latMin = 0
|
|
latDeg = latDeg + 1
|
|
end
|
|
|
|
if lonMin == 60 then
|
|
lonMin = 0
|
|
lonDeg = lonDeg + 1
|
|
end
|
|
|
|
local minFrmtStr -- create the formatting string for the minutes place
|
|
if acc <= 0 then -- no decimal place.
|
|
minFrmtStr = '%02d'
|
|
else
|
|
local width = 3 + acc -- 01.310 - that's a width of 6, for example.
|
|
minFrmtStr = '%0' .. width .. '.' .. acc .. 'f'
|
|
end
|
|
|
|
return string.format('%02d', latDeg) .. ' ' .. string.format(minFrmtStr, latMin) .. '\'' .. latHemi .. ' '
|
|
.. string.format('%02d', lonDeg) .. ' ' .. string.format(minFrmtStr, lonMin) .. '\'' .. lonHemi
|
|
|
|
end
|
|
end
|
|
|
|
local marks = world.getMarkPanels()
|
|
local result = ""
|
|
for _, mark in pairs(marks) do
|
|
local lat, lon = coord.LOtoLL({
|
|
x = mark.pos.x,
|
|
y = 0,
|
|
z = mark.pos.z
|
|
})
|
|
local alt = round(land.getHeight({
|
|
x = mark.pos.x,
|
|
y = mark.pos.z
|
|
}), 0)
|
|
result = result .. "\n" .. tostringLL(lat, lon, 2, true) .. "\n" .. tostring(alt) .. "m, " .. mark.text .. "\n"
|
|
end
|
|
return result
|
|
]]
|
|
|
|
local scratchpad = {
|
|
logFile = io.open(lfs.writedir() .. [[Logs\Scratchpad.log]], "w")
|
|
}
|
|
|
|
local dirPath = lfs.writedir() .. [[Scratchpad\]]
|
|
local currentPage = nil
|
|
local pagesCount = 0
|
|
local pages = {}
|
|
|
|
local function loadPage(page)
|
|
scratchpad.log("loading page " .. page.path)
|
|
file, err = io.open(page.path, "r")
|
|
if err then
|
|
scratchpad.log("Error reading file: " .. page.path)
|
|
return ""
|
|
else
|
|
local content = file:read("*all")
|
|
file:close()
|
|
textarea:setText(content)
|
|
|
|
-- update title
|
|
window:setText(page.name)
|
|
end
|
|
end
|
|
|
|
local function savePage(path, content, override)
|
|
scratchpad.log("saving page " .. path)
|
|
lfs.mkdir(lfs.writedir() .. [[Scratchpad\]])
|
|
local mode = "a"
|
|
if override then
|
|
mode = "w"
|
|
end
|
|
file, err = io.open(path, mode)
|
|
if err then
|
|
scratchpad.log("Error writing file: " .. path)
|
|
else
|
|
file:write(content)
|
|
file:flush()
|
|
file:close()
|
|
end
|
|
end
|
|
|
|
local function nextPage()
|
|
if pagesCount == 0 then
|
|
return
|
|
end
|
|
|
|
local lastPage = nil
|
|
for _, page in pairs(pages) do
|
|
if currentPage == nil or (lastPage ~= nil and lastPage.path == currentPage) then
|
|
loadPage(page)
|
|
currentPage = page.path
|
|
return
|
|
end
|
|
lastPage = page
|
|
end
|
|
|
|
-- restart at the beginning
|
|
loadPage(pages[1])
|
|
currentPage = pages[1].path
|
|
end
|
|
|
|
local function prevPage()
|
|
local lastPage = nil
|
|
for i, page in pairs(pages) do
|
|
if currentPage == nil or (page.path == currentPage and i ~= 1) then
|
|
loadPage(lastPage)
|
|
currentPage = lastPage.path
|
|
return
|
|
end
|
|
lastPage = page
|
|
end
|
|
|
|
-- restart at the end
|
|
loadPage(pages[pagesCount])
|
|
currentPage = pages[pagesCount].path
|
|
end
|
|
|
|
function scratchpad.loadConfiguration()
|
|
scratchpad.log("Loading config file...")
|
|
local tbl = Tools.safeDoFile(lfs.writedir() .. "Config/ScratchpadConfig.lua", false)
|
|
if (tbl and tbl.config) then
|
|
scratchpad.log("Configuration exists...")
|
|
scratchpad.config = tbl.config
|
|
|
|
-- config migration
|
|
|
|
-- add default fontSize config
|
|
if scratchpad.config.fontSize == nil then
|
|
scratchpad.config.fontSize = 14
|
|
scratchpad.saveConfiguration()
|
|
end
|
|
|
|
-- move content into text file
|
|
if scratchpad.config.content ~= nil then
|
|
savePage(dirPath .. [[0000.txt]], scratchpad.config.content, false)
|
|
scratchpad.config.content = nil
|
|
scratchpad.saveConfiguration()
|
|
end
|
|
else
|
|
scratchpad.log("Configuration not found, creating defaults...")
|
|
scratchpad.config = {
|
|
hotkey = "Ctrl+Shift+x",
|
|
windowPosition = {x = 200, y = 200},
|
|
windowSize = {w = 350, h = 150},
|
|
fontSize = 14
|
|
}
|
|
scratchpad.saveConfiguration()
|
|
end
|
|
|
|
-- scan scratchpad dir for pages
|
|
for name in lfs.dir(dirPath) do
|
|
local path = dirPath .. name
|
|
scratchpad.log(path)
|
|
if lfs.attributes(path, "mode") == "file" then
|
|
if name:sub(-4) ~= ".txt" then
|
|
scratchpad.log("Ignoring file " .. name .. ", because of it doesn't seem to be a text file (.txt)")
|
|
elseif lfs.attributes(path, "size") > 1024 * 1024 then
|
|
scratchpad.log("Ignoring file " .. name .. ", because of its file size of more than 1MB")
|
|
else
|
|
scratchpad.log("found page " .. path)
|
|
table.insert(
|
|
pages,
|
|
{
|
|
name = name:sub(1, -5),
|
|
path = path
|
|
}
|
|
)
|
|
pagesCount = pagesCount + 1
|
|
end
|
|
end
|
|
end
|
|
|
|
-- there are no pages yet, create one
|
|
if pagesCount == 0 then
|
|
path = dirPath .. [[0000.txt]]
|
|
scratchpad.log("creating page " .. path)
|
|
table.insert(
|
|
pages,
|
|
{
|
|
name = "0000",
|
|
path = path
|
|
}
|
|
)
|
|
pagesCount = pagesCount + 1
|
|
end
|
|
end
|
|
|
|
function scratchpad.saveConfiguration()
|
|
U.saveInFile(scratchpad.config, "config", lfs.writedir() .. "Config/ScratchpadConfig.lua")
|
|
end
|
|
|
|
function scratchpad.log(str)
|
|
if not str then
|
|
return
|
|
end
|
|
|
|
if scratchpad.logFile then
|
|
scratchpad.logFile:write("[" .. os.date("%H:%M:%S") .. "] " .. str .. "\r\n")
|
|
scratchpad.logFile:flush()
|
|
end
|
|
end
|
|
|
|
local function unlockKeyboardInput(releaseKeyboardKeys)
|
|
if keyboardLocked then
|
|
DCS.unlockKeyboardInput(releaseKeyboardKeys)
|
|
keyboardLocked = false
|
|
end
|
|
end
|
|
|
|
local function lockKeyboardInput()
|
|
if keyboardLocked then
|
|
return
|
|
end
|
|
|
|
local keyboardEvents = Input.getDeviceKeys(Input.getKeyboardDeviceName())
|
|
DCS.lockKeyboardInput(keyboardEvents)
|
|
keyboardLocked = true
|
|
end
|
|
|
|
local function insertCoordinates()
|
|
local coords = net.dostring_in("server", getCoordsLua)
|
|
local lineCountBefore = textarea:getLineCount()
|
|
|
|
if coords == "" then
|
|
textarea:setText(textarea:getText() .. "\nNo marks found\n")
|
|
else
|
|
textarea:setText(textarea:getText() .. coords .. "\n")
|
|
end
|
|
|
|
-- scroll to the bottom of the textarea
|
|
local lastLine = textarea:getLineCount() - 1
|
|
local lastLineChar = textarea:getLineTextLength(lastLine)
|
|
textarea:setSelectionNew(lastLine, 0, lastLine, lastLineLen)
|
|
savePage(currentPage, textarea:getText(), true)
|
|
end
|
|
|
|
function scratchpad.createWindow()
|
|
window = DialogLoader.spawnDialogFromFile(lfs.writedir() .. "Scripts\\Scratchpad\\ScratchpadWindow.dlg", cdata)
|
|
windowDefaultSkin = window:getSkin()
|
|
panel = window.Box
|
|
textarea = panel.ScratchpadEditBox
|
|
insertCoordsBtn = panel.ScratchpadInsertCoordsButton
|
|
prevButton = panel.ScratchpadPrevButton
|
|
nextButton = panel.ScratchpadNextButton
|
|
|
|
-- setup textarea
|
|
local skin = textarea:getSkin()
|
|
skin.skinData.states.released[1].text.fontSize = scratchpad.config.fontSize
|
|
textarea:setSkin(skin)
|
|
|
|
textarea:addChangeCallback(
|
|
function(self)
|
|
savePage(currentPage, self:getText(), true)
|
|
end
|
|
)
|
|
textarea:addFocusCallback(
|
|
function(self)
|
|
if self:getFocused() then
|
|
lockKeyboardInput()
|
|
else
|
|
unlockKeyboardInput(true)
|
|
end
|
|
end
|
|
)
|
|
textarea:addKeyDownCallback(
|
|
function(self, keyName, unicode)
|
|
if keyName == "escape" then
|
|
self:setFocused(false)
|
|
unlockKeyboardInput(true)
|
|
end
|
|
end
|
|
)
|
|
|
|
-- setup button callbacks
|
|
prevButton:addMouseDownCallback(
|
|
function(self)
|
|
prevPage()
|
|
end
|
|
)
|
|
nextButton:addMouseDownCallback(
|
|
function(self)
|
|
nextPage()
|
|
end
|
|
)
|
|
insertCoordsBtn:addMouseDownCallback(
|
|
function(self)
|
|
insertCoordinates()
|
|
end
|
|
)
|
|
|
|
-- setup window
|
|
window:setBounds(
|
|
scratchpad.config.windowPosition.x,
|
|
scratchpad.config.windowPosition.y,
|
|
scratchpad.config.windowSize.w,
|
|
scratchpad.config.windowSize.h
|
|
)
|
|
scratchpad.handleResize(window)
|
|
|
|
window:addHotKeyCallback(
|
|
scratchpad.config.hotkey,
|
|
function()
|
|
if isHidden == true then
|
|
scratchpad.show()
|
|
else
|
|
scratchpad.hide()
|
|
end
|
|
end
|
|
)
|
|
window:addSizeCallback(scratchpad.handleResize)
|
|
window:addPositionCallback(scratchpad.handleMove)
|
|
|
|
window:setVisible(true)
|
|
nextPage()
|
|
|
|
scratchpad.hide()
|
|
scratchpad.log("Scratchpad Window created")
|
|
end
|
|
|
|
function scratchpad.setVisible(b)
|
|
window:setVisible(b)
|
|
end
|
|
|
|
function scratchpad.handleResize(self)
|
|
local w, h = self:getSize()
|
|
|
|
panel:setBounds(0, 0, w, h - 20)
|
|
textarea:setBounds(0, 0, w, h - 20 - 20)
|
|
prevButton:setBounds(0, h - 40, 50, 20)
|
|
nextButton:setBounds(55, h - 40, 50, 20)
|
|
|
|
if pagesCount > 1 then
|
|
insertCoordsBtn:setBounds(120, h - 40, 50, 20)
|
|
else
|
|
insertCoordsBtn:setBounds(0, h - 40, 50, 20)
|
|
end
|
|
|
|
scratchpad.config.windowSize = {w = w, h = h}
|
|
scratchpad.saveConfiguration()
|
|
end
|
|
|
|
function scratchpad.handleMove(self)
|
|
local x, y = self:getPosition()
|
|
scratchpad.config.windowPosition = {x = x, y = y}
|
|
scratchpad.saveConfiguration()
|
|
end
|
|
|
|
function scratchpad.show()
|
|
if window == nil then
|
|
local status, err = pcall(scratchpad.createWindow)
|
|
if not status then
|
|
net.log("[Scratchpad] Error creating window: " .. tostring(err))
|
|
end
|
|
end
|
|
|
|
window:setVisible(true)
|
|
window:setSkin(windowDefaultSkin)
|
|
panel:setVisible(true)
|
|
window:setHasCursor(true)
|
|
|
|
-- insert coords only works if the client is the server, so hide the button otherwise
|
|
if DCS.isServer() then
|
|
insertCoordsBtn:setVisible(true)
|
|
else
|
|
insertCoordsBtn:setVisible(false)
|
|
end
|
|
|
|
-- show prev/next buttons only if we have more than one page
|
|
if pagesCount > 1 then
|
|
prevButton:setVisible(true)
|
|
nextButton:setVisible(true)
|
|
else
|
|
prevButton:setVisible(false)
|
|
nextButton:setVisible(false)
|
|
end
|
|
|
|
isHidden = false
|
|
end
|
|
|
|
function scratchpad.hide()
|
|
window:setSkin(windowSkinHidden)
|
|
panel:setVisible(false)
|
|
textarea:setFocused(false)
|
|
window:setHasCursor(false)
|
|
-- window.setVisible(false) -- if you make the window invisible, its destroyed
|
|
unlockKeyboardInput(true)
|
|
|
|
isHidden = true
|
|
end
|
|
|
|
function scratchpad.onSimulationFrame()
|
|
if scratchpad.config == nil then
|
|
scratchpad.loadConfiguration()
|
|
end
|
|
|
|
if not window then
|
|
scratchpad.log("Creating Scratchpad window hidden...")
|
|
scratchpad.createWindow()
|
|
end
|
|
end
|
|
|
|
DCS.setUserCallbacks(scratchpad)
|
|
|
|
net.log("[Scratchpad] Loaded ...")
|
|
end
|
|
|
|
local status, err = pcall(scratchpad_load)
|
|
if not status then
|
|
net.log("[Scratchpad] Load Error: " .. tostring(err))
|
|
end
|