loadmodule "luaiup" loadmodule "luahandle" loadmodule "lualibs" load "iupe-dbuffer.lua" load "iupe-hexview.lua" load "iupe-hexview-toolbox.lua" load "iupe-tview.lua" if not dalosp then dalosp = {} end if not dalos then dalos = {} end dalos.objects = {} function dalos:register_obj(name, constructor) table.insert(self.objects, { name = name, constructor = constructor }) if self.activemenu then self.activemenu:update_objects() end end dalosp.canvas = { DARK_WHITE = cd.EncodeColor(224, 224, 224), BEZIER_CTRL_LEN = 40, drawlink = function (self, src, dst, si, di) local cv = self.cvdb local iy = function (y) return cv:InvertYAxis(y) end local ctrl_len = dalosp.canvas.BEZIER_CTRL_LEN cv:Foreground(cd.BLACK) local x1, y1, x2, y2 x1 = self.origin.x + src.x + src.w x2 = self.origin.x + dst.x y1 = self.origin.y + src.y + si * src.h / (src.obj.noutputs + 1) y2 = self.origin.y + dst.y + di * src.h / (dst.obj.ninputs + 1) x1, x2, y1, y2 = math.floor(x1 + 0.5), math.floor(x2 + 0.5), math.floor(y1 + 0.5), math.floor(y2 + 0.5) cv:Begin(cd.BEZIER) cv:Vertex(x1, iy(y1)) cv:Vertex(x1 + ctrl_len, iy(y1)) cv:Vertex(x2 - ctrl_len, iy(y2)) cv:Vertex(x2, iy(y2)) cv:End() end, XMENUWIDTH = 100, drawxmenu = function (self, x, y) local cv = self.cvdb local width = dalosp.canvas.XMENUWIDTH local x1, x2, y1, y2 = x - width / 2, x + width / 2, y - width / 2, y + width / 2 x1, x2, y1, y2 = math.floor(x1 + 0.5), math.floor(x2 + 0.5), math.floor(y1 + 0.5), math.floor(y2 + 0.5) local iy = function (y) return cv:InvertYAxis(y) end local cx1, cx2, cy1, cy2 = x - 5, x + 5, y - 5, y + 5 cv:Foreground(dalosp.canvas.DARK_WHITE) cv:Box(x1, x2, iy(y2), iy(y1)) cv:Foreground(cd.GRAY) cv:Line(x1, iy(y1), x2, iy(y2)) cv:Line(x1, iy(y2), x2, iy(y1)) cv:Line(x1 + 1, iy(y1), x2, iy(y2 - 1)) cv:Line(x1, iy(y2 - 1), x2 - 1, iy(y1)) cv:Foreground(cd.WHITE) cv:Line(x1, iy(y1 + 1), x2 - 1, iy(y2)) cv:Line(x1 + 1, iy(y2), x2, iy(y1 + 1)) cv:Line(x1, iy(y1 + 2), x2 - 2, iy(y2)) cv:Line(x1 + 2, iy(y2), x2, iy(y1 + 2)) cv:Foreground(dalosp.canvas.DARK_WHITE) cv:Sector(x, iy(y), 10, 10, 0, 360) cv:Foreground(cd.GRAY) cv:Arc(x, iy(y), 10, 10, 0, 360) cv:Foreground(cd.WHITE) cv:Arc(x + 1, iy(y + 1), 10, 10, 0, 360) cv:Foreground(cd.GRAY) cv:Line(x1, iy(y2), x2, iy(y2)) cv:Line(x1 + 1, iy(y2 - 1), x2 - 1, iy(y2 - 1)) cv:Line(x1 + 2, iy(y2 - 2), x2 - 2, iy(y2 - 2)) cv:Line(x2, iy(y1), x2, iy(y2)) cv:Line(x2 - 1, iy(y1 + 1), x2 - 1, iy(y2 - 1)) cv:Line(x2 - 2, iy(y1 + 2), x2 - 2, iy(y2 - 2)) cv:Foreground(cd.WHITE) cv:Line(x1, iy(y1), x1, iy(y2)) cv:Line(x1 + 1, iy(y1 + 1), x1 + 1, iy(y2 - 1)) cv:Line(x1 + 2, iy(y1 + 2), x1 + 2, iy(y2 - 2)) cv:Line(x1, iy(y1), x2, iy(y1)) cv:Line(x1 + 1, iy(y1 + 1), x2 - 1, iy(y1 + 1)) cv:Line(x1 + 2, iy(y1 + 2), x2 - 2, iy(y1 + 2)) end, draw = function (self) local cvdb = self.cvdb if not cvdb then return iup.DEFAULT end cvdb:Background(dalosp.canvas.DARK_WHITE) cvdb:Clear() for k, v in ipairs(self.objects) do v.obj:draw(cvdb, self.origin.x + v.x, self.origin.y + v.y, v.w, v.h) for oi = 1, v.obj.noutputs do local dest = v.obj.outputs[oi] if dest and dest.obj then self:drawlink(v, dest.obj, oi, dest.ind) end end end if self.stateful.linking then cvdb:Foreground(cd.BLACK) cvdb:Line(self.bx, cvdb:InvertYAxis(self.by), self.ox, cvdb:InvertYAxis(self.oy)) end if self.menu.x then self:drawxmenu(self.menu.x, self.menu.y) end cvdb:Flush() return iup.DEFAULT end, addobj = function (self, obj, x, y, w, h) table.insert(self.objects, { obj = obj, x = x, y = y, w = w, h = h }) self:draw() end, findobj = function (self, x, y) local obj, ind x = x - self.origin.x y = y - self.origin.y for k, v in ipairs(self.objects) do if v.x <= x and (v.x + v.w) >= x and v.y <= y and (v.y + v.h) >= y then obj, ind = v, k end end return obj, ind end, moveobj = function (self, obj, dx, dy) obj.x, obj.y = obj.x + dx, obj.y + dy self:draw() end, setobjplace = function (self, obj, x, y) obj.x, obj.y = x, y self:draw() end, panning = function (self, dx, dy) self.origin.x = self.origin.x + dx self.origin.y = self.origin.y + dy self:draw() end, focus_cb = function (self, focus) if focus == 0 then self:button_cb() end end, stypes = { INFO = 0, WARNING = 1, ERROR = 2, }, setstatus = function (self, stype, msg) -- todo: add a status line end, motion_cb = function (self, x, y, status) if self.stateful.panning then local ox, oy = self.ox, self.oy local dx, dy = x - ox, y - oy if self.stateful.linking then self.bx, self.by = self.bx + dx, self.by + dy end self:panning(dx, dy) self.ox, self.oy = x, y elseif self.stateful.leftbutton then local linking = self.stateful.linking self.stateful.dragging = true local got_error = false if self.stateful.leftclk and not self.stateful.linking then linking = self.stateful.leftclk if linking.obj.noutputs >= 1 then self.stateful.linking = linking else self:setstatus(dalosp.canvas.stypes.ERROR, "Can't link: origin object doesn't have any output") self.stateful.leftclk = nil linking = nil got_error = true end else linking = self.stateful.linking end if linking then self.ox, self.oy = x, y self:draw() elseif not got_error then -- todo: start selection square ? end elseif self.stateful.rghtbutton then if self.stateful.rghtclk and not self.stateful.moving then self.stateful.moving = self.stateful.rghtclk end self.stateful.dragging = true local moving = self.stateful.moving if moving then local ox, oy = self.ox, self.oy local dx, dy = x - ox, y - oy self:moveobj(moving, dx, dy) self.ox, self.oy = x, y elseif not self.menu.x then self.menu.x, self.menu.y = x, y self:draw() end end end, createlink = function (self, src, dst) local oldsrc = src.obj.outputs[src.obj.curoutput] local olddst = dst.obj.inputs[dst.obj.curinput] if oldsrc then oldsrc.obj.obj.inputs[oldsrc.ind] = nil oldsrc.obj.obj:input_change(oldsrc.ind) end if olddst then olddst.obj.obj.outputs[olddst.ind] = nil olddst.obj.obj:output_change(olddst.ind) end src.obj.outputs[src.obj.curoutput] = { obj = dst, ind = dst.obj.curinput } dst.obj.inputs[dst.obj.curinput] = { obj = src, ind = src.obj.curoutput } src.obj:output_change(src.obj.curoutput) dst.obj:input_change(dst.obj.curinput) end, destroylink = function (self, src) local oldsrc = src.obj.outputs[src.obj.curoutput] if oldsrc then src.obj.outputs[src.obj.curoutput] = nil src.obj:output_change(src.obj.curoutput) oldsrc.obj.obj.inputs[oldsrc.ind] = nil oldsrc.obj.obj:input_change(oldsrc.ind) end end, button_cb = function (self, button, pressed, x, y, status) if not pressed then pressed = 0 end if not x then x = self.ox end if not y then y = self.oy end if button == iup.BUTTON1 or not button then if pressed == 1 then self.stateful.leftbutton = true if self.stateful.rghtbutton then self.stateful.panning = true else local obj, ind = self:findobj(x, y) if obj then self.stateful.leftclk = obj table.remove(self.objects, ind) table.insert(self.objects, obj) self.ox, self.oy = x, y self.bx, self.by = x, y self:draw() end end else if not self.stateful.linking and self.stateful.leftclk and not self.stateful.dragging then self.stateful.leftclk.obj:activate() elseif self.stateful.linking then local dest = self:findobj(x,y) if not dest then self:destroylink(self.stateful.linking) elseif dest == self.stateful.linking then self:setstatus(dalosp.canvas.stypes.ERROR, "Can't link: origin is the same as destination") elseif dest.obj.ninputs <= 0 then self:setstatus(dalosp.canvas.stypes.ERROR, "Can't link: destination has no input") else self:createlink(self.stateful.linking, dest) end self.stateful.linking = nil self:draw() end self.stateful.panning = nil self.stateful.linking = nil self.stateful.leftclk = nil self.stateful.leftbutton = nil if not self.stateful.rghtbutton then self.stateful.dragging = nil end end end if button == iup.BUTTON3 or not button then if pressed == 1 then self.stateful.rghtbutton = true if self.stateful.leftbutton then self.stateful.panning = true else local obj, ind = self:findobj(x, y) if obj then self.stateful.rghtclk = obj table.remove(self.objects, ind) table.insert(self.objects, obj) self.ox, self.oy = x, y self.bx, self.by = x, y self:draw() end end else if not self.stateful.moving and self.stateful.rghtclk and not self.stateful.dragging then self.stateful.rghtclk.obj:configure() self:draw() elseif self.menu.x then -- activate X menu operation self.menu.x = nil self.menu.y = nil self:draw() elseif self.stateful.moving then -- check for the trash can end self.stateful.panning = nil self.stateful.moving = nil self.stateful.rghtclk = nil self.stateful.rghtbutton = nil if not self.stateful.leftbutton then self.stateful.dragging = nil end end end end, wheel_cb = function (self, delta, x, y, status) local obj = self:findobj(x, y) if obj then if iup.isshift(status) then obj.obj:change_curoutput(delta) elseif iup.isalt(status) then obj.obj:change_quicksetting(delta) else obj.obj:change_curinput(delta) end end self:draw() end, create = function (tab) tab.border = "No" tab.expand = "Yes" tab.shrink = "Yes" tab.minsize = "1x1" tab.rastersize = "1x1" local r = iup.canvas(tab) r.action = iupep.dbuffer.action r.draw = dalosp.canvas.draw r.resize_cb = iupep.dbuffer.resize_cb r.map_cb = iupep.dbuffer.map_cb r.unmap_cb = iupep.dbuffer.unmap_cb r.focus_cb = dalosp.canvas.focus_cb r.motion_cb = dalosp.canvas.motion_cb r.button_cb = dalosp.canvas.button_cb r.wheel_cb = dalosp.canvas.wheel_cb r.addobj = dalosp.canvas.addobj r.findobj = dalosp.canvas.findobj r.moveobj = dalosp.canvas.moveobj r.setobjplace = dalosp.canvas.setobjplace r.panning = dalosp.canvas.panning r.setstatus = dalosp.canvas.setstatus r.createlink = dalosp.canvas.createlink r.destroylink = dalosp.canvas.destroylink r.drawlink = dalosp.canvas.drawlink r.drawxmenu = dalosp.canvas.drawxmenu r.origin = { x = 0, y = 0 } r.menu = { } r.stateful = {} r.objects = {} dalos.active_canvas = r return r end, } dalosp.menu = { action_exit = function (self) return iup.CLOSE end, action_about = function (self) local dlg = iup.messagedlg { DialogType = "Information", ButtonDefault = "1", Buttons = "OK", Title = "About", Value = 'DALOS (c) 2009-2010 Nicolas "Pixel" Noble.\nThis is free software with ABSOLUTELY NO WARRANTY.\nPlease look at the COPYRIGHT file for details.', } dlg:popup() return iup.DEFAULT end, update_objects = function (self) if dalos.dlg then local newmenu = dalos.menu {} -- copy anything from self to newmenu ? *shrug* dalos.dlg.menu = newmenu end end, create = function (tab) local item_exit = iup.item { title = "Exit" } item_exit.action = dalosp.menu.action_exit local item_about = iup.item { title = "About" } item_about.action = dalosp.menu.action_about local add_menu = { } local north_menu = { radio = "1" } local east_menu = { radio = "1" } local west_menu = { radio = "1" } local south_menu = { radio = "1" } if type(dalos.objects) == "table" then local item for k, v in ipairs(dalos.objects) do item = iup.item { title = v.name } table.insert(add_menu, item) item = iup.item { title = v.name } table.insert(north_menu, item) item = iup.item { title = v.name } table.insert(east_menu, item) item = iup.item { title = v.name } table.insert(west_menu, item) item = iup.item { title = v.name } table.insert(south_menu, item) end end local menu_file = iup.submenu { iup.menu { item_exit }, title = "File" } local menu_add = iup.submenu { iup.menu(add_menu), title = "Add" } local menu_cross = iup.submenu { iup.menu { iup.submenu { iup.menu(north_menu), title = "North" }, iup.submenu { iup.menu(east_menu), title = "East" }, iup.submenu { iup.menu(west_menu), title = "West" }, iup.submenu { iup.menu(south_menu), title = "South" }, }, title = "Cross", } local menu_help = iup.submenu { iup.menu { item_about }, title = "Help" } local r = iup.menu { menu_file, menu_add, menu_cross, menu_help } r.update_objects = dalosp.menu.update_objects r.add_menu = add_menu dalos.active_menu = r return r end, } dalosp.object = { default_draw = function (self, cv, x, y, w, h ) local x1, x2, y1, y2 = x, x + w, y, y + h y1, y2 = cv:InvertYAxis(y2), cv:InvertYAxis(y1) local cx, cy = (x1 + x2) / 2, (y1 + y2) / 2 cv:Foreground(self.color) cv:Box(x1, x2, y1, y2) cv:Foreground(cd.BLACK) cv:Rect(x1, x2, y1, y2) cv:TextAlignment(cd.SOUTH) cv:Text(cx, y2, self.name) cv:TextAlignment(cd.NORTH) if self.ninputs >= 1 then cv:Text(x1, y1, self.curinput .. "/" .. self.ninputs) end if self.noutputs >= 1 then cv:Text(x2, y1, self.curoutput .. "/" .. self.noutputs) end if self.quicksetting then cv:Text(cx, y1, self.quicksetting) end end, get_linked_input = function (self, ind) local ni = self.inputs[ind] if ni then local ii = ni.ind local io = ni.obj return io.obj:get_output(ii) else return nil end end, default_activate = function (self) print "activate" end, default_configure = function (self) print "configure" end, change_curinput = function (self, delta) if self.ninputs <= 1 then return end self.curinput = self.curinput + delta if self.curinput > self.ninputs then self.curinput = self.ninputs end if self.curinput <= 0 then self.curinput = 1 end end, change_curoutput = function (self, delta) if self.noutputs <= 1 then return end self.curoutput = self.curoutput + delta if self.curoutput > self.noutputs then self.curoutput = self.noutputs end if self.curoutput <= 0 then self.curoutput = 1 end end, default_change_quicksetting = function (self, delta) end, default_getoutput = function (self, ind) return self.houtputs[ind] end, default_inputchange = function (self, ind) end, default_outputchange = function (self, ind) end, set_houtput = function (self, h, ind) if not ind then ind = 1 end self.houtputs[ind] = h local obj = self.outputs[ind] if obj then obj.obj.obj:input_change(obj.ind) end end, create = function (dcanvas, tab, extra) if not tab then tab = {} end local obj = { draw = dalosp.object.default_draw, name = tab.name or "NoName", color = tab.color or cd.GRAY, ninputs = tab.ninputs or 0, noutputs = tab.noutputs or 0, inputs = {}, outputs = {}, curinput = 1, curoutput = 1, quicksetting = tab.quicksetting, otype = tab.otype or dalos.objtype.DUMMY, activate = tab.activate or dalosp.object.default_activate, configure = tab.configure or dalosp.object.default_configure, change_curinput = dalosp.object.change_curinput, change_curoutput = dalosp.object.change_curoutput, change_quicksetting = tab.change_quicksetting or dalosp.object.default_change_quicksetting, extra = extra, get_output = tab.getoutput or dalosp.object.default_getoutput, input_change = tab.input_change or dalosp.object.default_inputchange, output_change = tab.output_change or dalosp.object.default_outputchange, set_houtput = dalosp.object.set_houtput, houtputs = {}, get_linked_input = dalosp.object.get_linked_input, dcanvas = dcanvas, } if tab.otype and not tab.c then if tab.otype == dalos.objtype.DUMMY then obj.color = cd.GRAY elseif tab.otype == dalos.objtype.HANDLE then obj.color = cd.CYAN elseif tab.otype == dalos.objtype.LUA_VIEWER then obj.color = cd.MAGENTA elseif tab.otype == dalos.objtype.LUA_FILTER then obj.color = cd.YELLOW else obj.color = cd.DARK_RED end end dcanvas:addobj(obj, tab.x or 0, tab.y or 0, tab.w or 50, tab.h or 50) return obj end, } dalos.canvas = dalosp.canvas.create dalos.menu = dalosp.menu.create dalos.object = dalosp.object.create dalos.objtype = { UNKNOWN = 0, DUMMY = 1, HANDLE = 2, LUA_FILTER = 3, LUA_VIEWER = 4, } ---------------- dalosp.hexview = { activate = function (self) self.extra.hvdlg:show() end, input_change = function (self, ind) local extra = self.extra local hv = extra.hv local h = self:get_linked_input(ind) if not h then self.color = cd.YELLOW hv:updatehandle(nil) self.dcanvas:draw() return end if not h:canread() or not h:canseek() then self.color = cd.RED hv:updatehandle(nil) self.dcanvas:draw() return end self.color = cd.GREEN for i = 1, 12 do self:set_houtput(nil, i) self.oldcursors[i] = -1 end hv:updatehandle(h) self.dcanvas:draw() end, configure = function (self) end, output_change = function (self, ind) self.watchees[ind] = self.outputs[ind] and true or false end, update_houtput = function (self, ind, cursor) local h = self:get_linked_input(1) if h and self.watchees[ind] then local obj = { h = h, hvo = self, ind = ind, origin = cursor, offset = 0, size = h:getsize() - cursor, canread = function (self) return true end, canwrite = function (self) return false end, canseek = function (self) return true end, canwatch = function (self) return self.h:canwatch() end, getname = function (self) return self.hvo.name .. ":" .. self.ind end, tell = function (self) return self.offset end, getsize = function (self) return self.size end, getmodif = function (self) return self.hvo:getmodif() end, flush = function (self) return self.hvo:flush() end, seek = function (self, offset, wheel) if wheel == SEEK_SET then self.offset = offset elseif wheel == SEEK_CUR then self.offset = self.offset + offset elseif wheel == SEEK_END then self.offset = self.size + offset else error "Unknown wheel" end self.h:seek(self.offset + self.origin) return self.offset end, read = function (self, userdata, count) count = math.min(count, self.size - self.offset) if count == 0 then if self.got_eof then self.lh:close() end self.got_eof = true return 0 end self.got_eof = false local r = self.h:read(count, userdata) self.offset = self.offset + r if r == 0 then self.got_eof = true end return r end, } local newh = HandleLua(obj) obj.lh = newh self:set_houtput(newh, ind) end end, dalos_hv_cb = function (self, hv, reset) local m for i = 1, 10 do m = hv.markers[i] if self.oldcursors[i] ~= m then self:update_houtput(i, m) self.oldcursors[i] = m end end m = hv.kcursor if self.oldcursors[11] ~= m then self:update_houtput(11, m) self.oldcursors[11] = m end m = hv.mcursor if self.oldcursors[12] ~= m then self:update_houtput(12, m) self.oldcursors[12] = m end end, create = function (d, tab) if not tab.name then tab.name = "Hexview" end tab.otype = dalos.objtype.LUA_FILTER tab.activate = dalosp.hexview.activate tab.input_change = dalosp.hexview.input_change tab.output_change = dalosp.hexview.output_change tab.configure = dalosp.hexview.configure tab.ninputs = 1 tab.noutputs = 12 local hv = iupe.hexview { } local hvtb = iupe.hexview_toolbox { hexview = hv } local hvdlg = iup.dialog { iup.hbox { iup.frame { hv }, iup.sbox { direction = "WEST", hvtb } }, title = tab.name } local obj = dalos.object(d, tab, { hv = hv, hvtb = hvtb, hvdlg = hvdlg }) obj.oldcursors = { } obj.watchees = { } obj.update_houtput = dalosp.hexview.update_houtput for i = 1, 12 do obj.oldcursors[i] = -1 end hv:registercb(dalosp.hexview.dalos_hv_cb, obj) return obj end, } dalos.hexview = dalosp.hexview.create dalos:register_obj("Hexview", dalos.hexview) ---------------- d = dalos.canvas {} m = dalos.menu {} b1 = Buffer(true) b1:write("Buffer 1 contents") b2 = Buffer(true) b2:write("Buffer 2 contents") o1 = dalos.object(d, { y = 30, x = 10, noutputs = 1, name = "Buffer 1", otype = dalos.objtype.HANDLE }) o2 = dalos.object(d, { y = 120, x = 10, noutputs = 1, name = "Buffer 2", otype = dalos.objtype.HANDLE }) h1 = dalos.hexview(d, { y = 60, x = 100, name = "Hexview 1" }) h2 = dalos.hexview(d, { y = 60, x = 200, name = "Hexview 2" }) o1:set_houtput(b1) o2:set_houtput(b2) dlg = iup.dialog { d, title = "Dalos", menu = m } dalos.dialog = dlg dlg:show() iup.MainLoop() dlg:hide()