Quote |
On the Load Local, what I had in mind was a third button at the top of the display. |
Quote |
Then upon invocation if no database has been downloaded, show all buttons top and bottom ghosted out except "Update Database" and "Load Local" and perhaps a text message of "No database currently loaded" where you were showing the database date and count. |
Code Sample |
#!/bin/murgaLua --[[ MyDSL browser, 2008 mikshaw Changelog (yyyy/mm/dd) 2008/03/31: Replaced os.rename() with os.execute("mv") (thanks roberts) All infos are displayed (fixed bad gmatch string) Added "load local" button 2008/03/18: Added help screen 2008/03/17: More interface tweaks Convert date format to yyyy/mm/dd (hopefully a temp fix) 2008/03/16: Interface tweaks 2008/03/15: Improved the update function Database date check uses os.date/lfs instead of os.popen/ls/awk Use wizard instead of tabs for better use of resources 2008/03/14: Undid gmatch change due to improvements to the database file Improved keyboard control Use Fl_Menu_Button for menus to add to keyboard control Fixed crash on search or install with an empty database 2008/01/24: Fixed string.gmatch to break up files even if there is no newline between them 2008/01/17: Added date of last update Tweaked the update function (it's still sloppy) Fixed ".mydsl_dir not found" error message 2008/01/16: Fixed resizable behavior Uses file in .mydsl_dir Added auto update for first run 2008/01/15: Added text search 2008/01/14: Fixed display of incorrect duplicate info file Check for trailing spaces in title string Added "Update" function Code and interface tweaks 2008/01/13: start of project --]] mydsldirfile=io.open("/opt/.mydsl_dir") if not mydsldirfile then print(arg[0]..": Can't open /opt/.mydsl_dir") os.exit(1) end mydsl_dir=mydsldirfile:read("*l") mydsldirfile:close() listfile=mydsl_dir.."/mydslinfo.bz2" listurl="ftp://ibiblio.org/pub/Linux/distributions/damnsmall/mydsl/mydslinfo.bz2" list_sep="@b@B49@u" help_text=[[ @sMyDSL Browser, Version 1? @- @bAbout This program is used to view details of available myDSL extensions, and to install a selected package. An internet connection is required. The first time the program runs it will attempt to download a database from a myDSL server, which will be saved to the directory listed in /opt/.mydsl_dir Package installation is done using the existing myDSL system, which connects to the server listed in /opt/.dslrc @- @bBasic Usage Choose a package to view either by browsing the file list or by entering a text string to search, and download and install that package if you discover that you must have it. Text search of the entire database is available by clicking on the "search" button (Alt+S). The search is case-insensitive, but is otherwise literal, meaning that no translation is done for special characters like "*" or "?". Clicking on a search result will display the info for that package. The database can be retrieved at any time by clicking "Update" (Alt+U). If the download fails the program continues to use the current database. The "Install" button (Alt+I) attempts to download the package displayed in the info window, not necessarily the one highlighted in the file list. The "Install Local" button lets you browse for packages saved on a disk. @- @bKeyboard In addition to the key controls marked on the various buttons, there are some keys available to make keyboard control easier. In most cases the use of "Alt" is not needed in this application, and pressing just the appropriate key should work. When the search text input field has focus, however, it is necessary to Use "Alt" in addition to the key. Enter : Activate the selected item in the file list or search T : Toggle the visibility of the file list ] : Jump to next section in the file list. [ : Jump to previous section in the file list. These keys are not global. The widget that will receive the desired action must be onscreen at the time. For example, the database will not be updated if you press U when the main tab (browse) is not visible. @- @sCopyright (c) 2008 mikshaw @sThis script is free software, released without warranty and with no guarantee that it will do anything useful. ]] function toggle(b) local count,state,one,two=1 -- selected line and its label local me,label=b:value(),b:text(b:value()) if string.find(label,"%+%s*") then state,one,two="expanded","%+ ","%- " else state,one,two="collapsed","%- ","%+ " end b:text(me,string.gsub(label,one,two)) -- swap + and - -- toggle all lines until reaching the next section or the browser end while b:text(me+count) and not string.match(b:text(me+count),list_sep) do if state=="collapsed" then b:hide(me+count) else b:show(me+count) end count=count+1 end end function fill_list() inputfile = io.popen("bunzip2 -c "..listfile) data=inputfile:read("*a") inputfile:close() if not string.find(data,"%w") then -- if there's nothing readable in it info_display_buffer:text("Cannot read database "..listfile.."\nPlease try updating again.") else filelist:clear() header={} info={} -- table containing all package data: info[index].{location,title,date} info_count=0 -- number of packages local current_loc="" -- location string local headers=0 -- number of locations -- breaks up info for each package into a separate string locount=0 for s in string.gmatch(data,"(Location:.-\n)\n") do -- convert mm/dd/yyyy to yyyy/mm/dd local datestring=string.match(s,"Current:%s*(.-%d)%s") if string.find(datestring,"%d%d%D%d%d%D%d%d%d%d") then datestring=string.gsub(datestring,"(%d%d)%D(%d%d)%D(%d%d%d%d)","%3/%1/%2") end info_count=info_count+1 info[info_count]={ location=string.match(s,"Location:%s*(.-)%s*\n"), title=string.match(s,"Title:%s*(.-)%s*\n"), date=string.gsub(datestring,"%D","/"), --datestring=string.match(s,"Current:%s*(.-%d)%s"), -- plain text=s } if current_loc ~= info[info_count].location then -- create a header when location string changes current_loc=info[info_count].location filelist:add(list_sep.."- "..current_loc) headers=headers+1 header[headers]=filelist:size() end filelist:add(info[info_count].title) end for i,v in ipairs(header) do filelist:value(header[i]); toggle(filelist) end last_update:label("Database updated "..os.date("%d %b %Y",lfs.attributes(listfile).modification)..", "..info_count.." packages") end filelist:value(1) tab_browse:setonly() end function updatedb() -- download to temp file, check file integrity, and only then replace the old file tempfile=os.tmpname() os.execute(terminal.." -title \"Database Update\" -e wget "..listurl.." -O "..tempfile) if os.execute("bzip2 -t "..tempfile) == 0 then os.execute("mv "..tempfile.." "..listfile) fill_list() else fltk.fl_alert("Database check failed.\nThis may be the result of an incomplete download\nor gremlins in your internets.") os.remove(tempfile) end filelist:take_focus() -- for better keyboard control end function do_search() if search_text:value() ~= "" and info_count and info_count>0 then results:clear() if Fl_Browser.column_widths then results:add("@B49@b@uPackage Name"..spacer.."@B49@b@uLocation"..spacer.."@B49@b@uRelease Date") else results:add("@B49PACKAGE NAME"..spacer.."LOCATION"..spacer.."RELEASE DATE") end for i=1,info_count do if string.find(string.lower(info[i].text),string.lower(search_text:value()),1,1) then results:add(info[i].title..spacer..info[i].location..spacer..info[i].date) end end results:insert(1,"@B49@."..tostring(results:size()-1).." search results") end end function pick_search_result() -- show selected info file in the browse tab if results:value() > 2 then get_loc=string.match(results:text(results:value()),spacer.."(.*)"..spacer) get_fname=string.match(results:text(results:value()),"(.-)"..spacer) -- select the package in filelist local myloc=1 while myloc<=filelist:size() do if not string.find(filelist:text(myloc)," "..get_loc,1,1) then myloc=myloc+1 else filelist:value(myloc) break end end -- open submenu if not open if string.find(filelist:text(myloc),list_sep.."+ ",1,1) then toggle(filelist) end while myloc<=filelist:size() do if filelist:text(myloc) ~= get_fname then myloc=myloc+1 else filelist:value(myloc) break end end for i=1,info_count do -- make sure you get the right info AND category if info[i].title == get_fname and info[i].location == get_loc then info_display_buffer:text(info[i].text) tab_browse:setonly() myfile=info[i] -- this is the file that will be downloaded break end end tabs:value(tab1) end end function list_cb() if filelist:value()>0 then -- clicking a blank space can be fatal without this check if Fl:event() == fltk.FL_RELEASE or Fl:event_key()==fltk.FL_Enter then -- on click or Enter if string.find(filelist:text(filelist:value()),list_sep) then toggle(filelist) -- if it's a separator else -- find out what submenu we're under local myloc=filelist:value() while myloc > 0 do myloc=myloc-1 if string.find(filelist:text(myloc),list_sep) then myloc=string.gsub(filelist:text(myloc),".-%s%s","") break end end for i=1,info_count do -- make sure you get the right info AND category if info[i].title == filelist:text(filelist:value()) and info[i].location == myloc then info_display_buffer:text(info[i].text) myfile=info[i] tab_browse:setonly() break end end end end end end function toggle_list() if listw > 0 then oldw=listw tiles:position(listw+5,nil,6,nil) listw=0 info_display:take_focus() else tiles:position(6,nil,oldw+5,nil) listw=oldw filelist:take_focus() end end terminal="aterm +tr -bg white -fg black -geometry 80x4" ww=560; wh=380; bh=30; bw=(ww-10)/3 win=fltk:Fl_Window(ww,wh,"MyDSL Browser") tabs=fltk:Fl_Wizard(0,0,ww,wh-bh-5) tab1=fltk:Fl_Group(0,0,ww,wh-bh-5,"browse") -- the tiles are built weird initially (filelist is only 1px wide) -- this is a kluge; toggle_list doesn't seem to work if filelist -- is shrunk less than it's initial width tiles=fltk:Fl_Tile(5,5+bh,ww-10,wh-(15+bh*2)) filelist=fltk:Fl_Hold_Browser(5,5+bh,1,wh-(15+bh*2)) filelist:callback(list_cb) info_display=fltk:Fl_Text_Display(6,5+bh,ww-11,wh-(15+bh*2)) info_display:textfont(fltk.FL_SCREEN) info_display:textsize(12) info_display_buffer=fltk:Fl_Text_Buffer() info_display:buffer(info_display_buffer) fltk:Fl_End() --end tiles tiles:position(6,nil,ww/3,nil) -- resize the filelist tiles:callback(function() listw=filelist:w() end) listw=filelist:w() -- for toggle_list() -- offscreen buttons for keyboard control prevheader=fltk:Fl_Button(ww,wh,bh,bh,"&[") prevheader:callback( function() for i=table.getn(header),1,-1 do if header[i] < filelist:value() then filelist:value(header[i]); break end end filelist:take_focus() end) nextheader=fltk:Fl_Button(ww,wh,bh,bh,"&]") nextheader:callback( function() for i=1,table.getn(header) do if header[i] > filelist:value() then filelist:value(header[i]); break end end filelist:take_focus() end) enter=fltk:Fl_Return_Button(ww,wh,10,10) enter:callback(function() filelist:do_callback() end) togglist=fltk:Fl_Button(ww,wh,10,10,"&t") togglist:callback(toggle_list) -- top buttons b_updatedb=fltk:Fl_Button(5,5,bw,bh,"&Update Database") b_updatedb:callback(updatedb) install=fltk:Fl_Button(bw+5,5,bw,bh,"&Install Selected Package") install:callback( function() if myfile then mydslload=os.execute("mydsl-load "..myfile.title.." "..myfile.location) if mydslload~=0 then fltk.fl_alert(myfile.title..":\nDownload or Checksum error!") end end filelist:take_focus() end) install_local=fltk:Fl_Button(bw*2+5,5,bw,bh,"Install &Local Package @DnArrow") install_local:callback( function() local filename=fltk.fl_file_chooser("Browse for a MyDSL file","Mydsl files (*.{dsl,tar.gz,uci,unc})",mydsl_dir,nil) if filename then os.execute("mydsl-load "..filename) end end) fltk:Fl_End() --end tab1 tab2=fltk:Fl_Group(0,0,ww,wh-bh-5,"search text") search_text=fltk:Fl_Input(bh*3+5,5,ww-10-bh*3,bh,"search text ") search_text:when(fltk.FL_WHEN_ENTER_KEY) search_text:callback(do_search) results=fltk:Fl_Hold_Browser(5,5+bh,ww-10,wh-(15+bh*2)) -- column widths available in 0.6.4+ if Fl_Browser.column_widths then results:column_widths({results:w()/2,150,0}) spacer="\t" else spacer="\t\t" -- force some extra space for older versions results:textfont(fltk.FL_SCREEN) -- fixed-width makes it closer to columns end -- react to click only results:callback( function() if results:value() > 1 and Fl:event()==fltk.FL_RELEASE then pick_search_result() end end) -- offscreen results_callback button for Enter key results_cb=fltk:Fl_Return_Button(ww,wh,bh,bh) results_cb:callback(pick_search_result) fltk:Fl_End() --end tab2 tab3=fltk:Fl_Group(0,0,ww,wh-bh-5,"help") help=fltk:Fl_Browser(5,5,ww-10,wh-(15+bh)) fltk:Fl_End() --end tab3 fltk:Fl_End() --end tabs group for line in string.gmatch(help_text,".-\n") do help:add(line) end -- using buttons instead of tabs gives better keyboard control tabbuttons=fltk:Fl_Pack(5,wh-bh,200,bh-5) tabbuttons:type(fltk.FL_HORIZONTAL) tabbuttons:spacing(5) tab_browse=fltk:Fl_Radio_Button(0,0,100,bh-5,"&browse") tab_browse:when(fltk.FL_WHEN_CHANGED) tab_browse:box(fltk.FL_BORDER_BOX); tab_browse:selection_color(7) tab_browse:callback(function() tabs:value(tab1) end) tab_search=fltk:Fl_Radio_Button(0,0,100,bh-5,"text &search") tab_search:when(fltk.FL_WHEN_CHANGED) tab_search:box(fltk.FL_BORDER_BOX); tab_search:selection_color(7) tab_search:callback(function() tabs:value(tab2); Fl:focus(search_text) end) tab_help=fltk:Fl_Radio_Button(0,0,50,bh-5,"&help") tab_help:when(fltk.FL_WHEN_CHANGED) tab_help:box(fltk.FL_BORDER_BOX); tab_help:selection_color(7) tab_help:callback(function() tabs:value(tab3) end) fltk:Fl_End() --end tabs buttons last_update=fltk:Fl_Box(ww-5,wh-bh-5,5,bh+5) last_update:align(fltk.FL_ALIGN_LEFT) last_update:labelsize(12) win:resizable(tabs) tabs:resizable(tab1) tabs:resizable(tab2) tabs:resizable(tab3) tab1:resizable(tiles) tab2:resizable(results) tab3:resizable(help) win:show() find_file=io.open(listfile) if find_file then data=find_file:read("*a") else data="" end if string.find(data,"%w") then find_file:close(); fill_list() else ask4db=fltk.fl_choice("You have no database for online MyDSL apps.\nDo you want to download it now?","No","Yes",NULL) if ask4db==1 then Fl:check(); updatedb() end end Fl:run() |
Code Sample |
#!/bin/murgaLua -- MyDSL Browser, 2008 mikshaw mydsldirfile=io.open("/opt/.mydsl_dir") if not mydsldirfile then print(arg[0]..": Can't open /opt/.mydsl_dir") os.exit(1) end mydsl_dir=mydsldirfile:read("*l") mydsldirfile:close() listfile=mydsl_dir.."/mydslinfo.bz2" listurl="ftp://ibiblio.org/pub/Linux/distributions/damnsmall/mydsl/mydslinfo.bz2" list_sep="@b@B49@u" function toggle(b) local count,state,one,two=1 local me,label=b:value(),b:text(b:value()) if string.find(label,"%+%s*") then state,one,two="expanded","%+ ","%- " else state,one,two="collapsed","%- ","%+ " end b:text(me,string.gsub(label,one,two)) while b:text(me+count) and not string.match(b:text(me+count),list_sep) do if state=="collapsed" then b:hide(me+count) else b:show(me+count) end count=count+1 end end function fill_list() inputfile = io.popen("bunzip2 -c "..listfile) data=inputfile:read("*a") inputfile:close() if not string.find(data,"%w") then info_display_buffer:text("Cannot read database "..listfile.."\nPlease try updating again.") else filelist:clear() header={} info={} info_count=0 local current_loc="" local headers=0 locount=0 for s in string.gmatch(data,"(Location:.-\n)\n") do local datestring=string.match(s,"Current:%s*(.-%d)%s") if string.find(datestring,"%d%d%D%d%d%D%d%d%d%d") then datestring=string.gsub(datestring,"(%d%d)%D(%d%d)%D(%d%d%d%d)","%3/%1/%2") end info_count=info_count+1 info[info_count]={ location=string.match(s,"Location:%s*(.-)%s*\n"), title=string.match(s,"Title:%s*(.-)%s*\n"), date=string.gsub(datestring,"%D","/"), text=s } if current_loc ~= info[info_count].location then current_loc=info[info_count].location filelist:add(list_sep.."- "..current_loc) headers=headers+1 header[headers]=filelist:size() end filelist:add(info[info_count].title) end for i,v in ipairs(header) do filelist:value(header[i]); toggle(filelist) end last_update:label("Database updated "..os.date("%d %b %Y",lfs.attributes(listfile).modification)..", "..info_count.." packages") end filelist:value(1) tab_browse:setonly() end function updatedb() tempfile=os.tmpname() os.execute(terminal.." -title \"Database Update\" -e wget "..listurl.." -O "..tempfile) if os.execute("bzip2 -t "..tempfile) == 0 then os.execute("mv "..tempfile.." "..listfile) install:activate() tab_search:activate() tab_browse:activate() info_display_buffer:text("\nDatabase updated successfully.") fill_list() else fltk.fl_alert("Database check failed.\nThis may be the result of an incomplete download\nor gremlins in your internets.") os.remove(tempfile) end filelist:take_focus() end function do_search() if search_text:value() ~= "" and info_count and info_count>0 then results:clear() results:add("@B49@b@uPackage Name\t@B49@b@uLocation\t@B49@b@uRelease Date") for i=1,info_count do if string.find(string.lower(info[i].text),string.lower(search_text:value()),1,1) then results:add(info[i].title.."\t"..info[i].location.."\t"..info[i].date) end end results:insert(1,"@B49@."..tostring(results:size()-1).." search results") end end function pick_search_result() if results:value() > 2 then get_loc=string.match(results:text(results:value()),"\t(.*)\t") get_fname=string.match(results:text(results:value()),"(.-)\t") local myloc=1 while myloc<=filelist:size() do if not string.find(filelist:text(myloc)," "..get_loc,1,1) then myloc=myloc+1 else filelist:value(myloc) break end end if string.find(filelist:text(myloc),list_sep.."+ ",1,1) then toggle(filelist) end while myloc<=filelist:size() do if filelist:text(myloc) ~= get_fname then myloc=myloc+1 else filelist:value(myloc) break end end for i=1,info_count do if info[i].title == get_fname and info[i].location == get_loc then info_display_buffer:text(info[i].text) tab_browse:setonly() myfile=info[i] break end end tabs:value(tab1) end end function list_cb() if filelist:value()>0 then if Fl:event() == fltk.FL_RELEASE or Fl:event_key()==fltk.FL_Enter then if string.find(filelist:text(filelist:value()),list_sep) then toggle(filelist) else local myloc=filelist:value() while myloc > 0 do myloc=myloc-1 if string.find(filelist:text(myloc),list_sep) then myloc=string.gsub(filelist:text(myloc),".-%s%s","") break end end for i=1,info_count do if info[i].title == filelist:text(filelist:value()) and info[i].location == myloc then info_display_buffer:text(info[i].text) myfile=info[i] tab_browse:setonly() break end end end end end end function toggle_list() if listw > 0 then oldw=listw tiles:position(listw+5,nil,6,nil) listw=0 info_display:take_focus() else tiles:position(6,nil,oldw+5,nil) listw=oldw filelist:take_focus() end end terminal="aterm +tr -bg white -fg black -geometry 80x4" ww=560; wh=380; bh=30; bw=(ww-10)/3 win=fltk:Fl_Window(ww,wh,"MyDSL Browser") tabs=fltk:Fl_Wizard(0,0,ww,wh-bh-5) tab1=fltk:Fl_Group(0,0,ww,wh-bh-5,"browse") tiles=fltk:Fl_Tile(5,5+bh,ww-10,wh-(15+bh*2)) filelist=fltk:Fl_Hold_Browser(5,5+bh,1,wh-(15+bh*2)) filelist:callback(list_cb) info_display=fltk:Fl_Text_Display(6,5+bh,ww-11,wh-(15+bh*2)) info_display:textfont(fltk.FL_SCREEN) info_display:textsize(12) info_display_buffer=fltk:Fl_Text_Buffer() info_display:buffer(info_display_buffer) fltk:Fl_End() tiles:position(6,nil,ww/3,nil) tiles:callback(function() listw=filelist:w() end) listw=filelist:w() prevheader=fltk:Fl_Button(ww,wh,bh,bh,"&[") prevheader:callback( function() for i=table.getn(header),1,-1 do if header[i] < filelist:value() then filelist:value(header[i]); break end end filelist:take_focus() end) nextheader=fltk:Fl_Button(ww,wh,bh,bh,"&]") nextheader:callback( function() for i=1,table.getn(header) do if header[i] > filelist:value() then filelist:value(header[i]); break end end filelist:take_focus() end) enter=fltk:Fl_Return_Button(ww,wh,10,10) enter:callback(function() filelist:do_callback() end) togglist=fltk:Fl_Button(ww,wh,10,10,"&t") togglist:callback(toggle_list) b_updatedb=fltk:Fl_Button(5,5,bw,bh,"&Update Database") b_updatedb:callback(updatedb) install=fltk:Fl_Button(bw+5,5,bw,bh,"&Install Selected Package") install:callback( function() if myfile then mydslload=os.execute("mydsl-load "..myfile.title.." "..myfile.location) if mydslload~=0 then fltk.fl_alert(myfile.title..":\nDownload or Checksum error!") end end filelist:take_focus() end) install_local=fltk:Fl_Button(bw*2+5,5,bw,bh,"Install &Local Package @DnArrow") install_local:callback( function() local filename=fltk.fl_file_chooser("Browse for a MyDSL file","Mydsl files (*.{dsl,tar.gz,uci,unc})",mydsl_dir,nil) if filename then os.execute("mydsl-load "..filename) end end) fltk:Fl_End() tab2=fltk:Fl_Group(0,0,ww,wh-bh-5,"search text") search_text=fltk:Fl_Input(bh*3+5,5,ww-10-bh*3,bh,"search text ") search_text:when(fltk.FL_WHEN_ENTER_KEY) search_text:callback(do_search) results=fltk:Fl_Hold_Browser(5,5+bh,ww-10,wh-(15+bh*2)) results:column_widths({results:w()/2,150,0}) results:callback( function() if results:value() > 1 and Fl:event()==fltk.FL_RELEASE then pick_search_result() end end) results_cb=fltk:Fl_Return_Button(ww,wh,bh,bh) results_cb:callback(pick_search_result) fltk:Fl_End() fltk:Fl_End() tabbuttons=fltk:Fl_Pack(5,wh-bh,200,bh-5) tabbuttons:type(fltk.FL_HORIZONTAL) tabbuttons:spacing(5) tab_browse=fltk:Fl_Radio_Button(0,0,100,bh-5,"&browse") tab_browse:when(fltk.FL_WHEN_CHANGED) tab_browse:box(fltk.FL_BORDER_BOX); tab_browse:selection_color(7) tab_browse:callback(function() tabs:value(tab1) end) tab_search=fltk:Fl_Radio_Button(0,0,100,bh-5,"text &search") tab_search:when(fltk.FL_WHEN_CHANGED) tab_search:box(fltk.FL_BORDER_BOX); tab_search:selection_color(7) tab_search:callback(function() tabs:value(tab2); Fl:focus(search_text) end) fltk:Fl_End() last_update=fltk:Fl_Box(ww-5,wh-bh-5,5,bh+5) last_update:align(fltk.FL_ALIGN_LEFT) last_update:labelsize(12) win:resizable(tabs) tabs:resizable(tab1) tabs:resizable(tab2) tabs:resizable(tab3) tab1:resizable(tiles) tab2:resizable(results) win:show() find_file=io.open(listfile) if find_file then data=find_file:read("*a") else data="" end if string.find(data,"%w") then find_file:close(); fill_list() else ask4db=fltk.fl_choice("You have no database for online MyDSL apps.\nDo you want to download it now?","No","Yes",NULL) if ask4db==1 then Fl:check(); updatedb() else install:deactivate(); tab_search:deactivate(); tab_browse:deactivate() info_display_buffer:text("\nThere is no MyDSL database installed.\nClick \"Update Database\" \(Alt+U\) to download it.") end end Fl:run() |
Quote (mikshaw @ April 01 2008,10:52) |
Unfortunately mydsl-load failure (such as the "please mount" error we occasionally see) will bring down the whole gui. Does anyone know if FLTK has a means of handling errors more gracefully than just shutting down? |