Programming and Scripting :: XPM Icon Viewer



Quote
shouldn't you explicity call close on data here as well?
There is a file:close() after the loop. Does return cause the function to end?  I've never fully understood how return works, particularly when it is not the last command in a function.  I had a break in there at one time, and then the return, but it required four or five extra lines of code and showed no noticeable improvement.

Quote
would adding an extra button to optionally "fix" a broken image be a good idea?
I think it's a good idea.

Quote
does the xpm format actually allow spaces in front?  If it does, then technically it's not a "broken image"
I have no idea what the technical specs of the file format are, but apparently it is allowed in many applications that read XPM. That could simply be application developers making software that compensates for a common error, though, just as we are doing here.

I plan to add a couple more checks eventually. The most important will be to check whether or not a *.xpm file is actually an XPM. If a non-XPM file has a *.xpm filename, it will crash the app.

Quote
There is a file:close() after the loop. Does return cause the function to end?  I've never fully understood how return works, particularly when it is not the last command in a function.  I had a break in there at one time, and then the return, but it required four or five extra lines of code and showed no noticeable improvement.
The return statement can be like a GOTO command or break command, it basically exits or 'returns' from the function right there with a value (if specified) back to the point where the function was called.  So the close statement would not be read at all.  Although having multiple return statements usually isn't good semantics for programming, I find that scripting usually has looser guidelines (this is just my opinion).  Also if there is any return value at all, it is cleaner to return something of the same type to explicitly tell what the author intended, and can reduce potential error.  In psuedo-code you can do something like: VALID = true; for X in Y while VALID do; VALID = check_validity(X); done; return VALID

Quote
I plan to add a couple more checks eventually. The most important will be to check whether or not a *.xpm file is actually an XPM. If a non-XPM file has a *.xpm filename, it will crash the app.
I guess the most basic check would just to look for the /* XPM */ header.  Did a quick search and found 'cxpm' which might help out on the format details (see http://koala.ilog.fr/lehors/xpm.html )

I have a lot to learn about programming in general.
Your suggestion is similar to what I had originally done using break to hopefully speed things up a little, but since it didn't seem to make any difference I eventually went with the smaller code. I'll look at it again later.

Quote
I guess the most basic check would just to look for the /* XPM */ header.  Did a quick search and found 'cxpm'
that's amusing
I had both of those ideas in mind as well =o)
I haven't gotten to that yet, though.

The thing about cxpm is that it is not in DSL. Its buddy sxpm is in DSL, though, and I'm currently using that to do the fix. Simply opening and resaving the troublesome files with sxpm is enough to fix them.

Code Sample
#!/bin/murgaLua

-- XPM icon viewer
-- mikshaw 2007
-- thanks to ^thehatsrule^ for help and suggestions

dir="/usr/share/dfm/icons"
editor="xpaint"
xpm_size=32

broken_xpm_data=[[
/* XPM */
static char *broken_image[] = {
/* columns rows colors chars-per-pixel */
"14 16 16 1",
"  c black",
". c #800000",
"X c #008000",
"o c #808000",
"O c navy",
"+ c #800080",
"@ c #008080",
"# c #C0C0C0",
"$ c #808080",
"% c red",
"& c green",
"* c None",
"= c blue",
"- c magenta",
"; c cyan",
": c gray100",
/* pixels */
"          $***",
" :::::::::$$**",
" :########$:$*",
" :###XX###$::$",
" :##X&X ##    ",
" :##XXX ####: ",
" :###  #####: ",
" :######=== :*",
" :#%####=;= :*",
" :#-%###==O***",
" :#--%##  **: ",
" :#---%#***#: ",
" :#   *****#: ",
" :##*****###: ",
" ::*****::::: ",
"  ******      "
};
]]
xpm_tempfile=os.tmpname()
xpm_write=io.open(xpm_tempfile,"w")
if xpm_write then
 xpm_write:write(broken_xpm_data)
 xpm_write:close()
 broken_xpm=fltk:Fl_XPM_Image(xpm_tempfile)
 os.remove(xpm_tempfile)
end

edvar=os.getenv("XPM_EDITOR")
if edvar then editor=edvar end

function get_image_data(self)
 local old_data=display:image()
 if old_data then old_data:uncache() end
 local image_data=self:image()
 if image_data then
   display:image(image_data:copy(128,128)) --show it bigger!
   broken=nil
   i_menu:mode(3,1) -- disable "fix" item
 else
   display:image(broken_xpm)
   broken=self:tooltip()
   i_menu:mode(3,0)
 end
 current_file=dir..self:tooltip() -- for editor
 display:label("\n"..self:tooltip().."\n"..lfs.attributes(current_file).size.." bytes")
 display:redraw()
end

