Login Register






[Lua] Npad Text Editor filter_list
Author
Message
[Lua] Npad Text Editor #1
Latest version: 0.54 (unstable)
Latest stable version: none

I quite like the way it is turning out except for many text/settings editing functions which I'm having tons of troubles with.

Some functionalities so far:

[*] Open new file/load file/save file/save copy/rename currently opened file
[*] Change font, font color, font size, window background color, textarea background color (you need to restart the program to see the changes)
[*] Drap 'n' drop files to open them
[*] Open files from CLI (wlua npad.wlua myfile.txt anotherfile.txt) (I made this to be able to add a "Edit with Npad" option in the Windows right click menu)
[*] Display current line/column position and number of total lines, words and characters
[*] Undo, redo, go back to last saved copy
[*] Find function finally working (only downwards)
[*] Replace function
[*] Convert tabs/spaces

Known bugs:

[*] Find function does not work properly when used to search upwards
[*] The whole settings thing isn't working properly

Todo:

[*] Improve find and replace functions by adding matching-text selection, find-next (F3) functions
note: text selection in Lua:
Code:
iup.text.selection = "line,col:line,col"

[*] Add a Select all option in the Edit menu

The default font used is Dejavu Sans Mono, which I'd recommend you to download in case you don't have it. If you don't have it, you could be viewing the textarea's text in Arial, or whatever (, I don't know), but you can change the font in the settings to whatever you like.

Here are the shortcuts (OS provided ones like CTRL + A in the textarea to select all are not included but are available, of course - the ones mentioned, like CTRL + Z to undo, have been replaced with custom functions instead):

Code:
CTRL + N New CTRL + R Rename CTRL + O Open CTRL + S Save ALT + S Save as CTRL + P Save a copy CTRL + Z Undo CTRL + Y Redo CTRL + F Find CTRL + H Replace F1 Help F2 Shortcuts F3 About CTRL + Q Quit

The source code:

Code:
-------------------------------------------------------------------------------- -- -- -- Npad -- -- -- -------------------------------------------------------------------------------- -- -- -- You're allowed to edit and/or give away this code for free as long as you -- -- give credits. -- -- -- -------------------------------------------------------------------------------- -- -- -- npad · coded by noize · 2013 -- -- -- -------------------------------------------------------------------------------- require("iuplua") require("iupluacontrols") -- needed for the color browser dialog require("strbuf") require("list") require("socket") local npad = {} npad.version = "0.54" npad.versioninfo = "(unstable)" -------------------------------------------------------------------------------- -- custom alert function -- like iup.Message() but uses an error dialog instead -------------------------------------------------------------------------------- function alert(title,body,flag) local dlg = iup.messagedlg { dialogtype = flag, title = title, value = body } dlg:popup() dlg:destroy() end -------------------------------------------------------------------------------- -- hotkeys -------------------------------------------------------------------------------- npad.key = {} npad.key.new = 590 -- CTRL + N npad.key.ren = 594 -- CTRL + R npad.key.open = 591 -- CTRL + O npad.key.save = 595 -- CTRL + S npad.key.saveas = 851 -- ALT + S npad.key.savecopy = 592 -- CTRL + P npad.key.undo = 602 -- CTRL + Z npad.key.redo = 601 -- CTRL + Y npad.key.backup = 578 -- CTRL + B npad.key.copy = 579 -- CTRL + C npad.key.cut = 600 -- CTRL + X npad.key.paste = 598 -- CTRL + V npad.key.find = 582 -- CTRL + F npad.key.replace = 584 -- CTRL + H npad.key.convert = 596 -- CTRL + T npad.key.top = 642 -- CTRL + Arrow Up npad.key.bottom = 648 -- CTRL + Arrow Down npad.key.help = 143 -- F1 npad.key.shortcuts = 144 -- F2 npad.key.about = 145 -- F3 npad.key.quit = 593 -- CTRL + Q -------------------------------------------------------------------------------- -- initialise file name, status, values, checks and settings -------------------------------------------------------------------------------- npad.file = "untitled" npad.backup = {} npad.saved = 1 npad.font = "Dejavu Sans Mono" npad.font_size = 10 npad.temp = {} npad.settings = "npad.ini" local f = io.open(npad.settings,"r") if f then for l in io.lines(npad.settings) do local _, _, key, value = string.find(l, "(%a+)%s*=%s*(%a+)") if value then if key == "font-name" then npad.font = value elseif key == "font-color" then npad.color = value elseif key == "font-size" then npad.font_size = value elseif key == "textarea-background" then npad.textbg = value elseif key == "window-background" then npad.background = value elseif key == "tab-length" then npad.tab_length = value end end end f:close() end -------------------------------------------------------------------------------- -- dialog elements -------------------------------------------------------------------------------- npad.text = iup.multiline { expand = "YES", appendnewline = "NO", tabsize = npad.tab_length, size = "200x120" } if npad.color then npad.text.fgcolor = npad.color end if npad.textbg then npad.text.bgcolor = npad.textbg end npad.text.font = npad.font .. " , " .. npad.font_size npad.tot = {} npad.line = 1 npad.column = 1 npad.tot.ln = 1 npad.tot.words = 0 npad.tot.chars = 0 npad.pos = iup.label { title = "Ln " .. npad.line .. " Col " .. npad.column .. " | Ln " .. npad.tot.ln .. " Wrd " .. npad.tot.words .. " Ch " .. npad.tot.chars, tip = "[current line] | [whole file]", size = "300x" } npad.line = 1 npad.n = 0 if npad.background then for i in string.gmatch(npad.background,"%S+") do npad.n = npad.n + i end if npad.n < (40 * 3) then npad.pos.fgcolor = "255 255 255" end end -------------------------------------------------------------------------------- -- file handling -------------------------------------------------------------------------------- function npad.close() if npad.unsaved then local choice if npad.file:len() > 16 then choice = iup.Alarm("Npad - unsaved changes","Do you want to save the changes made to\n" .. npad.file .. "?","Yes","No","Cancel") else choice = iup.Alarm("Npad - unsaved changes","Do you want to save the changes made to " .. npad.file .. "?","Yes","No","Cancel") end if choice == 1 then save() elseif choice == 3 then iup.MainLoop() end end end function new() npad.close() npad.text.value = "" npad.wnd.title = "Npad" npad.file = "untitled" npad.latest = npad.wnd.title npad.backup = {} npad.saved = 1 npad.unsaved = false end function ren() if not npad.saved then alert("Npad - rename","No file currently opened","INFORMATION") return end local dlg = iup.filedlg { dialogtype = "SAVE", title = "Npad - Rename currently opened file", nochangedir = "NO", directory = npad.lastdir, filter = "*.*", filterinfo = "All files", allownew = "YES", } dlg:popup(iup.LEFT, iup.LEFT) local status = dlg.status local curf = npad.file npad.file = dlg.value npad.lastdir = dlg.directory npad.wnd.title = "Npad - " .. npad.file dlg:destroy() if status ~= "-1" then if not npad.file then alert("Npad","Failed to save to file " .. npad.file , "ERROR") return end os.rename(curf,npad.file) end end function npad.load(lfile) if not lfile then return nil end local f = io.open(lfile, "r") if not f then alert("Npad","Failed to load file " .. lfile , "ERROR") return else npad.text.value = f:read("*a") f:close() npad.file = lfile npad.wnd.title = "Npad - " .. npad.file npad.saved = true npad.backup = npad.text.value if npad.file:find("\\") then npad.tempf = string.split(npad.file,"\\") elseif npad.file:find("/") then npad.tempf = string.split(npad.file,"/") else npad.lastdir = "." npad.tempf = nil end if npad.tempf then table.remove(npad.tempf,#npad.tempf) npad.lastdir = table.concat(npad.tempf,"/") end end end function open() local dlg = iup.filedlg { dialogtype = "OPEN", title = "Npad - Load file", nochangedir = "NO", directory = npad.lastdir, filter = "*.*", filterinfo = "All Files", allownew = "NO", } dlg:popup(iup.CENTER, iup.CENTER) local lfile = dlg.value local status = dlg.status npad.file = lfile npad.lastdir = dlg.directory dlg:destroy() if status == 1 then alert("Npad","Failed to load file " .. lfile , "ERROR") return elseif status ~= -1 then npad.load(lfile) end npad.latest = npad.wnd.title npad.backup = {} npad.saved = 1 npad.unsaved = false end function save() if not npad.unsaved then local f = io.open(npad.file, "w") if f then f:write(npad.text.value) f:close() npad.latest = npad.text.value npad.unsaved = false else alert("Npad","Failed to save file " .. npad.file , "ERROR") return end else saveas() end end function saveas() local dlg = iup.filedlg { dialogtype = "SAVE", title = "Npad - Save file", nochangedir = "NO", directory = npad.lastdir, filter = "*.*", filterinfo = "All files", allownew = "YES", } dlg:popup(iup.LEFT, iup.LEFT) local status = dlg.status npad.file = dlg.value npad.lastdir = dlg.directory dlg:destroy() if status ~= "-1" then if not npad.file then alert("Npad","Failed to save to file " .. npad.file , "ERROR") return end local f = io.open(npad.file, "w") if f then f:write(npad.text.value) f:close() npad.latest = npad.text.value npad.unsaved = false npad.wnd.title = "Npad - " .. npad.file else alert("Npad","Failed to write to file " .. npad.file , "ERROR") end end end function savecopy() local dlg = iup.filedlg { dialogtype = "SAVE", title = "Npad - Save copy", nochangedir = "NO", directory = npad.lastdir, filter = "*.*", filterinfo = "All files", allownew = "YES", } dlg:popup(iup.LEFT, iup.LEFT) local status = dlg.status local file = dlg.value npad.lastdir = dlg.directory dlg:destroy() if status ~= "-1" then if not file then alert("Npad","Failed to save to file " .. file , "ERROR") return end local f = io.open(file, "w") if f then f:write(npad.text.value) f:close() npad.latest = npad.text.value npad.unsaved = false else alert("Npad","Failed to write to file " .. file , "ERROR") end end end function quit() npad.close() os.exit() end -------------------------------------------------------------------------------- -- text editing functions -------------------------------------------------------------------------------- function undo() local pos = npad.text.caret npad.saved = npad.saved - 1 npad.text.value = npad.backup[npad.saved] npad.text.caret = pos end function redo() if npad.saved < #npad.backup then local pos = npad.text.caret npad.saved = npad.saved + 1 npad.text.value = npad.backup[npad.saved] npad.text.caret = pos end end function backup() npad.text.value = npad.latest end function copy() npad.text.clipboard = "COPY" end function cut() npad.text.clipboard = "CUT" end function paste() npad.text.clipboard = "PASTE" end function remove() npad.text.clipboard = "CLEAR" end function find() local match = {} function found(l) iup.Message("Npad - Line " .. npad.temp, l) match.found = true end local findBox = iup.text {expand = "HORIZONTAL"} local down = iup.toggle {title = "Down"} local top = iup.toggle {title = "Down from top"} local up = iup.toggle {title = "Up"} local bottom = iup.toggle {title = "Up from bottom"} local ok = iup.button { title = "Ok", size = "35x", action = function () if (findBox.value == "" or not npad.text.value:find(findBox.value)) then return end local lines = npad.text.value:split("\n") if down.value == "ON" or top.value == "ON" then npad.temp = 1 for k,l in pairs(lines) do if l:find(findBox.value) then if top.value == "ON" then -- down from top found(l) break elseif npad.line < npad.temp then -- if direction is "down" take only lines beneath current line found(l) break end end npad.temp = npad.temp + 1 end else npad.temp = #lines for k,l in pairs(table.sort(lines,function(x,y) return x > y end)) do if l:find(findBox.value) then if bottom.value == "ON" then -- up from bottom found(l) break elseif npad.line > npad.temp then -- if direction is "up" take only lines above current line found(l) break end end npad.temp = npad.temp - 1 end end if not match.found then alert("Npad - no matches found","No results for your search\nTry changing searching direction","INFORMATION") end end } local cancel = iup.button { title = "Cancel", size = "50x", action = function () close() end } local dlg = iup.dialog { iup.vbox { iup.hbox { margin = "3x3", iup.label {title = "Find: "}, findBox, gap = 3 }, iup.hbox { margin = "3x3", iup.label {title = "Direction: "}, iup.radio { iup.vbox { iup.hbox { down, top, gap = 15 }, iup.hbox { up, bottom, gap = 31 }, margin = "3x0" } } }, iup.hbox { iup.fill {}, ok, cancel, iup.fill {} } }, defaultenter = ok, title = "Npad - Find", margin = "2x2", size = "250x", topmost = "YES" } function close() dlg:destroy() end dlg:showxy(iup.CENTER,iup.CENTER) iup.MainLoop() end function replace() local textbox = iup.text {expand = "HORIZONTAL"} local replace = iup.text {expand = "HORIZONTAL"} local toggle = {} toggle[1] = iup.toggle {title = "Escape sequences", tip = "Accepted sequences are \"\\n\" and \"\\t\"", value = "ON"} toggle[2] = iup.toggle {title = "Escape sequences", tip = "Accepted sequences are \"\\n\" and \"\\t\"", value = "ON"} local ok = iup.button { title = "Ok", size = "35x", action = function () local text = {} text[1] = textbox.value text[2] = replace.value for i = 1, #toggle do if toggle[i].value == "ON" then text[i] = text[i]:gsub("\\n","\n") text[i] = text[i]:gsub("\\t","\t") end end npad.text.value = npad.text.value:gsub(text[1],text[2]) end } local cancel = iup.button { title = "Cancel", size = "50x", action = function () close() end } local dlg = iup.dialog { iup.vbox { iup.hbox { iup.label {title = "Replace: "}, textbox, toggle[1], gap = 3 }, iup.hbox { iup.label {title = "With: "}, -- a more elegant solution, using gap or margin would hardly fit the look this well replace, toggle[2], gap = 3 }, iup.hbox { iup.fill {}, ok, cancel, iup.fill {} }, }, defaultenter = ok, title = "Npad - Replace", margin = "2x2", size = "250x", topmost = "YES" } function close() dlg:destroy() end dlg:showxy(iup.CENTER,iup.CENTER) iup.MainLoop() end function convert() local ok, choice = iup.GetParam("Npad", nil, "Convert: %l|Tabs to spaces|Spaces to tabs|\n", 0) if ok then if choice == 0 then local ok, times = iup.GetParam("Npad - Tabs to spaces", nil, "Number of spaces: %i\n", 4) if ok then local ws = " " npad.text.value = npad.text.value:gsub("\t",ws:rep(times)) end elseif choice == 1 then local ok, times = iup.GetParam("Npad - Spaces to tabs", nil, "Number of spaces: %i\n", 4) if ok then local ws = " " npad.text.value = npad.text.value:gsub(ws:rep(times),"\t") end end end end function top() npad.text.value = npad.text.value end function bottom() npad.text.append = "" end -------------------------------------------------------------------------------- -- settings -------------------------------------------------------------------------------- function npad.set(obj,value) local o = obj:gsub("%%%-","%_") npad[o] = value npad.text[o] = npad[o] npad.temp = {} local f = io.open(npad.settings,"r") if f then -- if the file exists and is readable local cont = f:read("*a") if string.find(cont,obj) then -- if the element is already defined in the file for l in io.lines(npad.settings) do -- parse each line if l:find(obj) and l:find("%=") then -- if any line defines the element table.insert(npad.temp,obj .. " = " .. npad[o]) -- redefine it else -- if the current line does not define the element table.insert(npad.temp,l) -- keep it as it is end end npad.setflag = "w" else -- if the element is not defined yet in the file npad.temp = {obj .. " = " .. npad[o]} npad.setflag = "a" end f:close() else -- no file exists, or it is not readable npad.temp = {obj .. " = " .. npad[o]} npad.setflag = "w" end local f = io.open(npad.settings,npad.setflag) if f then f:write(string.gsub(table.concat(npad.temp,"\n"),"%%","") .. "\n") f:close() local f = io.open("temp.lua","w") f:write("dofile(\"" .. arg[0] .. "\")") f:close() npad.wnd.hidetaskbar = "YES" dofile("temp.lua") else alert("Npad","Failed to save settings to file " .. npad.settings , "ERROR") end end function font() local textbox = iup.text {value = npad.font, expand = "HORIZONTAL",} local ok = iup.button {title = "Ok", size = "40x",} local apply = iup.button {title = "Apply", size = "40x",} local cancel = iup.button {title = "Cancel", size = "40x",} local dlg = iup.dialog { iup.vbox { iup.hbox { iup.label {title = "Font:", size = "30x",}, textbox, gap = "5", }, iup.hbox { iup.fill{}, ok, apply, cancel, iup.fill{}, }, margin = "2x2", }, defaultenter = ok, defaultesc = cancel, size = "200x50", title = "Font settings", topmost = "YES" } function ok:action() npad.set("font%-name",textbox.value) dlg:destroy() end function apply:action() npad.set("font%-name",textbox.value) end function cancel:action() dlg:destroy() end dlg:show() end function font_size() local ok, value = iup.GetParam("Npad", nil, "Font size: %i\n", npad.font_size) if ok ~= 0 and value then npad.set("font%-size",value) end end function color(element) if element == "font%-color" then npad.e = "color" elseif element == "textarea%-background" then npad.e = "textbg" elseif element == "window%-background" then npad.e = "background" end if npad[npad.e] then npad.temp.split = string.split(npad[npad.e]," ") else npad.temp.split = {255,255,255} end local r, g, b = iup.GetColor(iup.CENTER, iup.CENTER, npad.temp.split[1], npad.temp.split[2], npad.temp.split[3]) if r then local color = r .. " " .. g .. " " .. b npad.set(element,color) npad.textbg = color npad.wnd:popup() end end function tab_length() local ok, value = iup.GetParam("Npad", nil, "Tab length: %i\n", npad.tab_length) if ok ~= 0 and value then npad.set("tab%-length",value) npad.text.tabsize = value end end function default(obj) npad.set(obj,"") end -------------------------------------------------------------------------------- -- help functions -------------------------------------------------------------------------------- function help() alert("Gimme time","This is supposed to be the beta version, so calm down","ERROR") -- toadd: -- font suggestions in case of lack of installation of default font end function shortcuts() alert("Mh-mh","This is supposed to be the beta version, so calm down","ERROR") end function about() alert("Well","If this is not anymore the beta version, and I still haven't written this function, feel free to get upset","ERROR") end function version() alert("Npad - " .. npad.version , "Current version is " .. npad.version .. " " .. npad.versioninfo,"INFORMATION") end function updatescheck() local s = socket.connect("www.intosec.org",80) if not s then alert("Npad - Error","You need an internet connection to check for updates","ERROR") return end s:send("GET /index.php/topic,549.msg1459.html HTTP/1.0\r\nUser-Agent: Npad/1337\r\n\r\n") while true do local data, status, partial = s:receive(1024) local data = data or partial if data:find(" version: ") then local split = data:split("Latest version: ") local split = split[2]:split("%)") local latest = split[1] .. ")" local split = data:split("Latest stable version: ") local split = split[2]:split("<") local stable = split[1] alert( "Npad - Check for updates", "Current version is " .. npad.version .. " " .. npad.versioninfo .. "\n" .. "Latest version release is " .. latest .. "\n" .. "Latest stable release is " .. stable .. "\n\n" .. "You can download the most recent versions here:\nwww.intosec.org/index.php/topic,549.msg1459.html", "INFORMATION" ) end if status == "closed" then break end end s:close() end -------------------------------------------------------------------------------- -- main dialog objects -------------------------------------------------------------------------------- npad.vbox = iup.vbox { iup.vbox { npad.text, iup.hbox { npad.pos, margin = "1x-13", }, }, margin = "3x3", gap = "17", } npad.menu = iup.menu { iup.submenu { title = "&File", -- the "&" character identifies the ALT hotkey character iup.menu { iup.item { title = "&New\tCTRL + N", action = "new()", }, iup.item { title = "&Rename...\tCTRL + R", action = "ren()", }, iup.item { title = "&Open...\tCTRL + O", action = "open()", }, iup.item { title = "&Save\tCTRL + S", action = "save()", }, iup.item { title = "S&ave as...\tALT + S", action = "saveas()", }, iup.item { title = "Save a &copy...\tCTRL + P", action = "savecopy()", }, iup.separator{}, iup.item { title = "E&xit\tCTRL + Q", action = "quit()", }, }, }, iup.submenu { title = "&Edit", iup.menu { iup.item { title = "&Undo\tCTRL + Z", action = "undo()", }, iup.item { title = "&Redo\tCTRL + Y", action = "redo()", }, iup.item { title = "&Backup\tCTRL + B", action = "backup()", }, iup.separator {}, iup.item { title = "&Copy\tCTRL + C", action = "copy()", }, iup.item { title = "Cut\tCTRL + &X", action = "cut()", }, iup.item { title = "&Paste\tCTRL + V", action = "paste()", }, iup.item { title = "&Remove\tDEL", action = "remove()", }, iup.separator {}, iup.item { title = "&Find\tCTRL + F", action = "find()", }, iup.item { title = "&Replace\tCRTL + H", action = "replace()", }, iup.separator {}, iup.item { title = "Convert &tabs/spaces\tCTRL + T", action = "convert()", }, iup.separator {}, iup.item { title = "Go to top\tCTRL + Arrow Up", action = "top()", }, iup.item { title = "Go to bottom\tCTRL + Arrow Down", action = "bottom()", } }, }, iup.submenu { title = "Se&ttings", iup.menu { iup.submenu { title = "&Text", iup.menu { iup.item { title = "&Font", action = "font()", }, iup.item { title = "&Size", action = "font_size()", }, iup.item { title = "&Color", action = "color('font%-color')", }, }, }, iup.submenu { title = "&Background", iup.menu { iup.item { title = "&Textarea", action = "color('textarea%-background')", }, iup.item { title = "&Window", action = "color('window%-background')", }, }, }, iup.submenu { title = "&Default", iup.menu { iup.submenu { title = "&Text", iup.menu { iup.item { title = "&Font", action = "default('font')", }, iup.item { title = "&Size", action = "default('font%-size')", }, iup.item { title = "&Color", action = "default('color')", }, }, }, iup.submenu { title = "&Background", iup.menu { iup.item { title = "&Textarea", action = "default('textarea%-background')", }, iup.item { title = "&Window", action = "default('window%-background')", }, }, }, }, }, }, }, iup.submenu { title = "&Help", iup.menu { iup.item { title = "&Help\tF1", action = "help()", }, iup.item { title = "&Shortcuts\tF2", action = "shortcuts()", }, iup.item { title = "&About\tF3", action = "about()", }, iup.separator {}, iup.item { title = "&Version", action = "version()", }, iup.item { title = "&Check for updates", action = "updatescheck()", }, }, }, } npad.wnd = iup.dialog { npad.vbox, title = "Npad", menu = npad.menu, dragdrop = "YES", size = "440x200" } if npad.background then npad.wnd.background = npad.background end -------------------------------------------------------------------------------- -- event-driven functions -------------------------------------------------------------------------------- function npad.text:k_any(key) -- when any key is pressed in the textarea if npad.text.value ~= npad.backup[npad.saved] then npad.saved = npad.saved + 1 npad.backup[npad.saved] = npad.text.value end for k,v in pairs(npad.key) do if key == npad.key[k] then loadstring(k .. "()")() return iup.IGNORE end end end function npad.text:caret_cb(line, column) -- on cursor move in textarea if npad.text.value ~= npad.latest then -- if text is different from the last saved text npad.wnd.title = "Npad * " .. npad.file -- unsaved changes npad.unsaved = true else npad.wnd.title = "Npad - " .. npad.file -- no unsaved changes npad.unsaved = false end npad.tot.words = 0 npad.tot.chars = 0 npad.tot.ln = #npad.text.value:split("\n") for w in npad.text.value:gmatch("%S+") do npad.tot.words = npad.tot.words + 1 end for w in npad.text.value:gmatch(".") do if w ~= "\n" then npad.tot.chars = npad.tot.chars + 1 end end npad.line = line npad.column = column npad.pos.title = "Ln " .. npad.line .. " Col " .. npad.column .. " | Ln " .. npad.tot.ln .. " Wrd " .. npad.tot.words .. " Ch " .. npad.tot.chars -- update cursor position end function npad.wnd:dropfiles_cb(file, n, x, y) -- drag 'n' drop files wherever on the dialog if n == 0 then -- open only the first file npad.load(file) end end function npad.wnd:close_cb() -- on dialog close quit() end -------------------------------------------------------------------------------- -- show the dialog -------------------------------------------------------------------------------- npad.wnd:show() iup.SetFocus(npad.text) if arg[1] then npad.load(arg[1]) end if arg[2] then for i = 2, #arg do os.execute("lua " .. arg[0] .. " " .. arg[i]) end end iup.MainLoop()

