Order files
This commit is contained in:
@ -0,0 +1,735 @@
|
||||
-- Copyright 2013-15 Paul Kulchenko, ZeroBrane LLC
|
||||
---------------------------------------------------------
|
||||
|
||||
local ide = ide
|
||||
local iscaseinsensitive = wx.wxFileName("A"):SameAs(wx.wxFileName("a"))
|
||||
local unpack = table.unpack or unpack
|
||||
local q = EscapeMagic
|
||||
|
||||
local function eventHandle(handlers, event, ...)
|
||||
local success
|
||||
for package, handler in pairs(handlers) do
|
||||
local ok, res = pcall(handler, package, ...)
|
||||
if ok then
|
||||
if res == false then success = false end
|
||||
else
|
||||
DisplayOutputLn(TR("%s event failed: %s"):format(event, res))
|
||||
end
|
||||
end
|
||||
return success
|
||||
end
|
||||
|
||||
local function getEventHandlers(packages, event)
|
||||
local handlers = {}
|
||||
for _, package in pairs(packages) do
|
||||
if package[event] then handlers[package] = package[event] end
|
||||
end
|
||||
return handlers
|
||||
end
|
||||
|
||||
function PackageEventHandle(event, ...)
|
||||
return eventHandle(getEventHandlers(ide.packages, event), event, ...)
|
||||
end
|
||||
|
||||
function PackageEventHandleOnce(event, ...)
|
||||
-- copy packages as the event that is handled only once needs to be removed
|
||||
local handlers = getEventHandlers(ide.packages, event)
|
||||
-- remove all handlers as they need to be called only once
|
||||
-- this allows them to be re-installed if needed
|
||||
for _, package in pairs(ide.packages) do package[event] = nil end
|
||||
return eventHandle(handlers, event, ...)
|
||||
end
|
||||
|
||||
local function PackageEventHandleOne(file, event, ...)
|
||||
local package = ide.packages[file]
|
||||
if package and type(package[event]) == 'function' then
|
||||
local ok, res = pcall(package[event], package, ...)
|
||||
if ok then
|
||||
if res == false then return false end
|
||||
else
|
||||
DisplayOutputLn(TR("%s event failed: %s"):format(event, res))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PackageUnRegister(file, ...)
|
||||
PackageEventHandleOne(file, "onUnRegister", ...)
|
||||
-- remove from the list of installed packages
|
||||
local package = ide.packages[file]
|
||||
ide.packages[file] = nil
|
||||
return package
|
||||
end
|
||||
|
||||
function PackageRegister(file, ...)
|
||||
if not ide.packages[file] then
|
||||
local packages = {}
|
||||
local package = MergeFullPath(
|
||||
GetPathWithSep(ide.editorFilename), "packages/"..file..".lua")
|
||||
LoadLuaFileExt(packages, package, ide.proto.Plugin)
|
||||
packages[file].fname = file
|
||||
ide.packages[file] = packages[file]
|
||||
end
|
||||
return PackageEventHandleOne(file, "onRegister", ...)
|
||||
end
|
||||
|
||||
function ide:GetRootPath(path)
|
||||
return MergeFullPath(GetPathWithSep(self.editorFilename), path or '')
|
||||
end
|
||||
function ide:GetPackagePath(packname)
|
||||
return MergeFullPath(
|
||||
self.oshome and MergeFullPath(self.oshome, '.'..self:GetAppName()..'/') or self:GetRootPath(),
|
||||
MergeFullPath('packages', packname or '')
|
||||
)
|
||||
end
|
||||
function ide:GetApp() return self.editorApp end
|
||||
function ide:GetAppName() return self.appname end
|
||||
function ide:GetEditor(index) return GetEditor(index) end
|
||||
function ide:GetEditorWithFocus(...) return GetEditorWithFocus(...) end
|
||||
function ide:GetEditorWithLastFocus()
|
||||
-- make sure ide.infocus is still a valid component and not "some" userdata
|
||||
return (self:IsValidCtrl(self.infocus)
|
||||
and self.infocus:GetClassInfo():GetClassName() == "wxStyledTextCtrl"
|
||||
and self.infocus:DynamicCast("wxStyledTextCtrl") or nil)
|
||||
end
|
||||
function ide:GetMenuBar() return self.frame.menuBar end
|
||||
function ide:GetStatusBar() return self.frame.statusBar end
|
||||
function ide:GetToolBar() return self.frame.toolBar end
|
||||
function ide:GetDebugger() return self.debugger end
|
||||
function ide:GetMainFrame() return self.frame end
|
||||
function ide:GetUIManager() return self.frame.uimgr end
|
||||
function ide:GetDocument(ed) return ed and self.openDocuments[ed:GetId()] end
|
||||
function ide:GetDocuments() return self.openDocuments end
|
||||
function ide:GetKnownExtensions(ext)
|
||||
local knownexts, extmatch = {}, ext and ext:lower()
|
||||
for _, spec in pairs(self.specs) do
|
||||
for _, ext in ipairs(spec.exts or {}) do
|
||||
if not extmatch or extmatch == ext:lower() then
|
||||
table.insert(knownexts, ext)
|
||||
end
|
||||
end
|
||||
end
|
||||
table.sort(knownexts)
|
||||
return knownexts
|
||||
end
|
||||
|
||||
function ide:DoWhenIdle(func) table.insert(self.onidle, func) end
|
||||
|
||||
function ide:FindTopMenu(item)
|
||||
local index = self:GetMenuBar():FindMenu((TR)(item))
|
||||
return self:GetMenuBar():GetMenu(index), index
|
||||
end
|
||||
function ide:FindMenuItem(itemid, menu)
|
||||
local item, imenu = self:GetMenuBar():FindItem(itemid, menu)
|
||||
if menu and not item then item = menu:FindItem(itemid) end
|
||||
if not item then return end
|
||||
menu = menu or imenu
|
||||
|
||||
for pos = 0, menu:GetMenuItemCount()-1 do
|
||||
if menu:FindItemByPosition(pos):GetId() == itemid then
|
||||
return item, menu, pos
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
function ide:AttachMenu(...)
|
||||
-- AttachMenu([targetmenu,] id, submenu)
|
||||
-- `targetmenu` is only needed for menus not attached to the main menubar
|
||||
local menu, id, submenu = ...
|
||||
if select('#', ...) == 2 then menu, id, submenu = nil, ... end
|
||||
local item, menu, pos = self:FindMenuItem(id, menu)
|
||||
if not item then return end
|
||||
|
||||
local menuitem = wx.wxMenuItem(menu, id, item:GetItemLabel(), item:GetHelp(), wx.wxITEM_NORMAL, submenu)
|
||||
menu:Destroy(item)
|
||||
return menu:Insert(pos, menuitem), pos
|
||||
end
|
||||
function ide:CloneMenu(menu)
|
||||
if not menu then return end
|
||||
local newmenu = wx.wxMenu()
|
||||
local node = menu:GetMenuItems():GetFirst()
|
||||
while node do
|
||||
local item = node:GetData():DynamicCast("wxMenuItem")
|
||||
newmenu:Append(item:GetId(), item:GetItemLabel(), item:GetHelp(), item:GetKind())
|
||||
node = node:GetNext()
|
||||
end
|
||||
return newmenu
|
||||
end
|
||||
|
||||
function ide:FindDocument(path)
|
||||
local fileName = wx.wxFileName(path)
|
||||
for _, doc in pairs(self:GetDocuments()) do
|
||||
if doc.filePath and fileName:SameAs(wx.wxFileName(doc.filePath)) then
|
||||
return doc
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
function ide:FindDocumentsByPartialPath(path)
|
||||
local seps = "[\\/]"
|
||||
-- add trailing path separator to make sure full directory match
|
||||
if not path:find(seps.."$") then path = path .. GetPathSeparator() end
|
||||
local pattern = "^"..q(path):gsub(seps, seps)
|
||||
local lpattern = pattern:lower()
|
||||
|
||||
local docs = {}
|
||||
for _, doc in pairs(self:GetDocuments()) do
|
||||
if doc.filePath
|
||||
and (doc.filePath:find(pattern)
|
||||
or iscaseinsensitive and doc.filePath:lower():find(lpattern)) then
|
||||
table.insert(docs, doc)
|
||||
end
|
||||
end
|
||||
return docs
|
||||
end
|
||||
function ide:GetInterpreter() return self.interpreter end
|
||||
function ide:GetInterpreters() return self.interpreters end
|
||||
function ide:GetConfig() return self.config end
|
||||
function ide:GetOutput() return self.frame.bottomnotebook.errorlog end
|
||||
function ide:GetConsole() return self.frame.bottomnotebook.shellbox end
|
||||
function ide:GetEditorNotebook() return self.frame.notebook end
|
||||
function ide:GetOutputNotebook() return self.frame.bottomnotebook end
|
||||
function ide:GetOutline() return self.outline end
|
||||
function ide:GetProjectNotebook() return self.frame.projnotebook end
|
||||
function ide:GetProject() return FileTreeGetDir() end
|
||||
function ide:GetProjectStartFile()
|
||||
local projectdir = FileTreeGetDir()
|
||||
local startfile = self.filetree.settings.startfile[projectdir]
|
||||
return MergeFullPath(projectdir, startfile), startfile
|
||||
end
|
||||
function ide:GetLaunchedProcess() return self.debugger and self.debugger.pid end
|
||||
function ide:GetProjectTree() return self.filetree.projtreeCtrl end
|
||||
function ide:GetOutlineTree() return self.outline.outlineCtrl end
|
||||
function ide:GetWatch() return self.debugger and self.debugger.watchCtrl end
|
||||
function ide:GetStack() return self.debugger and self.debugger.stackCtrl end
|
||||
|
||||
local statusreset
|
||||
function ide:SetStatusFor(text, interval, field)
|
||||
field = field or 0
|
||||
interval = interval or 2
|
||||
local statusbar = self:GetStatusBar()
|
||||
if not ide.timers.status then
|
||||
ide.timers.status = wx.wxTimer(statusbar)
|
||||
statusbar:Connect(wx.wxEVT_TIMER, function(event) if statusreset then statusreset() end end)
|
||||
end
|
||||
statusreset = function()
|
||||
if statusbar:GetStatusText(field) == text then statusbar:SetStatusText("", field) end
|
||||
end
|
||||
ide.timers.status:Start(interval*1000, wx.wxTIMER_ONE_SHOT)
|
||||
statusbar:SetStatusText(text, field)
|
||||
end
|
||||
function ide:SetStatus(text, field) self:GetStatusBar():SetStatusText(text, field or 0) end
|
||||
function ide:GetStatus(field) return self:GetStatusBar():GetStatusText(field or 0) end
|
||||
function ide:PushStatus(text, field) self:GetStatusBar():PushStatusText(text, field or 0) end
|
||||
function ide:PopStatus(field) self:GetStatusBar():PopStatusText(field or 0) end
|
||||
function ide:Yield() wx.wxYield() end
|
||||
function ide:CreateBareEditor() return CreateEditor(true) end
|
||||
|
||||
local rawMethods = {"AddTextDyn", "InsertTextDyn", "AppendTextDyn", "SetTextDyn",
|
||||
"GetTextDyn", "GetLineDyn", "GetSelectedTextDyn", "GetTextRangeDyn"}
|
||||
local useraw = nil
|
||||
|
||||
function ide:CreateStyledTextCtrl(...)
|
||||
local editor = wxstc.wxStyledTextCtrl(...)
|
||||
if not editor then return end
|
||||
|
||||
if useraw == nil then
|
||||
useraw = true
|
||||
for _, m in ipairs(rawMethods) do
|
||||
if not pcall(function() return editor[m:gsub("Dyn", "Raw")] end) then useraw = false; break end
|
||||
end
|
||||
end
|
||||
|
||||
-- map all `GetTextDyn` to `GetText` or `GetTextRaw` if `*Raw` methods are present
|
||||
editor.useraw = useraw
|
||||
for _, m in ipairs(rawMethods) do
|
||||
-- some `*Raw` methods return `nil` instead of `""` as their "normal" calls do
|
||||
-- (for example, `GetLineRaw` and `GetTextRangeRaw` for parameters outside of text)
|
||||
local def = m:find("^Get") and "" or nil
|
||||
editor[m] = function(...) return editor[m:gsub("Dyn", useraw and "Raw" or "")](...) or def end
|
||||
end
|
||||
|
||||
local suffix = "\1\0"
|
||||
function editor:CopyDyn()
|
||||
if not self.useraw then return self:Copy() end
|
||||
-- check if selected fragment is a valid UTF-8 sequence
|
||||
local text = self:GetSelectedTextRaw()
|
||||
if text == "" or wx.wxString.FromUTF8(text) ~= "" then return self:Copy() end
|
||||
local tdo = wx.wxTextDataObject()
|
||||
-- append suffix as wxwidgets (3.1+ on Windows) truncate last char for odd-length strings
|
||||
local workaround = ide.osname == "Windows" and (#text % 2 > 0) and suffix or ""
|
||||
tdo:SetData(wx.wxDataFormat(wx.wxDF_TEXT), text..workaround)
|
||||
local clip = wx.wxClipboard.Get()
|
||||
clip:Open()
|
||||
clip:SetData(tdo)
|
||||
clip:Close()
|
||||
end
|
||||
|
||||
function editor:PasteDyn()
|
||||
if not self.useraw then return self:Paste() end
|
||||
local tdo = wx.wxTextDataObject()
|
||||
local clip = wx.wxClipboard.Get()
|
||||
clip:Open()
|
||||
clip:GetData(tdo)
|
||||
clip:Close()
|
||||
local ok, text = tdo:GetDataHere(wx.wxDataFormat(wx.wxDF_TEXT))
|
||||
-- check if the fragment being pasted is a valid UTF-8 sequence
|
||||
if not ok or text == "" or wx.wxString.FromUTF8(text) ~= "" then return self:Paste() end
|
||||
if ide.osname == "Windows" then text = text:gsub(suffix.."+$","") end
|
||||
self:AddTextRaw(text)
|
||||
self:GotoPos(self:GetCurrentPos())
|
||||
end
|
||||
|
||||
function editor:GotoPosEnforcePolicy(pos)
|
||||
self:GotoPos(pos)
|
||||
self:EnsureVisibleEnforcePolicy(self:LineFromPosition(pos))
|
||||
end
|
||||
|
||||
function editor:CanFold()
|
||||
local foldable = false
|
||||
for m = 0, ide.MAXMARGIN do
|
||||
if editor:GetMarginWidth(m) > 0
|
||||
and editor:GetMarginMask(m) == wxstc.wxSTC_MASK_FOLDERS then
|
||||
foldable = true
|
||||
end
|
||||
end
|
||||
return foldable
|
||||
end
|
||||
|
||||
-- circle through "fold all" => "hide base lines" => "unfold all"
|
||||
function editor:FoldSome()
|
||||
editor:Colourise(0, -1) -- update doc's folding info
|
||||
local foldall = false -- at least on header unfolded => fold all
|
||||
local hidebase = false -- at least one base is visible => hide all
|
||||
local lines = editor:GetLineCount()
|
||||
|
||||
for ln = 0, lines-1 do
|
||||
local foldRaw = editor:GetFoldLevel(ln)
|
||||
local foldLvl = foldRaw % 4096
|
||||
local foldHdr = (math.floor(foldRaw / 8192) % 2) == 1
|
||||
|
||||
-- at least one header is expanded
|
||||
foldall = foldall or (foldHdr and editor:GetFoldExpanded(ln))
|
||||
|
||||
-- at least one base can be hidden
|
||||
hidebase = hidebase or (
|
||||
not foldHdr
|
||||
and ln > 1 -- first line can't be hidden, so ignore it
|
||||
and foldLvl == wxstc.wxSTC_FOLDLEVELBASE
|
||||
and bit.band(foldRaw, wxstc.wxSTC_FOLDLEVELWHITEFLAG) == 0
|
||||
and editor:GetLineVisible(ln))
|
||||
end
|
||||
|
||||
-- shows lines; this doesn't change fold status for folded lines
|
||||
if not foldall and not hidebase then editor:ShowLines(0, lines-1) end
|
||||
|
||||
for ln = 0, lines-1 do
|
||||
local foldRaw = editor:GetFoldLevel(ln)
|
||||
local foldLvl = foldRaw % 4096
|
||||
local foldHdr = (math.floor(foldRaw / 8192) % 2) == 1
|
||||
|
||||
if foldall then
|
||||
if foldHdr and editor:GetFoldExpanded(ln) then
|
||||
editor:ToggleFold(ln)
|
||||
end
|
||||
elseif hidebase then
|
||||
if not foldHdr and (foldLvl == wxstc.wxSTC_FOLDLEVELBASE) then
|
||||
editor:HideLines(ln, ln)
|
||||
end
|
||||
else -- unfold all
|
||||
if foldHdr and not editor:GetFoldExpanded(ln) then
|
||||
editor:ToggleFold(ln)
|
||||
end
|
||||
end
|
||||
end
|
||||
editor:EnsureCaretVisible()
|
||||
end
|
||||
|
||||
local function getMarginWidth(editor)
|
||||
local width = 0
|
||||
for m = 0, ide.MAXMARGIN do width = width + editor:GetMarginWidth(m) end
|
||||
return width
|
||||
end
|
||||
|
||||
function editor:ShowPosEnforcePolicy(pos)
|
||||
local line = self:LineFromPosition(pos)
|
||||
self:EnsureVisibleEnforcePolicy(line)
|
||||
-- skip the rest if line wrapping is on
|
||||
if editor:GetWrapMode() ~= wxstc.wxSTC_WRAP_NONE then return end
|
||||
local xwidth = self:GetClientSize():GetWidth() - getMarginWidth(self)
|
||||
local xoffset = self:GetTextExtent(self:GetLineDyn(line):sub(1, pos-self:PositionFromLine(line)+1))
|
||||
self:SetXOffset(xoffset > xwidth and xoffset-xwidth or 0)
|
||||
end
|
||||
|
||||
function editor:ClearAny()
|
||||
local length = self:GetLength()
|
||||
local selections = ide.wxver >= "2.9.5" and self:GetSelections() or 1
|
||||
self:Clear() -- remove selected fragments
|
||||
|
||||
-- check if the modification has failed, which may happen
|
||||
-- if there is "invisible" text in the selected fragment.
|
||||
-- if there is only one selection, then delete manually.
|
||||
if length == self:GetLength() and selections == 1 then
|
||||
self:SetTargetStart(self:GetSelectionStart())
|
||||
self:SetTargetEnd(self:GetSelectionEnd())
|
||||
self:ReplaceTarget("")
|
||||
end
|
||||
end
|
||||
|
||||
function editor:MarkerGetAll(mask, from, to)
|
||||
mask = mask or 2^24-1
|
||||
local markers = {}
|
||||
local line = editor:MarkerNext(from or 0, mask)
|
||||
while line > -1 do
|
||||
table.insert(markers, {line, editor:MarkerGet(line)})
|
||||
if to and line > to then break end
|
||||
line = editor:MarkerNext(line + 1, mask)
|
||||
end
|
||||
return markers
|
||||
end
|
||||
|
||||
editor:Connect(wx.wxEVT_KEY_DOWN,
|
||||
function (event)
|
||||
local keycode = event:GetKeyCode()
|
||||
local mod = event:GetModifiers()
|
||||
if (keycode == wx.WXK_DELETE and mod == wx.wxMOD_SHIFT)
|
||||
or (keycode == wx.WXK_INSERT and mod == wx.wxMOD_CONTROL)
|
||||
or (keycode == wx.WXK_INSERT and mod == wx.wxMOD_SHIFT) then
|
||||
local id = keycode == wx.WXK_DELETE and ID.CUT or mod == wx.wxMOD_SHIFT and ID.PASTE or ID.COPY
|
||||
ide.frame:AddPendingEvent(wx.wxCommandEvent(wx.wxEVT_COMMAND_MENU_SELECTED, id))
|
||||
elseif keycode == wx.WXK_CAPITAL and mod == wx.wxMOD_CONTROL then
|
||||
-- ignore Ctrl+CapsLock
|
||||
else
|
||||
event:Skip()
|
||||
end
|
||||
end)
|
||||
return editor
|
||||
end
|
||||
|
||||
function ide:LoadFile(...) return LoadFile(...) end
|
||||
|
||||
function ide:CopyToClipboard(text)
|
||||
if wx.wxClipboard:Get():Open() then
|
||||
wx.wxClipboard:Get():SetData(wx.wxTextDataObject(text))
|
||||
wx.wxClipboard:Get():Close()
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function ide:GetSetting(path, setting)
|
||||
local settings = self.settings
|
||||
local curpath = settings:GetPath()
|
||||
settings:SetPath(path)
|
||||
local ok, value = settings:Read(setting)
|
||||
settings:SetPath(curpath)
|
||||
return ok and value or nil
|
||||
end
|
||||
|
||||
function ide:RemoveMenuItem(id, menu)
|
||||
local _, menu, pos = self:FindMenuItem(id, menu)
|
||||
if menu then
|
||||
self:GetMainFrame():Disconnect(id, wx.wxID_ANY, wx.wxEVT_COMMAND_MENU_SELECTED)
|
||||
self:GetMainFrame():Disconnect(id, wx.wxID_ANY, wx.wxEVT_UPDATE_UI)
|
||||
menu:Disconnect(id, wx.wxID_ANY, wx.wxEVT_COMMAND_MENU_SELECTED)
|
||||
menu:Disconnect(id, wx.wxID_ANY, wx.wxEVT_UPDATE_UI)
|
||||
menu:Remove(id)
|
||||
|
||||
local positem = menu:FindItemByPosition(pos)
|
||||
if (not positem or positem:GetKind() == wx.wxITEM_SEPARATOR)
|
||||
and pos > 0
|
||||
and (menu:FindItemByPosition(pos-1):GetKind() == wx.wxITEM_SEPARATOR) then
|
||||
menu:Destroy(menu:FindItemByPosition(pos-1))
|
||||
end
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function ide:ExecuteCommand(cmd, wdir, callback, endcallback)
|
||||
local proc = wx.wxProcess(self:GetOutput())
|
||||
proc:Redirect()
|
||||
|
||||
local cwd
|
||||
if (wdir and #wdir > 0) then -- ignore empty directory
|
||||
cwd = wx.wxFileName.GetCwd()
|
||||
cwd = wx.wxFileName.SetCwd(wdir) and cwd
|
||||
end
|
||||
|
||||
local pid = wx.wxExecute(cmd, wx.wxEXEC_ASYNC, proc)
|
||||
pid = pid ~= -1 and pid ~= 0 and pid or nil
|
||||
if cwd then wx.wxFileName.SetCwd(cwd) end -- restore workdir
|
||||
if not pid then return pid, wx.wxSysErrorMsg() end
|
||||
|
||||
OutputSetCallbacks(pid, proc, callback or function() end, endcallback)
|
||||
return pid
|
||||
end
|
||||
|
||||
function ide:CreateImageList(group, ...)
|
||||
local _ = wx.wxLogNull() -- disable error reporting in popup
|
||||
local size = wx.wxSize(16,16)
|
||||
local imglist = wx.wxImageList(16,16)
|
||||
for i = 1, select('#', ...) do
|
||||
local icon, file = self:GetBitmap(select(i, ...), group, size)
|
||||
if imglist:Add(icon) == -1 then
|
||||
DisplayOutputLn(("Failed to add image '%s' to the image list.")
|
||||
:format(file or select(i, ...)))
|
||||
end
|
||||
end
|
||||
return imglist
|
||||
end
|
||||
|
||||
local tintdef = 100
|
||||
local function iconFilter(bitmap, tint)
|
||||
if type(tint) == 'function' then return tint(bitmap) end
|
||||
if type(tint) ~= 'table' or #tint ~= 3 then return bitmap end
|
||||
|
||||
local tr, tg, tb = tint[1]/255, tint[2]/255, tint[3]/255
|
||||
local pi = 0.299*tr + 0.587*tg + 0.114*tb -- pixel intensity
|
||||
local perc = (tint[0] or tintdef)/tintdef
|
||||
tr, tg, tb = tr*perc, tg*perc, tb*perc
|
||||
|
||||
local img = bitmap:ConvertToImage()
|
||||
for x = 0, img:GetWidth()-1 do
|
||||
for y = 0, img:GetHeight()-1 do
|
||||
if not img:IsTransparent(x, y) then
|
||||
local r, g, b = img:GetRed(x, y)/255, img:GetGreen(x, y)/255, img:GetBlue(x, y)/255
|
||||
local gs = (r + g + b) / 3
|
||||
local weight = 1-4*(gs-0.5)*(gs-0.5)
|
||||
r = math.max(0, math.min(255, math.floor(255 * (gs + (tr-pi) * weight))))
|
||||
g = math.max(0, math.min(255, math.floor(255 * (gs + (tg-pi) * weight))))
|
||||
b = math.max(0, math.min(255, math.floor(255 * (gs + (tb-pi) * weight))))
|
||||
img:SetRGB(x, y, r, g, b)
|
||||
end
|
||||
end
|
||||
end
|
||||
return wx.wxBitmap(img)
|
||||
end
|
||||
|
||||
local icons = {} -- icon cache to avoid reloading the same icons
|
||||
function ide:GetBitmap(id, client, size)
|
||||
local im = self.config.imagemap
|
||||
local width = size:GetWidth()
|
||||
local key = width.."/"..id
|
||||
local keyclient = key.."-"..client
|
||||
local mapped = im[keyclient] or im[id.."-"..client] or im[key] or im[id]
|
||||
-- mapped may be a file name/path or wxImage object; take that into account
|
||||
if type(im[id.."-"..client]) == 'string' then keyclient = width.."/"..im[id.."-"..client]
|
||||
elseif type(im[keyclient]) == 'string' then keyclient = im[keyclient]
|
||||
elseif type(im[id]) == 'string' then
|
||||
id = im[id]
|
||||
key = width.."/"..id
|
||||
keyclient = key.."-"..client
|
||||
end
|
||||
|
||||
local fileClient = self:GetAppName() .. "/res/" .. keyclient .. ".png"
|
||||
local fileKey = self:GetAppName() .. "/res/" .. key .. ".png"
|
||||
local isImage = type(mapped) == 'userdata' and mapped:GetClassInfo():GetClassName() == 'wxImage'
|
||||
local file
|
||||
if mapped and (isImage or wx.wxFileName(mapped):FileExists()) then file = mapped
|
||||
elseif wx.wxFileName(fileClient):FileExists() then file = fileClient
|
||||
elseif wx.wxFileName(fileKey):FileExists() then file = fileKey
|
||||
else return wx.wxArtProvider.GetBitmap(id, client, size) end
|
||||
local icon = icons[file] or iconFilter(wx.wxBitmap(file), self.config.imagetint)
|
||||
icons[file] = icon
|
||||
return icon, file
|
||||
end
|
||||
|
||||
function ide:AddPackage(name, package)
|
||||
self.packages[name] = setmetatable(package, self.proto.Plugin)
|
||||
self.packages[name].fname = name
|
||||
return self.packages[name]
|
||||
end
|
||||
function ide:RemovePackage(name) self.packages[name] = nil end
|
||||
|
||||
function ide:AddWatch(watch, value)
|
||||
local mgr = self.frame.uimgr
|
||||
local pane = mgr:GetPane("watchpanel")
|
||||
if (pane:IsOk() and not pane:IsShown()) then
|
||||
pane:Show()
|
||||
mgr:Update()
|
||||
end
|
||||
|
||||
local watchCtrl = self.debugger.watchCtrl
|
||||
if not watchCtrl then return end
|
||||
|
||||
local root = watchCtrl:GetRootItem()
|
||||
if not root or not root:IsOk() then return end
|
||||
|
||||
local item = watchCtrl:GetFirstChild(root)
|
||||
while true do
|
||||
if not item:IsOk() then break end
|
||||
if watchCtrl:GetItemExpression(item) == watch then
|
||||
if value then watchCtrl:SetItemText(item, watch .. ' = ' .. tostring(value)) end
|
||||
return item
|
||||
end
|
||||
item = watchCtrl:GetNextSibling(item)
|
||||
end
|
||||
|
||||
item = watchCtrl:AppendItem(root, watch, 1)
|
||||
watchCtrl:SetItemExpression(item, watch, value)
|
||||
return item
|
||||
end
|
||||
|
||||
function ide:AddInterpreter(name, interpreter)
|
||||
self.interpreters[name] = setmetatable(interpreter, self.proto.Interpreter)
|
||||
ProjectUpdateInterpreters()
|
||||
end
|
||||
function ide:RemoveInterpreter(name)
|
||||
self.interpreters[name] = nil
|
||||
ProjectUpdateInterpreters()
|
||||
end
|
||||
|
||||
function ide:AddSpec(name, spec)
|
||||
self.specs[name] = spec
|
||||
UpdateSpecs()
|
||||
end
|
||||
function ide:RemoveSpec(name) self.specs[name] = nil end
|
||||
|
||||
function ide:AddAPI(type, name, api)
|
||||
self.apis[type] = self.apis[type] or {}
|
||||
self.apis[type][name] = api
|
||||
end
|
||||
function ide:RemoveAPI(type, name) self.apis[type][name] = nil end
|
||||
|
||||
function ide:AddConsoleAlias(alias, table) return ShellSetAlias(alias, table) end
|
||||
function ide:RemoveConsoleAlias(alias) return ShellSetAlias(alias, nil) end
|
||||
|
||||
function ide:AddMarker(...) return StylesAddMarker(...) end
|
||||
function ide:GetMarker(marker) return StylesGetMarker(marker) end
|
||||
function ide:RemoveMarker(marker) StylesRemoveMarker(marker) end
|
||||
|
||||
local indicators = {}
|
||||
function ide:AddIndicator(indic, num)
|
||||
num = num or indicators[indic]
|
||||
if not num then -- new indicator; find the smallest available number
|
||||
local nums = {}
|
||||
for _, indicator in pairs(indicators) do
|
||||
if indicator >= wxstc.wxSTC_INDIC_CONTAINER then
|
||||
nums[indicator-wxstc.wxSTC_INDIC_CONTAINER+1] = true
|
||||
end
|
||||
end
|
||||
num = #nums + wxstc.wxSTC_INDIC_CONTAINER
|
||||
if num > wxstc.wxSTC_INDIC_MAX then return end
|
||||
end
|
||||
indicators[indic] = num
|
||||
return num
|
||||
end
|
||||
function ide:GetIndicator(indic) return indicators[indic] end
|
||||
function ide:GetIndicators() return indicators end
|
||||
function ide:RemoveIndicator(indic) indicators[indic] = nil end
|
||||
|
||||
-- this provides a simple stack for saving/restoring current configuration
|
||||
local configcache = {}
|
||||
function ide:AddConfig(name, files)
|
||||
if not name or configcache[name] then return end -- don't overwrite existing slots
|
||||
if type(files) ~= "table" then files = {files} end -- allow to pass one value
|
||||
configcache[name] = {
|
||||
config = require('mobdebug').dump(self.config, {nocode = true}),
|
||||
configmeta = getmetatable(self.config),
|
||||
packages = {},
|
||||
}
|
||||
-- build a list of existing packages
|
||||
local packages = {}
|
||||
for package in pairs(self.packages) do packages[package] = true end
|
||||
-- load config file(s)
|
||||
for _, file in pairs(files) do LoadLuaConfig(MergeFullPath(name, file)) end
|
||||
-- register newly added packages (if any)
|
||||
for package in pairs(self.packages) do
|
||||
if not packages[package] then -- this is a newly added package
|
||||
PackageEventHandleOne(package, "onRegister")
|
||||
configcache[name].packages[package] = true
|
||||
end
|
||||
end
|
||||
ReApplySpecAndStyles() -- apply current config to the UI
|
||||
end
|
||||
function ide:RemoveConfig(name)
|
||||
if not name or not configcache[name] then return end
|
||||
-- unregister cached packages
|
||||
for package in pairs(configcache[name].packages) do PackageUnRegister(package) end
|
||||
-- load original config
|
||||
local ok, res = LoadSafe(configcache[name].config)
|
||||
if ok then
|
||||
self.config = res
|
||||
if configcache[name].configmeta then setmetatable(self.config, configcache[name].configmeta) end
|
||||
else
|
||||
DisplayOutputLn(("Error while restoring configuration: '%s'."):format(res))
|
||||
end
|
||||
configcache[name] = nil -- clear the slot after use
|
||||
ReApplySpecAndStyles() -- apply current config to the UI
|
||||
end
|
||||
|
||||
local panels = {}
|
||||
function ide:AddPanel(ctrl, panel, name, conf)
|
||||
local width, height = 360, 200
|
||||
local notebook = wxaui.wxAuiNotebook(self.frame, wx.wxID_ANY,
|
||||
wx.wxDefaultPosition, wx.wxDefaultSize,
|
||||
wxaui.wxAUI_NB_DEFAULT_STYLE + wxaui.wxAUI_NB_TAB_EXTERNAL_MOVE
|
||||
- wxaui.wxAUI_NB_CLOSE_ON_ACTIVE_TAB + wx.wxNO_BORDER)
|
||||
notebook:AddPage(ctrl, name, true)
|
||||
notebook:Connect(wxaui.wxEVT_COMMAND_AUINOTEBOOK_BG_DCLICK,
|
||||
function() PaneFloatToggle(notebook) end)
|
||||
notebook:Connect(wxaui.wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSE,
|
||||
function(event) event:Veto() end)
|
||||
|
||||
local mgr = self.frame.uimgr
|
||||
mgr:AddPane(notebook, wxaui.wxAuiPaneInfo():
|
||||
Name(panel):Float():CaptionVisible(false):PaneBorder(false):
|
||||
MinSize(width/2,height/2):
|
||||
BestSize(width,height):FloatingSize(width,height):
|
||||
PinButton(true):Hide())
|
||||
if type(conf) == "function" then conf(mgr:GetPane(panel)) end
|
||||
mgr.defaultPerspective = mgr:SavePerspective() -- resave default perspective
|
||||
|
||||
panels[name] = {ctrl, panel, name, conf}
|
||||
return mgr:GetPane(panel), notebook
|
||||
end
|
||||
|
||||
function ide:RemovePanel(panel)
|
||||
local mgr = self.frame.uimgr
|
||||
local pane = mgr:GetPane(panel)
|
||||
if pane:IsOk() then
|
||||
local win = pane.window
|
||||
mgr:DetachPane(win)
|
||||
win:Destroy()
|
||||
mgr:Update()
|
||||
end
|
||||
end
|
||||
|
||||
function ide:AddPanelDocked(notebook, ctrl, panel, name, conf, activate)
|
||||
notebook:AddPage(ctrl, name, activate ~= false)
|
||||
panels[name] = {ctrl, panel, name, conf}
|
||||
return notebook
|
||||
end
|
||||
function ide:IsPanelDocked(panel)
|
||||
local layout = self:GetSetting("/view", "uimgrlayout")
|
||||
return layout and not layout:find(panel)
|
||||
end
|
||||
|
||||
function ide:IsValidCtrl(ctrl)
|
||||
return ctrl and pcall(function() ctrl:GetId() end)
|
||||
end
|
||||
|
||||
function ide:IsValidProperty(ctrl, prop)
|
||||
return ide:IsValidCtrl(ctrl) and pcall(function() return ctrl[prop] end)
|
||||
end
|
||||
|
||||
function ide:IsWindowShown(win)
|
||||
while win do
|
||||
if not win:IsShown() then return false end
|
||||
win = win:GetParent()
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function ide:RestorePanelByLabel(name)
|
||||
if not panels[name] then return end
|
||||
return self:AddPanel(unpack(panels[name]))
|
||||
end
|
||||
|
||||
function ide:AddTool(name, command, updateui)
|
||||
return ToolsAddTool(name, command, updateui)
|
||||
end
|
||||
|
||||
function ide:RemoveTool(name)
|
||||
return ToolsRemoveTool(name)
|
||||
end
|
Reference in New Issue
Block a user