function menu_cb()
 local v=i_menu:value()
 if v==0 and current_file then os.execute(editor.." "..current_file.." &")
 elseif v==1 then chdir()
 elseif v==2 then refresh()
 elseif v==3 then fix_xpm()
 elseif v==4 then os.exit(0)
 end
end

function find_stupid_space(file)
 local data=io.open(file)
 if data then
   for line in data:lines() do
     if string.find(line,"^%s") then return 1 end
   end
 data:close()
 end
end

function build_list()
 local active=1
 if not string.find(dir,"/$") then dir=dir.."/" end
 scroll=fltk:Fl_Scroll(5,bh,sw,sh)
 w:add(scroll)
 pack=fltk:Fl_Pack(5,bh,bw,sh)
 pack:spacing(2)
 scroll:add(pack)
 -- get all xpm files
 for file in lfs.dir(dir) do
   if string.find(file,"%.xpm$") then
     table.insert(images,file)
   end
 end
 table.sort(images)
 -- make buttons for "xpm_size" images only
 xpm_count=0
 filesize=0
 for i=1,table.getn(images) do
   icon[i]=fltk:Fl_XPM_Image(dir..images[i])
   if icon[i]:w()==xpm_size and icon[i]:h()==xpm_size then
       xpm_count=1+xpm_count
       filesize=filesize+lfs.attributes(dir..images[i]).size
       butt[xpm_count]=fltk:Fl_Button(0,0,bw,bw)
       butt[xpm_count]:box(15) --shadow
     if not find_stupid_space(dir..images[i]) then
       butt[xpm_count]:image(icon[i])
     else butt[xpm_count]:label("bad\nimage")
     end
       butt[xpm_count]:callback(get_image_data)
       butt[xpm_count]:tooltip(images[i])
       if broken==images[i] then active=xpm_count end -- for refreshing display
       pack:add(butt[xpm_count])
     else icon[i]:uncache()
   end
 end
 if butt[active] then
   -- show first image
   display:show()
   butt[active]:do_callback()
 end
 display2:label(xpm_count.." files | "..math.ceil(filesize/1024).." kb")
end

function refresh()
 local old_image=display:image()
 if old_image then old_image:uncache(); display:hide() end
 -- clear all tables and remove buttons
 for i=1,table.getn(images) do table.remove(images) end
 for i=1,table.getn(icon) do icon[i]:uncache() end
 for i=1,table.getn(icon) do table.remove(icon) end
 for i=1,table.getn(butt) do table.remove(butt) end
 w:remove(scroll)
 Fl:delete_widget(scroll)
 scroll=nil
 build_list()
end

function chdir()
 local dirname=fltk.fl_dir_chooser("pick a dir...",dir)
 if dirname then
   dir=dirname
   refresh()
 end
end

function fix_xpm()
 if broken then
   os.execute("cd "..dir.." && sxpm -nod "..broken.." -o "..broken)
   refresh()
 end
end

-- candy
Fl:set_boxtype(fltk.FL_UP_BOX,fltk.FL_THIN_UP_BOX)
Fl:set_boxtype(fltk.FL_DOWN_BOX,fltk.FL_THIN_DOWN_BOX)
fltk.fl_define_FL_SHADOW_BOX()
Fl:set_color(fltk.FL_DARK3,150,150,150) -- shadow
Fl:set_color(fltk.FL_GRAY0,128,128,128) -- frame
--Fl_Tooltip:disable()

-- some sizes and positions are determined by xpm_size
bw=xpm_size+20 -- button width
bh=30; sw=bw+20; sh=250 -- menu, scroll size
ww=sw+sh+10
wh=bh+sh+10
images={}; icon={}; butt={}
w=fltk:Fl_Window(ww,wh,"XPM Icons")

menu_items={"&Edit","&Directory","&Refresh","&Fix","&Quit"}
i_menu=fltk:Fl_Menu_Button(0,0,sw+5,bh,"&Menu")
i_menu:align(20)
i_menu:callback(menu_cb)
i_menu:selection_color(fltk.FL_WHITE)
for i,v in ipairs(menu_items) do i_menu:add(v) end

display=fltk:Fl_Box(sw+5,0,sh,sh) -- big image
display2=fltk:Fl_Box(sw+5,sh,sh,bh) -- dir info

build_list()
w:show()
Fl:run()

This is programmed very application centric. It fine for accessing everything from within the application. But for DSL v4.x it would be nice to be able to double click on an .xpm file or drag-n-drop an .xpm file and have this application process it.
Looking good...

I'm not sure how it works so far with hundreds of xpm's, but if you don't display all of them at the same time, you can save startup time by loading/checking the xpm's for when you actually do display them.  Either incrementally (i.e. scrolling through) or via pages would be fine, but this comes at the expense of having them load at whatever intervals.

DND would be a nice to-have for 4.x.

Next Page...
original here.