Lua version used: 5.1.

Please, report any bug/suggestion.
My Bitcoin address: 1AtxVsSSG2Z8JfjNy9KNFDUN6haeKr7LiP
Give me money by visiting www.google.com here: http://coin-ads.com/6Ol83U

If you want a Bitcoin URL shortener/advertiser, please, use this referral: http://coin-ads.com/register.php?refid=noize

Reply

[Lua] Npad Text Editor #2
Latest version: 0.54 (unstable)
Latest stable version: none

I quite like the way it is turning out except for many text/settings editing functions which I'm having tons of troubles with.

Some functionalities so far:

[*] Open new file/load file/save file/save copy/rename currently opened file
[*] Change font, font color, font size, window background color, textarea background color (you need to restart the program to see the changes)
[*] Drap 'n' drop files to open them
[*] Open files from CLI (wlua npad.wlua myfile.txt anotherfile.txt) (I made this to be able to add a "Edit with Npad" option in the Windows right click menu)
[*] Display current line/column position and number of total lines, words and characters
[*] Undo, redo, go back to last saved copy
[*] Find function finally working (only downwards)
[*] Replace function
[*] Convert tabs/spaces

Known bugs:

[*] Find function does not work properly when used to search upwards
[*] The whole settings thing isn't working properly

Todo:

