lennartb@lne.kth.se (Lennart Brjeson @ KTH, Stockholm) (03/12/91)
**HP48** This is SWING for the HP48. SWING was named after an old PD utility for VMS. SWING draws a tree representing your entire directory structure and lets you walk around in it using the cursor key. SWING "precompiles" the tree so it can be redrawn easily, this means that you must execute the SWSAVE routine anytime you have created or deleted directories if the SWING tree is to be correct. SWING is written in user RPL but I myself find the speed acceptable. SWING must be installed as a library (or be placed in the HOME directory) in order to work. Exit SWING by pressing ENTER, this will change your path to the one indicated. RUBOUT (key 55) quits without changing the path. SWING is assigned library 1644, BTW. Library and source follows. The library in ASC format: --Cut here----------------------------- %%HP: T(3)A(D)F(.); "04B201171011357594E47402269702C456E6E61627472411C664100000000DC0 002A610E4A20EB00000000000000000064000E40006700000000000000000000 00000000000000000000000000000000005000404524C44430050357594E4740 005035757454452005074C424C44440060357535146554100E30006100093000 A500043000E4A20F50001600039100872004F20053300F46000770029800F690 0DF900B701098210A0310DD310C74103C41035510B75108C66000D9D20E16323 CE22491A14563284E204005051425976324BAC1AFE22D9D2084E204005051425 9C2A2387C147A2084E204005051425DCC02B213076BA1B21305BF2247A2047A2 084E204005051425B2130EFE02B21305DF2229E20C66500521A1779200000000 000000000000000000000000029E20C669003FBF1E0CF1A59C18DBF1EB3A13CE 22AFE22EB3A15BF228DBF15DF2293632B21308C66100D9D20E1632521A1041A1 29E20C66300E4A2051000000000000000000078BF12ABF129E20C66400E4A205 1000040000000000000029E20C66010DBBF1E4A2051000380000000000000029 E20C66010DBBF13F2A2387C14563284E2090357796E6764416471697632DCC02 EB3A193632B21308C66200D9D20E1632FD33284E2090357796E67644164716F1 732D9D208DBF129E20C6610029E20C66200B21305BF22D9D20B7FC18DBF1B213 05DF2293632B21308C66300D9D20E1632521A147A20041A1B213029E20C66700 DBBF1EB3A193632B2130000C66400D9D20E16321C432D6E2020D687D6E2020D6 97D6E201087D6E201097E1632B7FC18DBF1DBBF178BF19C2A2387C1B0BC13F2A 292CF18B9C1ED2A290DA1C58C1D6E201087D6E201097ED2A2387C1DBBF13F2A2 387C178BF13F2A26C7D129E20C668008B9C1D6E20109776BA1D6E2020D69729E 20C6601045632D6E2020D69797632DCC02D6E20108776BA1803A276BA145632D 6E20108797632DCC02D6E201087D6E2020D68729E20C6601045632D6E2020D68 797632DCC02DBBF1B7FC11C432D6E201037E16323CE22D6E201037AFE22D9D20 9C2A2D6E20103730132D6E2010375BCF1D6E2010375BCF1ED2A2387C1D6E2020 D687D6E2020D697D6E201087D6E20109729E20C66400D6E2020D69729E20C660 1045632D6E2020D69797632DCC02D6E2020D68729E20C6601045632D6E2020D6 8797632DCC02D6E2020D69745632D6E20109797632DCC0247A20B2130ED2A208 33247A20B21309C2A2D6E2010373013276BA1C4232B21305BF2247A20B21305D F229C2A2387C176BA1EF532D6E2020D687D6E2020D697EF53293632B2130D9D2 0E163229E20C662002ABF1614E1634E1DCC02BB6919C2A290DA14B2A2DBBF1E9 7C1DBBF1BB6919C2A290DA14B2A2E97C1ED2A2387C147A2084E2010854B2A277 92000000000000000000000000000000000166E184E201095B213076BA145632 84E20400505142597632DCC02743A24A5A177920000000000000000000000000 000000000F2E129E20C6660093632B2130D9D20E1632A59C11C432D6E201037E 16323CE22D6E201037AFE22D9D209C2A2D6E20103730132D6E2010375BCF1D6E 2010375BCF1A72E1D6E2010375BCF12ABF1634E1E0CF1E0CF129E20C66800AE8 C192CF129E20C66D00D6E2010375BCF129E20C66600803A208332D6E2010379C 2A290DA1A9CF1803A2A9CF129E20C66C00B21305DF22D6E201037387C1EF5329 3632B2130D9D20E163278BF1EB3A1339201000000000000510FA1A11C432D6E2 01067E163247A20B21304B2A2D6E2010678B9C10A132D6E2010963CE22D6E201 096AFE22D9D20D6E201067D6E20109678BF1C58C129E20C6670076BA1B21305D F22C42329C2A2387C176BA1EF532B51A193632B2130D9D20E16329C2A2DA5E17 8BF18B9C19C2A276BA1DBBF19C2A276BA1DBBF1614E147A20E4A205100010000 00000000000E4A20510001000000000000000B2130E0CF1AE8C193632B2130D9 D20E1632DBBF178BF19C2A29C2A2C58C1DBBF1ED2A292CF18B9C1C58C14B2A27 8BF11C432D6E201046D6E20200767D6E2020C613D6E2020C623D6E201027D6E2 01056E16323C032D6E201046D6E2020C613B7FC18DBF14BAC178BF11C432D6E2 01007D6E20200707E16323C0323CE22D6E2020C6238B9C1AFE22D9D20D6E2010 46D6E2010073F2A276BA16C7D1D6E2020C623D6E2020076729E20C6690045632 D6E2020076797632DCC023CE2278BF1F88E1AFE22D9D203FBF14B2A278BF1B21 305DF2245632D6E20105697632DCC0245632D6E20102797632DCC0245632D6E2 020C62397632DCC02B21305BF22D9D20634E1D6E201046D6E2010077C8D14563 2D6E2020C613976329C2A2E0CF1704D17C8D1E0CF1E0CF16C7D129E20C668005 99A1D6E2020076729E20C66A0045632D6E2020076797632DCC024B2A2F17A1D6 BB11C432D6E2010B6E1632D8732D9D20D6E2010B633920100000000000052016 7E1D6E2010B6339201000000000000530167E1908E18A732D9D20D6E2010B633 920100000000000003090DA1D13A250FA1803A2EEDA1D6E20100776BA19C2A29 0DA1D6E2010468B9C1D4EB19C2A276BA145632D6E2020070797632DCC02B2130 5DF22D6E2010B6339201000000000000430167E18A732D9D2047A20B21304563 2D6E2020C61397632DCC029C2A245632D6E20102797632DCC02B21305DF22D6E 2010B6339201000000000000630167E18A732D9D20D6E201046D6E2010073F2A 276BA16C7D19C2A278BF1C58C145632D6E2020C62397632DCC02B21305DF22D6 E2010B6339201000000000000150167E18A732D9D209C2A245632D6E20102797 632DCC029C2A245632D6E20105697632DCC02B21305DF22D6E2010B633920100 0000000000550167E18A732D9D209C2A245632D6E20105697632DCC02B21305D F223392030000000000420103392099900000000005204C5A1B21305DF22EF53 2634E1D6E201046D6E2010079C2A276BA17C8D1E0CF1E0CF16C7D129E20C6680 0AE8C1D6E2020070745632D6E20100797632DCC02B21305DF22DE032D6E20102 7D6E201056908E19B632EF532DE032D6E201027D6E201056908E19B632D6E202 0C613D6E2020C62376BA1D6E201027D6E201056D6E20200767EF53293632B213 0D9D20E1632E89C11C432D6E20200787D6E20200797E1632E0CF13F2A2A9CF13 F2A2A9CF1AE8C18B9C1E0CF1E89C11C432D6E201077D6E201086D6E20207687D 6E20207697E163247A20EBBE1B2130D6E2020768745632D6E202007879763229 E20C66B0047A20EBBE1B2130D6E2020769745632D6E202007979763229E20C66 B0047A20D5CE1B2130D6E20207687D6E201077BB69176BA1803A276BA1339202 00000000000131090DA145632D6E202007879763229E20C66B0047A20D5CE1B2 130D6E20207697D6E201086BB69176BA133920100000000000046090DA145632 D6E202007979763229E20C66B00D6E20200787D6E20200797E97C178BF10F2E1 EF532EF53293632B2130D9D20E16321C432D6E2010E6D6E201067E16323CE22D 6E2010E6D6E201067EB3A1E0CF1EB3A1AFE22D9D20D6E2010E6D6E201067DCC0 2B21305DF22EF53293632B2130D9D20E16323CE2278BF1E89C18DBF1AFE22D9D 20779200000000000000039000000000000002076BA1DBBF178BF17792000000 00000000049000000000000002076BA1A13E1779200000000000000039000000 000000002076BA1893E1B21305BF223FBF15DF2293632B2130D9D20E16323CE2 278BF1E89C18DBF1AFE22D9D2077920000000000000002900000000000000207 6BA178BF1779200000000000000019000000000000000076BA1893E1B21305BF 228DBF15DF2293632B213047A2029E20C6600029E20C6650029E20C6610029E2 0C6620029E20C6630029E20C66400B213047A207792000000000000005690000 000000000139779200000000000000560000000000000023084E2010854B2A27 792000000000000000000000000000000000166E184E201095B2130D9D20E163 2BB691DBBF1BB69117CB1B969193632B2130D9D20E1632339203000000000044 6108441293632B2130EC755EA0" --Cut here----------------------------- The source directory: --Cut here----------------------------- %%HP: T(3)A(D)F(.); DIR SWING @ Main routine. No arguments. \<< IF VARS 'PPAR' POS @ Check for existence of PPAR THEN @ PPAR exits, save it in post-swing procedure PPAR 1 \->LIST { PPAR STO } + ELSE @ doesn't exist, purge it in post-swing proc. { { PPAR } PURGE } END SHOWDIR @ Draw tree PATH (0,0) SWSUB @ Start interaction at current path DROP2 ROT LIST\-> DROP EVAL @ Drop position, execute post-swing procedure IF @ If normal exit, change path, else drop it THEN EVAL ELSE DROP END \>> SWSAVE @ Pre-compile tree. No arguments. \<< PATH HOME @ Save current path, goto HOME TBLD @ Build list of directories # 0h DUP DUP2 GLBLD @ Convert list to graphic list # 40h MAXB SWAP @ PICT must be at least 131x64 # 83h MAXB SWAP 3 \->LIST 'SwingData' STO @ Save in SwingData EVAL @ Return to old path \>> SWGET @ Get SwingData. No arguments. \<< IFERR SwingData @ Check existence of SwingData THEN DROP SWSAVE SWGET @ If not, create ELSE OBJ\-> DROP @ Exists, extract graphic list and PICT size END \>> TBLD @ Build directory table (list). No arguments. \<< PATH @ Save current path { HOME } RBLD @ Start at HOME, call recursive builder SWAP EVAL @ Restore path \>> GLBLD @ Graphic list builder. @ Directory list = {[Dirname Directory_list_of_subdirs]...} @ Inputs: 5: Directory table from TBLD @ 4: Max x, so far (binary) @ 3: Max y, so far (binary) @ 2: x position (binary) @ 1: y position (binary) @ Outputs:3: Graphic list @ 2: Max x @ 1: Max y @ Graphic list = {[Dirname Dirpos Dirstr graphiclist_of_subdirs]...} \<< \-> mx my x y \<< OBJ\-> DROP @ The HOME directory table has only one entry, @ GLBLD will call itself with one-entry-lists @ only, so we can drop the size. @ To convert Dirname to a GROB, we can't just @ do \->GROB, since HOME \->STR will result @ in "HOME", but FOOBAR \->STR will result in @ "'FOOBAR'". The current workaround is to @ put the name in a list, convert the list to @ a string and to extract the relevant part. SWAP DUP 1 \->LIST @ Put Dirname into a list \->STR @ Convert to string 3 OVER SIZE 2 - SUB @ Extract "Dirname", i.e. Dirstr x y 2 \->LIST @ Make the coord {x y}, i.e. Dirpos SWAP 3 \->LIST @ Make {Dirname Dirpos Dirstr} DUP 3 GET EGROB SIZE @ Get size of the GROB of Dirstr y + my MAXB 'my' STO @ Update maximum y x + 4 + 'x' STO @ Update x x mx MAXB 'mx' STO @ Update maximum x SWAP OBJ\-> \-> s @ Explode directory_list_of_subdirs \<< IF s @ If any subdirs at all... THEN 1 s @ ...loop over all subdirs START s ROLL s ROLL @ Get next subdir 2 \->LIST @ Make it a one-entry subdir_list mx my x y GLBLD @ Call GLBLD recursively my MAXB 'my' STO @ Update maximum y mx MAXB 'mx' STO @ Update maximum x my 'y' STO @ Update y { } 2 @ Dummy list to keep stack depth, loop step STEP { } 1 s @ Concatenate the s graphic_subdir_lists START + @ on the stack NEXT ELSE { } @ No subdirs, return empty list END 1 \->LIST + @ Make {Dirname Dirpos Dirstr subdir_list} \>> mx my @ Return cuurent maximum x and y \>> \>> SHOWDIR @ Draws the tree. No arguments. \<< SWGET @ Get SwingData DUP2 BLANK PICT STO @ Set size of PICT B\->R 1 - 0 SWAP R\->C @ Lower left corner in user coords SWAP B\->R 1 - 0 R\->C @ Upper right in user coords 2 \->LIST { X 0 (0,0) @ Make new PPAR FUNCTION Y } + 'PPAR' STO 7 FREEZE @ Freeze display so we don't get clock! (0,0) PVIEW SHWSUB @ Show PICT and call SHWSUB \>> SHWSUB @ Recursive tree drawer. @ Input: 1: Graphic list. @ Graphic list = {[Dirname Dirpos Dirstr graphiclist_of_subdirs]...} @ Output:1: Graphic list. (Pos changed to user coords.) \<< LIST\-> \-> s @ Explode list \<< IF s @ If non-empty list... THEN 1 s @ ...walk through all elements START s ROLL s ROLL @ Get Dirname and Dirpos PX\->C @ Convert Dirpos to user coord s ROLL DUP2 @ Get Dirstr, save Dirpos_u and Dirstr PICT ROT ROT EGROB @ Make PICT Dirpos_u DirGROB REPL @ Put GROB in PICT OVER DRAWTAG @ Draw a small horizontal tag at Dirpos s ROLL SHWSUB @ Get subdir_list and draw it recurs. 4 @ Loop step STEP s 1 - PICK @ Get pos of first subdir 4 PICK @ Get pos of last subdir DRAWBAR @ Draw vertical bar from 1:st to last END s \->LIST @ Rebuild Graphic list \>> \>> RBLD @ Recursive builder. @ Input: 1: Single directory name. @ Output: 1: Directory list. @ Directory list = {[Dirname Directory_list_of_subdirs]...} \<< DUP @ Save directory name EVAL @ Enter directory 15 TVARS \-> v @ Get list of subdirs \<< { } 0 v SIZE @ Loop over all subdirs FOR i IF i @ If any subdir THEN v i DUP SUB @ Get subdir # i RBLD @ Call builder recursively + @ Concatenate list END NEXT 1 \->LIST @ Make subdir_list + @ Make {Dirname subdir_list} \>> UPDIR @ Return to directory above \>> EGROB @ Extended GROB. Same as 1 \->GROB, only the grob is extended @ one row above and one column on the right. @ Input: 1: Any. @ Output: 1: Grob. \<< 1 \->GROB @ Make original grob DUP SIZE 1 + SWAP 1 + SWAP @ Get size + 1 BLANK { # 1h # 1h } ROT REPL @ Extend grob \>> SWSUB @ The interactive swing subroutine which traverses the tree. @ One complicating fact is that we may have to 'pan' the PICT around @ if it is bigger than the screen. The first-level argument keeps track @ of the current point to PVIEW. @ Inputs: 3: Graphic list @ 2: Path of directory to traverse to @ 1: PVIEW point @ Outputs:4: Updated path @ 3: return-flag @ 2: exit-flag @ 1: Updated PVIEW point \<< SWAP @ Get path DUP 1 1 SUB @ Get {1:st-dir-in-path} SWAP 2 OVER SIZE SUB @ Get {rest-of-path} 0 DUP \-> d pv l1 l2 r e @ Save gr_list, pview_point, @ sublist1, sublist2, @ and init return-flag and exit-flag @ sublist1 is always 1 element long @ sublist2 is the subpath that remains @ to be traversed @ return-flag and exit-flag are used @ as follows: @ r | e | meaning @ --------------- @ 0 | 0 | Continue on current level @ 0 | 1 | Exit all, don't set path @ 1 | 0 | Exit current level only @ 1 | 1 | Exit all, set path \<< DO d @ graphic list l1 OBJ\-> DROP @ get 1:st dir in path POS @ find position in graphic list DUP \-> p pp @ save in p and pp \<< DO @ Do while not (exit or return) IF l2 SIZE @ Are there dirs left in path? THEN @ Yes, traverse down in tree d p 3 + GET @ Get graphic list of subdir l2 pv @ Get subpath and pview point SWSUB @ Call SWSUB recursively 'pv' STO @ Update pview point IF DUP NOT @ If not exit-flag... THEN DROP2 0 DUP @ ...then don't exit further END 'e' STO @ Update exit-flag 'r' STO @ Update return-flag 'l2' STO @ Update subpath ELSE @ No, reverse (black) cur. dir in tree PICT @ PICT on stack d p GETI @ Put current Dirname... 'l1' 1 ROT PUT @ ... in sublist1 GETI @ Get current Dirpos... ROT ROT GET @ ... and current Dirstr EGROB NEG @ Make a reversed GROB of the Dirstr pv SWREPL @ SWREPL is like REPL, but it also @ pans PICT if any part the current @ dir is invisible 'pv' STO @ Update PVIEW point 0 WAIT @ Wait for keypress IP \-> k @ Save key code in k \<< CASE k 25 SAME @ If 'Up'- or... k 35 SAME OR @ 'Down'-key THEN k 30 - 5 / 4 * @ -4 if 'Up', +4 if 'Down' p + @ New p 1 - d SIZE MOD 1 +@ Wrap around 'pp' STO @ Save new p in pp END k 34 SAME @ If 'Left'-key THEN { } 'l1' STO @ Blank out sublist1 @ (Sublist2 is already empty, @ so this will make the current @ subpath empty - which will make @ SWSUB remain at the level above @ the current.) 1 'r' STO @ Set return-flag END k 36 SAME @ If 'Right'-key THEN d p 3 + GET @ Get graphic list of subdirs 1 DUP SUB @ Get 1:st subdir 'l2' STO @ Save in sublist2 (Which will make @ SWSUB to traverse down one level @ in the next loop) END k 51 SAME @ If 'Enter'-key THEN 1 'r' STO @ Set return-flag and... 1 'e' STO @ ...and exit-flag END k 55 SAME @ If 'RubOut'-key THEN 1 'e' STO @ Set exit-flag END 1024 .25 BEEP @ Beep on all other keys END @ End of CASE \>> @ Exit, key dispatch, render dir normal PICT @ Save PICT on stack d p 1 + GETI @ Get current Dirpos... ROT ROT GET @ ...and Dirstr EGROB REPL @ Make a GROB and put it in PICT pp 'p' STO @ Update new p END @ END of IF any subdirs UNTIL r e OR @ Exit if return or exit END \>> UNTIL r e OR @ Exit if return or exit END l1 l2 + r e pv @ Exit SWSUB, return path, @ return-flag, exit-flag and @ current PVIEW point \>> \>> SWREPL @ SWREPL is like REPL, but it will also pan PICT if any part of @ the current dir is invisible. @ Inputs: 4: PICT @ 3: coord @ 2: GROB @ 1: PVIEW point @ Output: 1: New PVIEW point \<< C\->R \-> px py @ Explode PVIEW point \<< ROT 3 PICK 3 PICK REPL @ Save coor and GROB, do REPL SIZE @ Get size of GROB ROT C\->R @ Explode coord \-> w h gx gy @ Save width and height and coords \<< { < } gx 'px' SWCLIP @ Clip on left edge of screen { < } gy 'py' SWCLIP @ Clip on top edge of screen { > } gx w B\->R + 4 + 131 - @ Right limit of GROB 'px' SWCLIP @ Clip on right edge of edge { > } gy h B\->R + 64 - @ Bottom limit of GROB 'py' SWCLIP @ Clip on bottom edge of screen px py R\->C @ New PVIEW point DUP PVIEW @ Save it on stack, do PVIEW \>> \>> \>> SWCLIP @ Clips a variable to a value @ Inputs: 3: Condition operator @ 2: Value number @ 1: Variable name @ No output. \<< \-> n v @ Save number and variable \<< IF n @ Get number v EVAL @ Get value in variable ROT EVAL @ Evaluate condition THEN n v STO @ If true, store new value END \>> \>> DRAWBAR @ Draws a line between two directory positions @ (The bar is always vertical, from the first to the last @ subdirectory) @ Inputs: 2: Coord of first subdir @ 1: Coord of last subdir @ No output. \<< IF DUP C\->R DROP @ If x coord nonzero then... THEN (-3,2) + @ Offset last pos SWAP DUP @ Get and save first pos (-4,2) + PIXON @ Extend the tag at 1:st dir (-3,2) + @ Offset last pos LINE @ Draw the bar ELSE DROP2 @ ...else do nothing END \>> DRAWTAG @ Draws a short tag at the left of a directory @ Input: 1: Coord of dir @ No output. \<< IF DUP C\->R DROP @ If x coord nonzero then... THEN (-2,2) + @ Offset start position DUP (-1,0) + @ Offset stop position LINE @ Draw the tag ELSE DROP @ ...else do nothing END \>> CST @ Custom menu. Not included in library. { SWING SHOWDIR SWSAVE SWGET TBLD GLBLD } PPAR @ Default PPAR. Not included in library. { (-6.5,-3.1) (6.5,3.2) X 0 (0,0) FUNCTION Y } MAXB @ Maximum of binaries. @ Inputs: 2: Binary @ 1: Binary @ Output: 1: Max binary. \<< B\->R SWAP B\->R MAX R\->B \>> $TITLE "SWING by LennartB" $VISIBLE { SWING SWSAVE SWGET TBLD GLBLD } $CONFIG \<< 1644 ATTACH \>> $ROMID 1644 $VARS { CST PPAR SwingData } END --Cut here----------------------------- !++ ! Lennart Boerjeson, System Manager ! School of Electrical Engineering ! Royal Institute of Technology ! S-100 44 Stockholm, Sweden ! tel: int+46-8-7907814 ! Internet: lennartb@lne.kth.se !-- !++ ! Lennart Boerjeson, System Manager ! School of Electrical Engineering ! Royal Institute of Technology ! S-100 44 Stockholm, Sweden ! tel: int+46-8-7907814 ! Internet: lennartb@lne.kth.se !--