[*] Improve find and replace functions by adding matching-text selection, find-next (F3) functions
note: text selection in Lua:
Code:
iup.text.selection = "line,col:line,col"

[*] Add a Select all option in the Edit menu

The default font used is Dejavu Sans Mono, which I'd recommend you to download in case you don't have it. If you don't have it, you could be viewing the textarea's text in Arial, or whatever (, I don't know), but you can change the font in the settings to whatever you like.

Here are the shortcuts (OS provided ones like CTRL + A in the textarea to select all are not included but are available, of course - the ones mentioned, like CTRL + Z to undo, have been replaced with custom functions instead):

Code:
CTRL + N New CTRL + R Rename CTRL + O Open CTRL + S Save ALT + S Save as CTRL + P Save a copy CTRL + Z Undo CTRL + Y Redo CTRL + F Find CTRL + H Replace F1 Help F2 Shortcuts F3 About CTRL + Q Quit

The source code:

Code:
-------------------------------------------------------------------------------- -- -- -- Npad -- -- -- -------------------------------------------------------------------------------- -- -- -- You're allowed to edit and/or give away this code for free as long as you -- -- give credits. -- -- -- -------------------------------------------------------------------------------- -- -- -- npad · coded by noize · 2013 -- -- -- -------------------------------------------------------------------------------- require("iuplua") require("iupluacontrols") -- needed for the color browser dialog require("strbuf") require("list") require("socket") local npad = {} npad.version = "0.54" npad.versioninfo = "(unstable)" -------------------------------------------------------------------------------- -- custom alert function -- like iup.Message() but uses an error dialog instead -------------------------------------------------------------------------------- function alert(title,body,flag) local dlg = iup.messagedlg { dialogtype = flag, title = title, value = body } dlg:popup() dlg:destroy() end -------------------------------------------------------------------------------- -- hotkeys -------------------------------------------------------------------------------- npad.key = {} npad.key.new = 590 -- CTRL + N npad.key.ren = 594 -- CTRL + R npad.key.open = 591 -- CTRL + O npad.key.save = 595 -- CTRL + S npad.key.saveas = 851 -- ALT + S npad.key.savecopy = 592 -- CTRL + P npad.key.undo = 602 -- CTRL + Z npad.key.redo = 601 -- CTRL + Y npad.key.backup = 578 -- CTRL + B npad.key.copy = 579 -- CTRL + C npad.key.cut = 600 -- CTRL + X npad.key.paste = 598 -- CTRL + V npad.key.find = 582 -- CTRL + F npad.key.replace = 584 -- CTRL + H npad.key.convert = 596 -- CTRL + T npad.key.top = 642 -- CTRL + Arrow Up npad.key.bottom = 648 -- CTRL + Arrow Down npad.key.help = 143 -- F1 npad.key.shortcuts = 144 -- F2 npad.key.about = 145 -- F3 npad.key.quit = 593 -- CTRL + Q -------------------------------------------------------------------------------- -- initialise file name, status, values, checks and settings -------------------------------------------------------------------------------- npad.file = "untitled" npad.backup = {} npad.saved = 1 npad.font = "Dejavu Sans Mono" npad.font_size = 10 npad.temp = {} npad.settings = "npad.ini" local f = io.open(npad.settings,"r") if f then for l in io.lines(npad.settings) do local _, _, key, value = string.find(l, "(%a+)%s*=%s*(%a+)") if value then if key == "font-name" then npad.font = value elseif key == "font-color" then npad.color = value elseif key == "font-size" then npad.font_size = value elseif key == "textarea-background" then npad.textbg = value elseif key == "window-background" then npad.background = value elseif key == "tab-length" then npad.tab_length = value end end end f:close() end -------------------------------------------------------------------------------- -- dialog elements -------------------------------------------------------------------------------- npad.text = iup.multiline { expand = "YES", appendnewline = "NO", tabsize = npad.tab_length, size = "200x120" } if npad.color then npad.text.fgcolor = npad.color end if npad.textbg then npad.text.bgcolor = npad.textbg end npad.text.font = npad.font .. " , " .. npad.font_size npad.tot = {} npad.line = 1 npad.column = 1 npad.tot.ln = 1 npad.tot.words = 0 npad.tot.chars = 0 npad.pos = iup.label { title = "Ln " .. npad.line .. " Col " .. npad.column .. " | Ln " .. npad.tot.ln .. " Wrd " .. npad.tot.words .. " Ch " .. npad.tot.chars, tip = "[current line] | [whole file]", size = "300x" } npad.line = 1 npad.n = 0 if npad.background then for i in string.gmatch(npad.background,"%S+") do npad.n = npad.n + i end if npad.n < (40 * 3) then npad.pos.fgcolor = "255 255 255" end end -------------------------------------------------------------------------------- -- file handling -------------------------------------------------------------------------------- function npad.close() if npad.unsaved then local choice if npad.file:len() > 16 then choice = iup.Alarm("Npad - unsaved changes","Do you want to save the changes made to\n" .. npad.file .. "?","Yes","No","Cancel") else choice = iup.Alarm("Npad - unsaved changes","Do you want to save the changes made to " .. npad.file .. "?","Yes","No","Cancel") end if choice == 1 then save() elseif choice == 3 then iup.MainLoop() end end end function new() npad.close() npad.text.value = "" npad.wnd.title = "Npad" npad.file = "untitled" npad.latest = npad.wnd.title npad.backup = {} npad.saved = 1 npad.unsaved = false end function ren() if not npad.saved then alert("Npad - rename","No file currently opened","INFORMATION") return end local dlg = iup.filedlg { dialogtype = "SAVE", title = "Npad - Rename currently opened file", nochangedir = "NO", directory = npad.lastdir, filter = "*.*", filterinfo = "All files", allownew = "YES", } dlg:popup(iup.LEFT, iup.LEFT) local status = dlg.status local curf = npad.file npad.file = dlg.value npad.lastdir = dlg.directory npad.wnd.title = "Npad - " .. npad.file dlg:destroy() if status ~= "-1" then if not npad.file then alert("Npad","Failed to save to file " .. npad.file , "ERROR") return end os.rename(curf,npad.file) end end function npad.load(lfile) if not lfile then return nil end local f = io.open(lfile, "r") if not f then alert("Npad","Failed to load file " .. lfile , "ERROR") return else npad.text.value = f:read("*a") f:close() npad.file = lfile npad.wnd.title = "Npad - " .. npad.file npad.saved = true npad.backup = npad.text.value if npad.file:find("\\") then npad.tempf = string.split(npad.file,"\\") elseif npad.file:find("/") then npad.tempf = string.split(npad.file,"/") else npad.lastdir = "." npad.tempf = nil end if npad.tempf then table.remove(npad.tempf,#npad.tempf) npad.lastdir = table.concat(npad.tempf,"/") end end end function open() local dlg = iup.filedlg { dialogtype = "OPEN", title = "Npad - Load file", nochangedir = "NO", directory = npad.lastdir, filter = "*.*", filterinfo = "All Files", allownew = "NO", } dlg:popup(iup.CENTER, iup.CENTER) local lfile = dlg.value local status = dlg.status npad.file = lfile npad.lastdir = dlg.directory dlg:destroy() if status == 1 then alert("Npad","Failed to load file " .. lfile , "ERROR") return elseif status ~= -1 then npad.load(lfile) end npad.latest = npad.wnd.title npad.backup = {} npad.saved = 1 npad.unsaved = false end function save() if not npad.unsaved then local f = io.open(npad.file, "w") if f then f:write(npad.text.value) f:close() npad.latest = npad.text.value npad.unsaved = false else alert("Npad","Failed to save file " .. npad.file , "ERROR") return end else saveas() end end function saveas() local dlg = iup.filedlg { dialogtype = "SAVE", title = "Npad - Save file", nochangedir = "NO", directory = npad.lastdir, filter = "*.*", filterinfo = "All files", allownew = "YES", } dlg:popup(iup.LEFT, iup.LEFT) local status = dlg.status npad.file = dlg.value npad.lastdir = dlg.directory dlg:destroy() if status ~= "-1" then if not npad.file then alert("Npad","Failed to save to file " .. npad.file , "ERROR") return end local f = io.open(npad.file, "w") if f then f:write(npad.text.value) f:close() npad.latest = npad.text.value npad.unsaved = false npad.wnd.title = "Npad - " .. npad.file else alert("Npad","Failed to write to file " .. npad.file , "ERROR") end end end function savecopy() local dlg = iup.filedlg { dialogtype = "SAVE", title = "Npad - Save copy", nochangedir = "NO", directory = npad.lastdir, filter = "*.*", filterinfo = "All files", allownew = "YES", } dlg:popup(iup.LEFT, iup.LEFT) local status = dlg.status local file = dlg.value npad.lastdir = dlg.directory dlg:destroy() if status ~= "-1" then if not file then alert("Npad","Failed to save to file " .. file , "ERROR") return end local f = io.open(file, "w") if f then f:write(npad.text.value) f:close() npad.latest = npad.text.value npad.unsaved = false else alert("Npad","Failed to write to file " .. file , "ERROR") end end end function quit() npad.close() os.exit() end -------------------------------------------------------------------------------- -- text editing functions -------------------------------------------------------------------------------- function undo() local pos = npad.text.caret npad.saved = npad.saved - 1 npad.text.value = npad.backup[npad.saved] npad.text.caret = pos end function redo() if npad.saved < #npad.backup then local pos = npad.text.caret npad.saved = npad.saved + 1 npad.text.value = npad.backup[npad.saved] npad.text.caret = pos end end function backup() npad.text.value = npad.latest end function copy() npad.text.clipboard = "COPY" end function cut() npad.text.clipboard = "CUT" end function paste() npad.text.clipboard = "PASTE" end function remove() npad.text.clipboard = "CLEAR" end function find() local match = {} function found(l) iup.Message("Npad - Line " .. npad.temp, l) match.found = true end local findBox = iup.text {expand = "HORIZONTAL"} local down = iup.toggle {title = "Down"} local top = iup.toggle {title = "Down from top"} local up = iup.toggle {title = "Up"} local bottom = iup.toggle {title = "Up from bottom"} local ok = iup.button { title = "Ok", size = "35x", action = function () if (findBox.value == "" or not npad.text.value:find(findBox.value)) then return end local lines = npad.text.value:split("\n") if down.value == "ON" or top.value == "ON" then npad.temp = 1 for k,l in pairs(lines) do if l:find(findBox.value) then if top.value == "ON" then -- down from top found(l) break elseif npad.line < npad.temp then -- if direction is "down" take only lines beneath current line found(l) break end end npad.temp = npad.temp + 1 end else npad.temp = #lines for k,l in pairs(table.sort(lines,function(x,y) return x > y end)) do if l:find(findBox.value) then if bottom.value == "ON" then -- up from bottom found(l) break elseif npad.line > npad.temp then -- if direction is "up" take only lines above current line found(l) break end end npad.temp = npad.temp - 1 end end if not match.found then alert("Npad - no matches found","No results for your search\nTry changing searching direction","INFORMATION") end end } local cancel = iup.button { title = "Cancel", size = "50x", action = function () close() end } local dlg = iup.dialog { iup.vbox { iup.hbox { margin = "3x3", iup.label {title = "Find: "}, findBox, gap = 3 }, iup.hbox { margin = "3x3", iup.label {title = "Direction: "}, iup.radio { iup.vbox { iup.hbox { down, top, gap = 15 }, iup.hbox { up, bottom, gap = 31 }, margin = "3x0" } } }, iup.hbox { iup.fill {}, ok, cancel, iup.fill {} } }, defaultenter = ok, title = "Npad - Find", margin = "2x2", size = "250x", topmost = "YES" } function close() dlg:destroy() end dlg:showxy(iup.CENTER,iup.CENTER) iup.MainLoop() end function replace() local textbox = iup.text {expand = "HORIZONTAL"} local replace = iup.text {expand = "HORIZONTAL"} local toggle = {} toggle[1] = iup.toggle {title = "Escape sequences", tip = "Accepted sequences are \"\\n\" and \"\\t\"", value = "ON"} toggle[2] = iup.toggle {title = "Escape sequences", tip = "Accepted sequences are \"\\n\" and \"\\t\"", value = "ON"} local ok = iup.button { title = "Ok", size = "35x", action = function () local text = {} text[1] = textbox.value text[2] = replace.value for i = 1, #toggle do if toggle[i].value == "ON" then text[i] = text[i]:gsub("\\n","\n") text[i] = text[i]:gsub("\\t","\t") end end npad.text.value = npad.text.value:gsub(text[1],text[2]) end } local cancel = iup.button { title = "Cancel", size = "50x", action = function () close() end } local dlg = iup.dialog { iup.vbox { iup.hbox { iup.label {title = "Replace: "}, textbox, toggle[1], gap = 3 }, iup.hbox { iup.label {title = "With: "}, -- a more elegant solution, using gap or margin would hardly fit the look this well replace, toggle[2], gap = 3 }, iup.hbox { iup.fill {}, ok, cancel, iup.fill {} }, }, defaultenter = ok, title = "Npad - Replace", margin = "2x2", size = "250x", topmost = "YES" } function close() dlg:destroy() end dlg:showxy(iup.CENTER,iup.CENTER) iup.MainLoop() end function convert() local ok, choice = iup.GetParam("Npad", nil, "Convert: %l|Tabs to spaces|Spaces to tabs|\n", 0) if ok then if choice == 0 then local ok, times = iup.GetParam("Npad - Tabs to spaces", nil, "Number of spaces: %i\n", 4) if ok then local ws = " " npad.text.value = npad.text.value:gsub("\t",ws:rep(times)) end elseif choice == 1 then local ok, times = iup.GetParam("Npad - Spaces to tabs", nil, "Number of spaces: %i\n", 4) if ok then local ws = " " npad.text.value = npad.text.value:gsub(ws:rep(times),"\t") end end end end function top() npad.text.value = npad.text.value end function bottom() npad.text.append = "" end -------------------------------------------------------------------------------- -- settings -------------------------------------------------------------------------------- function npad.set(obj,value) local o = obj:gsub("%%%-","%_") npad[o] = value npad.text[o] = npad[o] npad.temp = {} local f = io.open(npad.settings,"r") if f then -- if the file exists and is readable local cont = f:read("*a") if string.find(cont,obj) then -- if the element is already defined in the file for l in io.lines(npad.settings) do -- parse each line if l:find(obj) and l:find("%=") then -- if any line defines the element table.insert(npad.temp,obj .. " = " .. npad[o]) -- redefine it else -- if the current line does not define the element table.insert(npad.temp,l) -- keep it as it is end end npad.setflag = "w" else -- if the element is not defined yet in the file npad.temp = {obj .. " = " .. npad[o]} npad.setflag = "a" end f:close() else -- no file exists, or it is not readable npad.temp = {obj .. " = " .. npad[o]} npad.setflag = "w" end local f = io.open(npad.settings,npad.setflag) if f then f:write(string.gsub(table.concat(npad.temp,"\n"),"%%","") .. "\n") f:close() local f = io.open("temp.lua","w") f:write("dofile(\"" .. arg[0] .. "\")") f:close() npad.wnd.hidetaskbar = "YES" dofile("temp.lua") else alert("Npad","Failed to save settings to file " .. npad.settings , "ERROR") end end function font() local textbox = iup.text {value = npad.font, expand = "HORIZONTAL",} local ok = iup.button {title = "Ok", size = "40x",} local apply = iup.button {title = "Apply", size = "40x",} local cancel = iup.button {title = "Cancel", size = "40x",} local dlg = iup.dialog { iup.vbox { iup.hbox { iup.label {title = "Font:", size = "30x",}, textbox, gap = "5", }, iup.hbox { iup.fill{}, ok, apply, cancel, iup.fill{}, }, margin = "2x2", }, defaultenter = ok, defaultesc = cancel, size = "200x50", title = "Font settings", topmost = "YES" } function ok:action() npad.set("font%-name",textbox.value) dlg:destroy() end function apply:action() npad.set("font%-name",textbox.value) end function cancel:action() dlg:destroy() end dlg:show() end function font_size() local ok, value = iup.GetParam("Npad", nil, "Font size: %i\n", npad.font_size) if ok ~= 0 and value then npad.set("font%-size",value) end end function color(element) if element == "font%-color" then npad.e = "color" elseif element == "textarea%-background" then npad.e = "textbg" elseif element == "window%-background" then npad.e = "background" end if npad[npad.e] then npad.temp.split = string.split(npad[npad.e]," ") else npad.temp.split = {255,255,255} end local r, g, b = iup.GetColor(iup.CENTER, iup.CENTER, npad.temp.split[1], npad.temp.split[2], npad.temp.split[3]) if r then local color = r .. " " .. g .. " " .. b npad.set(element,color) npad.textbg = color npad.wnd:popup() end end function tab_length() local ok, value = iup.GetParam("Npad", nil, "Tab length: %i\n", npad.tab_length) if ok ~= 0 and value then npad.set("tab%-length",value) npad.text.tabsize = value end end function default(obj) npad.set(obj,"") end -------------------------------------------------------------------------------- -- help functions -------------------------------------------------------------------------------- function help() alert("Gimme time","This is supposed to be the beta version, so calm down","ERROR") -- toadd: -- font suggestions in case of lack of installation of default font end function shortcuts() alert("Mh-mh","This is supposed to be the beta version, so calm down","ERROR") end function about() alert("Well","If this is not anymore the beta version, and I still haven't written this function, feel free to get upset","ERROR") end function version() alert("Npad - " .. npad.version , "Current version is " .. npad.version .. " " .. npad.versioninfo,"INFORMATION") end function updatescheck() local s = socket.connect("www.intosec.org",80) if not s then alert("Npad - Error","You need an internet connection to check for updates","ERROR") return end s:send("GET /index.php/topic,549.msg1459.html HTTP/1.0\r\nUser-Agent: Npad/1337\r\n\r\n") while true do local data, status, partial = s:receive(1024) local data = data or partial if data:find(" version: ") then local split = data:split("Latest version: ") local split = split[2]:split("%)") local latest = split[1] .. ")" local split = data:split("Latest stable version: ") local split = split[2]:split("<") local stable = split[1] alert( "Npad - Check for updates", "Current version is " .. npad.version .. " " .. npad.versioninfo .. "\n" .. "Latest version release is " .. latest .. "\n" .. "Latest stable release is " .. stable .. "\n\n" .. "You can download the most recent versions here:\nwww.intosec.org/index.php/topic,549.msg1459.html", "INFORMATION" ) end if status == "closed" then break end end s:close() end -------------------------------------------------------------------------------- -- main dialog objects -------------------------------------------------------------------------------- npad.vbox = iup.vbox { iup.vbox { npad.text, iup.hbox { npad.pos, margin = "1x-13", }, }, margin = "3x3", gap = "17", } npad.menu = iup.menu { iup.submenu { title = "&File", -- the "&" character identifies the ALT hotkey character iup.menu { iup.item { title = "&New\tCTRL + N", action = "new()", }, iup.item { title = "&Rename...\tCTRL + R", action = "ren()", }, iup.item { title = "&Open...\tCTRL + O", action = "open()", }, iup.item { title = "&Save\tCTRL + S", action = "save()", }, iup.item { title = "S&ave as...\tALT + S", action = "saveas()", }, iup.item { title = "Save a &copy...\tCTRL + P", action = "savecopy()", }, iup.separator{}, iup.item { title = "E&xit\tCTRL + Q", action = "quit()", }, }, }, iup.submenu { title = "&Edit", iup.menu { iup.item { title = "&Undo\tCTRL + Z", action = "undo()", }, iup.item { title = "&Redo\tCTRL + Y", action = "redo()", }, iup.item { title = "&Backup\tCTRL + B", action = "backup()", }, iup.separator {}, iup.item { title = "&Copy\tCTRL + C", action = "copy()", }, iup.item { title = "Cut\tCTRL + &X", action = "cut()", }, iup.item { title = "&Paste\tCTRL + V", action = "paste()", }, iup.item { title = "&Remove\tDEL", action = "remove()", }, iup.separator {}, iup.item { title = "&Find\tCTRL + F", action = "find()", }, iup.item { title = "&Replace\tCRTL + H", action = "replace()", }, iup.separator {}, iup.item { title = "Convert &tabs/spaces\tCTRL + T", action = "convert()", }, iup.separator {}, iup.item { title = "Go to top\tCTRL + Arrow Up", action = "top()", }, iup.item { title = "Go to bottom\tCTRL + Arrow Down", action = "bottom()", } }, }, iup.submenu { title = "Se&ttings", iup.menu { iup.submenu { title = "&Text", iup.menu { iup.item { title = "&Font", action = "font()", }, iup.item { title = "&Size", action = "font_size()", }, iup.item { title = "&Color", action = "color('font%-color')", }, }, }, iup.submenu { title = "&Background", iup.menu { iup.item { title = "&Textarea", action = "color('textarea%-background')", }, iup.item { title = "&Window", action = "color('window%-background')", }, }, }, iup.submenu { title = "&Default", iup.menu { iup.submenu { title = "&Text", iup.menu { iup.item { title = "&Font", action = "default('font')", }, iup.item { title = "&Size", action = "default('font%-size')", }, iup.item { title = "&Color", action = "default('color')", }, }, }, iup.submenu { title = "&Background", iup.menu { iup.item { title = "&Textarea", action = "default('textarea%-background')", }, iup.item { title = "&Window", action = "default('window%-background')", }, }, }, }, }, }, }, iup.submenu { title = "&Help", iup.menu { iup.item { title = "&Help\tF1", action = "help()", }, iup.item { title = "&Shortcuts\tF2", action = "shortcuts()", }, iup.item { title = "&About\tF3", action = "about()", }, iup.separator {}, iup.item { title = "&Version", action = "version()", }, iup.item { title = "&Check for updates", action = "updatescheck()", }, }, }, } npad.wnd = iup.dialog { npad.vbox, title = "Npad", menu = npad.menu, dragdrop = "YES", size = "440x200" } if npad.background then npad.wnd.background = npad.background end -------------------------------------------------------------------------------- -- event-driven functions -------------------------------------------------------------------------------- function npad.text:k_any(key) -- when any key is pressed in the textarea if npad.text.value ~= npad.backup[npad.saved] then npad.saved = npad.saved + 1 npad.backup[npad.saved] = npad.text.value end for k,v in pairs(npad.key) do if key == npad.key[k] then loadstring(k .. "()")() return iup.IGNORE end end end function npad.text:caret_cb(line, column) -- on cursor move in textarea if npad.text.value ~= npad.latest then -- if text is different from the last saved text npad.wnd.title = "Npad * " .. npad.file -- unsaved changes npad.unsaved = true else npad.wnd.title = "Npad - " .. npad.file -- no unsaved changes npad.unsaved = false end npad.tot.words = 0 npad.tot.chars = 0 npad.tot.ln = #npad.text.value:split("\n") for w in npad.text.value:gmatch("%S+") do npad.tot.words = npad.tot.words + 1 end for w in npad.text.value:gmatch(".") do if w ~= "\n" then npad.tot.chars = npad.tot.chars + 1 end end npad.line = line npad.column = column npad.pos.title = "Ln " .. npad.line .. " Col " .. npad.column .. " | Ln " .. npad.tot.ln .. " Wrd " .. npad.tot.words .. " Ch " .. npad.tot.chars -- update cursor position end function npad.wnd:dropfiles_cb(file, n, x, y) -- drag 'n' drop files wherever on the dialog if n == 0 then -- open only the first file npad.load(file) end end function npad.wnd:close_cb() -- on dialog close quit() end -------------------------------------------------------------------------------- -- show the dialog -------------------------------------------------------------------------------- npad.wnd:show() iup.SetFocus(npad.text) if arg[1] then npad.load(arg[1]) end if arg[2] then for i = 2, #arg do os.execute("lua " .. arg[0] .. " " .. arg[i]) end end iup.MainLoop()

Lua version used: 5.1.

Please, report any bug/suggestion.
My Bitcoin address: 1AtxVsSSG2Z8JfjNy9KNFDUN6haeKr7LiP
Give me money by visiting www.google.com here: http://coin-ads.com/6Ol83U

If you want a Bitcoin URL shortener/advertiser, please, use this referral: http://coin-ads.com/register.php?refid=noize

Reply

[Lua] Npad Text Editor #3
Latest version: 0.54 (unstable)
Latest stable version: none

I quite like the way it is turning out except for many text/settings editing functions which I'm having tons of troubles with.

Some functionalities so far:

[*] Open new file/load file/save file/save copy/rename currently opened file
[*] Change font, font color, font size, window background color, textarea background color (you need to restart the program to see the changes)
[*] Drap 'n' drop files to open them
[*] Open files from CLI (wlua npad.wlua myfile.txt anotherfile.txt) (I made this to be able to add a "Edit with Npad" option in the Windows right click menu)
[*] Display current line/column position and number of total lines, words and characters
[*] Undo, redo, go back to last saved copy
[*] Find function finally working (only downwards)
[*] Replace function
[*] Convert tabs/spaces

Known bugs:

[*] Find function does not work properly when used to search upwards
[*] The whole settings thing isn't working properly

Todo:

[*] Improve find and replace functions by adding matching-text selection, find-next (F3) functions
note: text selection in Lua:
Code:
iup.text.selection = "line,col:line,col"

[*] Add a Select all option in the Edit menu

The default font used is Dejavu Sans Mono, which I'd recommend you to download in case you don't have it. If you don't have it, you could be viewing the textarea's text in Arial, or whatever (, I don't know), but you can change the font in the settings to whatever you like.

Here are the shortcuts (OS provided ones like CTRL + A in the textarea to select all are not included but are available, of course - the ones mentioned, like CTRL + Z to undo, have been replaced with custom functions instead):

Code:
CTRL + N New CTRL + R Rename CTRL + O Open CTRL + S Save ALT + S Save as CTRL + P Save a copy CTRL + Z Undo CTRL + Y Redo CTRL + F Find CTRL + H Replace F1 Help F2 Shortcuts F3 About CTRL + Q Quit

The source code:

Code:
-------------------------------------------------------------------------------- -- -- -- Npad -- -- -- -------------------------------------------------------------------------------- -- -- -- You're allowed to edit and/or give away this code for free as long as you -- -- give credits. -- -- -- -------------------------------------------------------------------------------- -- -- -- npad · coded by noize · 2013 -- -- -- -------------------------------------------------------------------------------- require("iuplua") require("iupluacontrols") -- needed for the color browser dialog require("strbuf") require("list") require("socket") local npad = {} npad.version = "0.54" npad.versioninfo = "(unstable)" -------------------------------------------------------------------------------- -- custom alert function -- like iup.Message() but uses an error dialog instead -------------------------------------------------------------------------------- function alert(title,body,flag) local dlg = iup.messagedlg { dialogtype = flag, title = title, value = body } dlg:popup() dlg:destroy() end -------------------------------------------------------------------------------- -- hotkeys -------------------------------------------------------------------------------- npad.key = {} npad.key.new = 590 -- CTRL + N npad.key.ren = 594 -- CTRL + R npad.key.open = 591 -- CTRL + O npad.key.save = 595 -- CTRL + S npad.key.saveas = 851 -- ALT + S npad.key.savecopy = 592 -- CTRL + P npad.key.undo = 602 -- CTRL + Z npad.key.redo = 601 -- CTRL + Y npad.key.backup = 578 -- CTRL + B npad.key.copy = 579 -- CTRL + C npad.key.cut = 600 -- CTRL + X npad.key.paste = 598 -- CTRL + V npad.key.find = 582 -- CTRL + F npad.key.replace = 584 -- CTRL + H npad.key.convert = 596 -- CTRL + T npad.key.top = 642 -- CTRL + Arrow Up npad.key.bottom = 648 -- CTRL + Arrow Down npad.key.help = 143 -- F1 npad.key.shortcuts = 144 -- F2 npad.key.about = 145 -- F3 npad.key.quit = 593 -- CTRL + Q -------------------------------------------------------------------------------- -- initialise file name, status, values, checks and settings -------------------------------------------------------------------------------- npad.file = "untitled" npad.backup = {} npad.saved = 1 npad.font = "Dejavu Sans Mono" npad.font_size = 10 npad.temp = {} npad.settings = "npad.ini" local f = io.open(npad.settings,"r") if f then for l in io.lines(npad.settings) do local _, _, key, value = string.find(l, "(%a+)%s*=%s*(%a+)") if value then if key == "font-name" then npad.font = value elseif key == "font-color" then npad.color = value elseif key == "font-size" then npad.font_size = value elseif key == "textarea-background" then npad.textbg = value elseif key == "window-background" then npad.background = value elseif key == "tab-length" then npad.tab_length = value end end end f:close() end -------------------------------------------------------------------------------- -- dialog elements -------------------------------------------------------------------------------- npad.text = iup.multiline { expand = "YES", appendnewline = "NO", tabsize = npad.tab_length, size = "200x120" } if npad.color then npad.text.fgcolor = npad.color end if npad.textbg then npad.text.bgcolor = npad.textbg end npad.text.font = npad.font .. " , " .. npad.font_size npad.tot = {} npad.line = 1 npad.column = 1 npad.tot.ln = 1 npad.tot.words = 0 npad.tot.chars = 0 npad.pos = iup.label { title = "Ln " .. npad.line .. " Col " .. npad.column .. " | Ln " .. npad.tot.ln .. " Wrd " .. npad.tot.words .. " Ch " .. npad.tot.chars, tip = "[current line] | [whole file]", size = "300x" } npad.line = 1 npad.n = 0 if npad.background then for i in string.gmatch(npad.background,"%S+") do npad.n = npad.n + i end if npad.n < (40 * 3) then npad.pos.fgcolor = "255 255 255" end end -------------------------------------------------------------------------------- -- file handling -------------------------------------------------------------------------------- function npad.close() if npad.unsaved then local choice if npad.file:len() > 16 then choice = iup.Alarm("Npad - unsaved changes","Do you want to save the changes made to\n" .. npad.file .. "?","Yes","No","Cancel") else choice = iup.Alarm("Npad - unsaved changes","Do you want to save the changes made to " .. npad.file .. "?","Yes","No","Cancel") end if choice == 1 then save() elseif choice == 3 then iup.MainLoop() end end end function new() npad.close() npad.text.value = "" npad.wnd.title = "Npad" npad.file = "untitled" npad.latest = npad.wnd.title npad.backup = {} npad.saved = 1 npad.unsaved = false end function ren() if not npad.saved then alert("Npad - rename","No file currently opened","INFORMATION") return end local dlg = iup.filedlg { dialogtype = "SAVE", title = "Npad - Rename currently opened file", nochangedir = "NO", directory = npad.lastdir, filter = "*.*", filterinfo = "All files", allownew = "YES", } dlg:popup(iup.LEFT, iup.LEFT) local status = dlg.status local curf = npad.file npad.file = dlg.value npad.lastdir = dlg.directory npad.wnd.title = "Npad - " .. npad.file dlg:destroy() if status ~= "-1" then if not npad.file then alert("Npad","Failed to save to file " .. npad.file , "ERROR") return end os.rename(curf,npad.file) end end function npad.load(lfile) if not lfile then return nil end local f = io.open(lfile, "r") if not f then alert("Npad","Failed to load file " .. lfile , "ERROR") return else npad.text.value = f:read("*a") f:close() npad.file = lfile npad.wnd.title = "Npad - " .. npad.file npad.saved = true npad.backup = npad.text.value if npad.file:find("\\") then npad.tempf = string.split(npad.file,"\\") elseif npad.file:find("/") then npad.tempf = string.split(npad.file,"/") else npad.lastdir = "." npad.tempf = nil end if npad.tempf then table.remove(npad.tempf,#npad.tempf) npad.lastdir = table.concat(npad.tempf,"/") end end end function open() local dlg = iup.filedlg { dialogtype = "OPEN", title = "Npad - Load file", nochangedir = "NO", directory = npad.lastdir, filter = "*.*", filterinfo = "All Files", allownew = "NO", } dlg:popup(iup.CENTER, iup.CENTER) local lfile = dlg.value local status = dlg.status npad.file = lfile npad.lastdir = dlg.directory dlg:destroy() if status == 1 then alert("Npad","Failed to load file " .. lfile , "ERROR") return elseif status ~= -1 then npad.load(lfile) end npad.latest = npad.wnd.title npad.backup = {} npad.saved = 1 npad.unsaved = false end function save() if not npad.unsaved then local f = io.open(npad.file, "w") if f then f:write(npad.text.value) f:close() npad.latest = npad.text.value npad.unsaved = false else alert("Npad","Failed to save file " .. npad.file , "ERROR") return end else saveas() end end function saveas() local dlg = iup.filedlg { dialogtype = "SAVE", title = "Npad - Save file", nochangedir = "NO", directory = npad.lastdir, filter = "*.*", filterinfo = "All files", allownew = "YES", } dlg:popup(iup.LEFT, iup.LEFT) local status = dlg.status npad.file = dlg.value npad.lastdir = dlg.directory dlg:destroy() if status ~= "-1" then if not npad.file then alert("Npad","Failed to save to file " .. npad.file , "ERROR") return end local f = io.open(npad.file, "w") if f then f:write(npad.text.value) f:close() npad.latest = npad.text.value npad.unsaved = false npad.wnd.title = "Npad - " .. npad.file else alert("Npad","Failed to write to file " .. npad.file , "ERROR") end end end function savecopy() local dlg = iup.filedlg { dialogtype = "SAVE", title = "Npad - Save copy", nochangedir = "NO", directory = npad.lastdir, filter = "*.*", filterinfo = "All files", allownew = "YES", } dlg:popup(iup.LEFT, iup.LEFT) local status = dlg.status local file = dlg.value npad.lastdir = dlg.directory dlg:destroy() if status ~= "-1" then if not file then alert("Npad","Failed to save to file " .. file , "ERROR") return end local f = io.open(file, "w") if f then f:write(npad.text.value) f:close() npad.latest = npad.text.value npad.unsaved = false else alert("Npad","Failed to write to file " .. file , "ERROR") end end end function quit() npad.close() os.exit() end -------------------------------------------------------------------------------- -- text editing functions -------------------------------------------------------------------------------- function undo() local pos = npad.text.caret npad.saved = npad.saved - 1 npad.text.value = npad.backup[npad.saved] npad.text.caret = pos end function redo() if npad.saved < #npad.backup then local pos = npad.text.caret npad.saved = npad.saved + 1 npad.text.value = npad.backup[npad.saved] npad.text.caret = pos end end function backup() npad.text.value = npad.latest end function copy() npad.text.clipboard = "COPY" end function cut() npad.text.clipboard = "CUT" end function paste() npad.text.clipboard = "PASTE" end function remove() npad.text.clipboard = "CLEAR" end function find() local match = {} function found(l) iup.Message("Npad - Line " .. npad.temp, l) match.found = true end local findBox = iup.text {expand = "HORIZONTAL"} local down = iup.toggle {title = "Down"} local top = iup.toggle {title = "Down from top"} local up = iup.toggle {title = "Up"} local bottom = iup.toggle {title = "Up from bottom"} local ok = iup.button { title = "Ok", size = "35x", action = function () if (findBox.value == "" or not npad.text.value:find(findBox.value)) then return end local lines = npad.text.value:split("\n") if down.value == "ON" or top.value == "ON" then npad.temp = 1 for k,l in pairs(lines) do if l:find(findBox.value) then if top.value == "ON" then -- down from top found(l) break elseif npad.line < npad.temp then -- if direction is "down" take only lines beneath current line found(l) break end end npad.temp = npad.temp + 1 end else npad.temp = #lines for k,l in pairs(table.sort(lines,function(x,y) return x > y end)) do if l:find(findBox.value) then if bottom.value == "ON" then -- up from bottom found(l) break elseif npad.line > npad.temp then -- if direction is "up" take only lines above current line found(l) break end end npad.temp = npad.temp - 1 end end if not match.found then alert("Npad - no matches found","No results for your search\nTry changing searching direction","INFORMATION") end end } local cancel = iup.button { title = "Cancel", size = "50x", action = function () close() end } local dlg = iup.dialog { iup.vbox { iup.hbox { margin = "3x3", iup.label {title = "Find: "}, findBox, gap = 3 }, iup.hbox { margin = "3x3", iup.label {title = "Direction: "}, iup.radio { iup.vbox { iup.hbox { down, top, gap = 15 }, iup.hbox { up, bottom, gap = 31 }, margin = "3x0" } } }, iup.hbox { iup.fill {}, ok, cancel, iup.fill {} } }, defaultenter = ok, title = "Npad - Find", margin = "2x2", size = "250x", topmost = "YES" } function close() dlg:destroy() end dlg:showxy(iup.CENTER,iup.CENTER) iup.MainLoop() end function replace() local textbox = iup.text {expand = "HORIZONTAL"} local replace = iup.text {expand = "HORIZONTAL"} local toggle = {} toggle[1] = iup.toggle {title = "Escape sequences", tip = "Accepted sequences are \"\\n\" and \"\\t\"", value = "ON"} toggle[2] = iup.toggle {title = "Escape sequences", tip = "Accepted sequences are \"\\n\" and \"\\t\"", value = "ON"} local ok = iup.button { title = "Ok", size = "35x", action = function () local text = {} text[1] = textbox.value text[2] = replace.value for i = 1, #toggle do if toggle[i].value == "ON" then text[i] = text[i]:gsub("\\n","\n") text[i] = text[i]:gsub("\\t","\t") end end npad.text.value = npad.text.value:gsub(text[1],text[2]) end } local cancel = iup.button { title = "Cancel", size = "50x", action = function () close() end } local dlg = iup.dialog { iup.vbox { iup.hbox { iup.label {title = "Replace: "}, textbox, toggle[1], gap = 3 }, iup.hbox { iup.label {title = "With: "}, -- a more elegant solution, using gap or margin would hardly fit the look this well replace, toggle[2], gap = 3 }, iup.hbox { iup.fill {}, ok, cancel, iup.fill {} }, }, defaultenter = ok, title = "Npad - Replace", margin = "2x2", size = "250x", topmost = "YES" } function close() dlg:destroy() end dlg:showxy(iup.CENTER,iup.CENTER) iup.MainLoop() end function convert() local ok, choice = iup.GetParam("Npad", nil, "Convert: %l|Tabs to spaces|Spaces to tabs|\n", 0) if ok then if choice == 0 then local ok, times = iup.GetParam("Npad - Tabs to spaces", nil, "Number of spaces: %i\n", 4) if ok then local ws = " " npad.text.value = npad.text.value:gsub("\t",ws:rep(times)) end elseif choice == 1 then local ok, times = iup.GetParam("Npad - Spaces to tabs", nil, "Number of spaces: %i\n", 4) if ok then local ws = " " npad.text.value = npad.text.value:gsub(ws:rep(times),"\t") end end end end function top() npad.text.value = npad.text.value end function bottom() npad.text.append = "" end -------------------------------------------------------------------------------- -- settings -------------------------------------------------------------------------------- function npad.set(obj,value) local o = obj:gsub("%%%-","%_") npad[o] = value npad.text[o] = npad[o] npad.temp = {} local f = io.open(npad.settings,"r") if f then -- if the file exists and is readable local cont = f:read("*a") if string.find(cont,obj) then -- if the element is already defined in the file for l in io.lines(npad.settings) do -- parse each line if l:find(obj) and l:find("%=") then -- if any line defines the element table.insert(npad.temp,obj .. " = " .. npad[o]) -- redefine it else -- if the current line does not define the element table.insert(npad.temp,l) -- keep it as it is end end npad.setflag = "w" else -- if the element is not defined yet in the file npad.temp = {obj .. " = " .. npad[o]} npad.setflag = "a" end f:close() else -- no file exists, or it is not readable npad.temp = {obj .. " = " .. npad[o]} npad.setflag = "w" end local f = io.open(npad.settings,npad.setflag) if f then f:write(string.gsub(table.concat(npad.temp,"\n"),"%%","") .. "\n") f:close() local f = io.open("temp.lua","w") f:write("dofile(\"" .. arg[0] .. "\")") f:close() npad.wnd.hidetaskbar = "YES" dofile("temp.lua") else alert("Npad","Failed to save settings to file " .. npad.settings , "ERROR") end end function font() local textbox = iup.text {value = npad.font, expand = "HORIZONTAL",} local ok = iup.button {title = "Ok", size = "40x",} local apply = iup.button {title = "Apply", size = "40x",} local cancel = iup.button {title = "Cancel", size = "40x",} local dlg = iup.dialog { iup.vbox { iup.hbox { iup.label {title = "Font:", size = "30x",}, textbox, gap = "5", }, iup.hbox { iup.fill{}, ok, apply, cancel, iup.fill{}, }, margin = "2x2", }, defaultenter = ok, defaultesc = cancel, size = "200x50", title = "Font settings", topmost = "YES" } function ok:action() npad.set("font%-name",textbox.value) dlg:destroy() end function apply:action() npad.set("font%-name",textbox.value) end function cancel:action() dlg:destroy() end dlg:show() end function font_size() local ok, value = iup.GetParam("Npad", nil, "Font size: %i\n", npad.font_size) if ok ~= 0 and value then npad.set("font%-size",value) end end function color(element) if element == "font%-color" then npad.e = "color" elseif element == "textarea%-background" then npad.e = "textbg" elseif element == "window%-background" then npad.e = "background" end if npad[npad.e] then npad.temp.split = string.split(npad[npad.e]," ") else npad.temp.split = {255,255,255} end local r, g, b = iup.GetColor(iup.CENTER, iup.CENTER, npad.temp.split[1], npad.temp.split[2], npad.temp.split[3]) if r then local color = r .. " " .. g .. " " .. b npad.set(element,color) npad.textbg = color npad.wnd:popup() end end function tab_length() local ok, value = iup.GetParam("Npad", nil, "Tab length: %i\n", npad.tab_length) if ok ~= 0 and value then npad.set("tab%-length",value) npad.text.tabsize = value end end function default(obj) npad.set(obj,"") end -------------------------------------------------------------------------------- -- help functions -------------------------------------------------------------------------------- function help() alert("Gimme time","This is supposed to be the beta version, so calm down","ERROR") -- toadd: -- font suggestions in case of lack of installation of default font end function shortcuts() alert("Mh-mh","This is supposed to be the beta version, so calm down","ERROR") end function about() alert("Well","If this is not anymore the beta version, and I still haven't written this function, feel free to get upset","ERROR") end function version() alert("Npad - " .. npad.version , "Current version is " .. npad.version .. " " .. npad.versioninfo,"INFORMATION") end function updatescheck() local s = socket.connect("www.intosec.org",80) if not s then alert("Npad - Error","You need an internet connection to check for updates","ERROR") return end s:send("GET /index.php/topic,549.msg1459.html HTTP/1.0\r\nUser-Agent: Npad/1337\r\n\r\n") while true do local data, status, partial = s:receive(1024) local data = data or partial if data:find(" version: ") then local split = data:split("Latest version: ") local split = split[2]:split("%)") local latest = split[1] .. ")" local split = data:split("Latest stable version: ") local split = split[2]:split("<") local stable = split[1] alert( "Npad - Check for updates", "Current version is " .. npad.version .. " " .. npad.versioninfo .. "\n" .. "Latest version release is " .. latest .. "\n" .. "Latest stable release is " .. stable .. "\n\n" .. "You can download the most recent versions here:\nwww.intosec.org/index.php/topic,549.msg1459.html", "INFORMATION" ) end if status == "closed" then break end end s:close() end -------------------------------------------------------------------------------- -- main dialog objects -------------------------------------------------------------------------------- npad.vbox = iup.vbox { iup.vbox { npad.text, iup.hbox { npad.pos, margin = "1x-13", }, }, margin = "3x3", gap = "17", } npad.menu = iup.menu { iup.submenu { title = "&File", -- the "&" character identifies the ALT hotkey character iup.menu { iup.item { title = "&New\tCTRL + N", action = "new()", }, iup.item { title = "&Rename...\tCTRL + R", action = "ren()", }, iup.item { title = "&Open...\tCTRL + O", action = "open()", }, iup.item { title = "&Save\tCTRL + S", action = "save()", }, iup.item { title = "S&ave as...\tALT + S", action = "saveas()", }, iup.item { title = "Save a &copy...\tCTRL + P", action = "savecopy()", }, iup.separator{}, iup.item { title = "E&xit\tCTRL + Q", action = "quit()", }, }, }, iup.submenu { title = "&Edit", iup.menu { iup.item { title = "&Undo\tCTRL + Z", action = "undo()", }, iup.item { title = "&Redo\tCTRL + Y", action = "redo()", }, iup.item { title = "&Backup\tCTRL + B", action = "backup()", }, iup.separator {}, iup.item { title = "&Copy\tCTRL + C", action = "copy()", }, iup.item { title = "Cut\tCTRL + &X", action = "cut()", }, iup.item { title = "&Paste\tCTRL + V", action = "paste()", }, iup.item { title = "&Remove\tDEL", action = "remove()", }, iup.separator {}, iup.item { title = "&Find\tCTRL + F", action = "find()", }, iup.item { title = "&Replace\tCRTL + H", action = "replace()", }, iup.separator {}, iup.item { title = "Convert &tabs/spaces\tCTRL + T", action = "convert()", }, iup.separator {}, iup.item { title = "Go to top\tCTRL + Arrow Up", action = "top()", }, iup.item { title = "Go to bottom\tCTRL + Arrow Down", action = "bottom()", } }, }, iup.submenu { title = "Se&ttings", iup.menu { iup.submenu { title = "&Text", iup.menu { iup.item { title = "&Font", action = "font()", }, iup.item { title = "&Size", action = "font_size()", }, iup.item { title = "&Color", action = "color('font%-color')", }, }, }, iup.submenu { title = "&Background", iup.menu { iup.item { title = "&Textarea", action = "color('textarea%-background')", }, iup.item { title = "&Window", action = "color('window%-background')", }, }, }, iup.submenu { title = "&Default", iup.menu { iup.submenu { title = "&Text", iup.menu { iup.item { title = "&Font", action = "default('font')", }, iup.item { title = "&Size", action = "default('font%-size')", }, iup.item { title = "&Color", action = "default('color')", }, }, }, iup.submenu { title = "&Background", iup.menu { iup.item { title = "&Textarea", action = "default('textarea%-background')", }, iup.item { title = "&Window", action = "default('window%-background')", }, }, }, }, }, }, }, iup.submenu { title = "&Help", iup.menu { iup.item { title = "&Help\tF1", action = "help()", }, iup.item { title = "&Shortcuts\tF2", action = "shortcuts()", }, iup.item { title = "&About\tF3", action = "about()", }, iup.separator {}, iup.item { title = "&Version", action = "version()", }, iup.item { title = "&Check for updates", action = "updatescheck()", }, }, }, } npad.wnd = iup.dialog { npad.vbox, title = "Npad", menu = npad.menu, dragdrop = "YES", size = "440x200" } if npad.background then npad.wnd.background = npad.background end -------------------------------------------------------------------------------- -- event-driven functions -------------------------------------------------------------------------------- function npad.text:k_any(key) -- when any key is pressed in the textarea if npad.text.value ~= npad.backup[npad.saved] then npad.saved = npad.saved + 1 npad.backup[npad.saved] = npad.text.value end for k,v in pairs(npad.key) do if key == npad.key[k] then loadstring(k .. "()")() return iup.IGNORE end end end function npad.text:caret_cb(line, column) -- on cursor move in textarea if npad.text.value ~= npad.latest then -- if text is different from the last saved text npad.wnd.title = "Npad * " .. npad.file -- unsaved changes npad.unsaved = true else npad.wnd.title = "Npad - " .. npad.file -- no unsaved changes npad.unsaved = false end npad.tot.words = 0 npad.tot.chars = 0 npad.tot.ln = #npad.text.value:split("\n") for w in npad.text.value:gmatch("%S+") do npad.tot.words = npad.tot.words + 1 end for w in npad.text.value:gmatch(".") do if w ~= "\n" then npad.tot.chars = npad.tot.chars + 1 end end npad.line = line npad.column = column npad.pos.title = "Ln " .. npad.line .. " Col " .. npad.column .. " | Ln " .. npad.tot.ln .. " Wrd " .. npad.tot.words .. " Ch " .. npad.tot.chars -- update cursor position end function npad.wnd:dropfiles_cb(file, n, x, y) -- drag 'n' drop files wherever on the dialog if n == 0 then -- open only the first file npad.load(file) end end function npad.wnd:close_cb() -- on dialog close quit() end -------------------------------------------------------------------------------- -- show the dialog -------------------------------------------------------------------------------- npad.wnd:show() iup.SetFocus(npad.text) if arg[1] then npad.load(arg[1]) end if arg[2] then for i = 2, #arg do os.execute("lua " .. arg[0] .. " " .. arg[i]) end end iup.MainLoop()

Lua version used: 5.1.

Please, report any bug/suggestion.
My Bitcoin address: 1AtxVsSSG2Z8JfjNy9KNFDUN6haeKr7LiP
Give me money by visiting www.google.com here: http://coin-ads.com/6Ol83U

If you want a Bitcoin URL shortener/advertiser, please, use this referral: http://coin-ads.com/register.php?refid=noize

Reply

RE: [Lua] Npad Text Editor 0.2 #4
update: version 0.2 out now.

more functions, more settings, many minor changes.
My Bitcoin address: 1AtxVsSSG2Z8JfjNy9KNFDUN6haeKr7LiP
Give me money by visiting www.google.com here: http://coin-ads.com/6Ol83U

If you want a Bitcoin URL shortener/advertiser, please, use this referral: http://coin-ads.com/register.php?refid=noize

Reply

RE: [Lua] Npad Text Editor 0.3 #5
update: version 0.3 out now.
My Bitcoin address: 1AtxVsSSG2Z8JfjNy9KNFDUN6haeKr7LiP
Give me money by visiting www.google.com here: http://coin-ads.com/6Ol83U

If you want a Bitcoin URL shortener/advertiser, please, use this referral: http://coin-ads.com/register.php?refid=noize

Reply

RE: [Lua] Npad Text Editor 0.3 #6
update: version 0.3 out now.
My Bitcoin address: 1AtxVsSSG2Z8JfjNy9KNFDUN6haeKr7LiP
Give me money by visiting www.google.com here: http://coin-ads.com/6Ol83U

If you want a Bitcoin URL shortener/advertiser, please, use this referral: http://coin-ads.com/register.php?refid=noize

Reply

RE: [Lua] Npad Text Editor #7
update: version 0.3.5 out now.
My Bitcoin address: 1AtxVsSSG2Z8JfjNy9KNFDUN6haeKr7LiP
Give me money by visiting www.google.com here: http://coin-ads.com/6Ol83U

If you want a Bitcoin URL shortener/advertiser, please, use this referral: http://coin-ads.com/register.php?refid=noize

Reply

RE: [Lua] Npad Text Editor #8
update: version 0.3.5 out now.
My Bitcoin address: 1AtxVsSSG2Z8JfjNy9KNFDUN6haeKr7LiP
Give me money by visiting www.google.com here: http://coin-ads.com/6Ol83U

If you want a Bitcoin URL shortener/advertiser, please, use this referral: http://coin-ads.com/register.php?refid=noize

Reply

RE: [Lua] Npad Text Editor #9
Missed to add some version updates over here.

0.4.4 out now.
My Bitcoin address: 1AtxVsSSG2Z8JfjNy9KNFDUN6haeKr7LiP
Give me money by visiting www.google.com here: http://coin-ads.com/6Ol83U

If you want a Bitcoin URL shortener/advertiser, please, use this referral: http://coin-ads.com/register.php?refid=noize

Reply

RE: [Lua] Npad Text Editor #10
Version 0.5 out now.
My Bitcoin address: 1AtxVsSSG2Z8JfjNy9KNFDUN6haeKr7LiP
Give me money by visiting www.google.com here: http://coin-ads.com/6Ol83U

If you want a Bitcoin URL shortener/advertiser, please, use this referral: http://coin-ads.com/register.php?refid=noize

Reply