An excerpt from the manual page:
NAME
buttonfly - a pretty user interface for Silicon Graphics demos
SYNOPSIS
buttonfly [ menufile ]
DESCRIPTION
Buttonfly is a user reconfigurable, hierarchical, graphical menu system.
Buttonfly opens up a window and displays one or more buttons, each with
its own title. Moving the mouse pointer over a button and pressing the
left mouse button selects that button, and will cause it to either exe-
cute UNIX commands (such as running a program) or fly forward and flip
over, exposing one or more new buttons. Each of the newly exposed but-
tons may also be selected to either execute UNIX commands or expose a new
level of buttons, and so on.
To go back to the previous set of buttons move the mouse pointer so it
isn't over any of the buttons, and press the left mouse button (i.e.
select the background). The buttons will flip over backwards to bring up
the previous set. Selecting the background at the top level does noth-
ing.Here are screenshots from the Tk version (no 3D currently):

#!/bin/sh
# The next line restarts using wish \
exec wish "$0" -- ${1+"$@"}
# tkButtonFly: A Tk-based replacement of SGI's buttonfly program.
# Notice: Currently not supported:
# ".popup." and ".menu. -".
# Toggling between window and fullscreen mode.
# No 3D buttons - and therfore no "fly".
set auto_path [linsert $auto_path 0 [file dirname [info script]]]
set auto_path [linsert $auto_path 0 [file dirname [info nameofexecutable]]]
# Some functions to implement a simple toolhelp.
proc poToolhelp:Init { w { bgColor yellow } { fgColor black } } {
if { [winfo exists $w] } {
destroy $w
}
toplevel $w
set ::topWidget $w
label $w.l -text "This is toolhelp" -bg $bgColor -fg $fgColor -relief ridge
pack $w.l
wm overrideredirect $w true
wm geometry $w [format "+%d+%d" -100 -100]
}
proc poToolhelp:ShowToolhelp { x y str } {
$::topWidget.l configure -text $str
raise $::topWidget
wm geometry $::topWidget [format "+%d+%d" $x [expr $y +10]]
}
proc poToolhelp:HideToolhelp {} {
wm geometry $::topWidget [format "+%d+%d" -100 -100]
}
proc poToolhelp:AddBinding { w str } {
if { ![info exists ::topWidget]} {
poToolhelp:Init .poToolhelp
}
bind $w <Enter> "poToolhelp:ShowToolhelp %X %Y [list $str]"
bind $w <Leave> { poToolhelp:HideToolhelp }
bind $w <Button> { poToolhelp:HideToolhelp }
}
# Convert color value from range [0..1] into [0..255].
proc f2i { col } {
return [expr int($col * 255.0)]
}
# Convert color given as (r, g, b) into #RRGGBB.
proc rgb2hex { r g b } {
return [format "#%02X%02X%02X" [f2i $r] [f2i $g] [f2i $b]]
}
# Parse the config file fileName to build up the panel information in gMenu.
proc parseFile { fileName } {
global gMenu gFile gBtn gCurDir
set retVal [catch {open $fileName r} inFp]
if { $retVal != 0 } {
tk_messageBox -message "Cannot read file $fileName" \
-icon warning -type ok
return
}
while { [gets $inFp line] >= 0 } {
if { [string match "#*" $line] } {
# Comment line. Do nothing.
} elseif { [string match "\t*" $line] } {
# Line starts with a tab. Should be one of the defined dot-commands
# or the action for the button.
if { [string length [string trim $line]] == 0 } {
# Blank or whitespaced line. Do nothing.
} elseif { [string match "\t.color.*" $line] } {
scan $line " %s %f %f %f" dummy r g b
set ::gMenu($::gFile,$::gBtn,fore) [rgb2hex $r $g $b]
} elseif { [string match "\t.backcolor.*" $line] } {
scan $line " %s %f %f %f" dummy r g b
set ::gMenu($::gFile,$::gBtn,back) [rgb2hex $r $g $b]
} elseif { [string match "\t.highcolor.*" $line] } {
scan $line " %s %f %f %f" dummy r g b
set ::gMenu($::gFile,$::gBtn,high) [rgb2hex $r $g $b]
} elseif { [string match "\t.cd.*" $line] } {
scan $line " %s %s" dummy dir
if { [string compare [file pathtype $dir] "relative"] == 0 } {
set dir [file join $gCurDir $dir]
}
set ::gMenu($::gFile,$::gBtn,cd) [list $dir]
} elseif { [string match "\t.menu.*" $line] } {
scan $line " %s %s" dummy menuName
set ::gMenu($::gFile,$::gBtn,menu) $menuName
} elseif { [string match "\t.popup.*" $line] } {
scan $line " %s %s" dummy popupTitle
set ::gMenu($::gFile,$::gBtn,popup) $popupTitle
tk_messageBox -message "Popup menus not supported." \
-icon warning -type ok
} else {
set gMenu($gFile,$gBtn,cmd) [string trimleft $line]
}
} else {
if { [string length [string trim $line]] != 0 } {
# This must be the button's name.
lappend gMenu($gFile,btns) $line
set gBtn $line
# Initialize buttons attributes with default values.
set gMenu($gFile,$gBtn,high) "#CCCCCC" ; # Active color
set gMenu($gFile,$gBtn,fore) "#AAAAAA" ; # Button color
set gMenu($gFile,$gBtn,back) "#444444" ; # Frame background
set gMenu($gFile,$gBtn,cd) "$gCurDir" ; # Dir to cd to
set gMenu($gFile,$gBtn,cmd) "" ; # Default command
}
}
}
}
# Read the config file fileName.
# If there were references to other panels via .menu.,
# recursively parse these files, too.
proc readFile { parent fileName } {
global gMenu gFile gBtn gScanned
set gFile $fileName
parseFile $fileName
lappend gScanned $fileName
set gMenu($gFile,parent) $parent
foreach f [array names gMenu $fileName,*,menu] {
set foundInd [lsearch -exact $gScanned $gMenu($f)]
if { $foundInd < 0 } {
readFile $fileName $gMenu($f)
}
}
}
# Build a button panel into frame $fr according to config file fileName.
# The background of the panel's parent frame will be set to frColor.
proc buildPanel { fr fileName { frColor "#444444" } } {
global gMenu gFile gBtn
# Destroy an existing panel frame (and all contained buttons)
# and create a new one.
set newFr $fr.f
catch { destroy $newFr }
frame $newFr -bg $frColor
pack $newFr -expand 1 -fill both
if { [string compare $gMenu($fileName,parent) ""] != 0 } {
bind Frame <ButtonRelease-1> "buildPanel $fr $gMenu($fileName,parent)"
}
# Step through the list of button names for this panel and create
# button widgets with corresponding colors. Add a binding either for
# creating a new panel via buildPanel or for executing a command.
set ind 0
set row 0
set col 0
set btnList $gMenu($fileName,btns)
set noCols [expr int (ceil (sqrt ([llength $btnList])))]
set noRows [expr int (ceil (double ([llength $btnList]) / $noCols))]
for { set c 0 } { $c < $noCols } { incr c } {
grid columnconfigure $newFr $c -weight 1
}
for { set r 0 } { $r < $noRows } { incr r } {
grid rowconfigure $newFr $r -weight 1
}
foreach btnName $btnList {
set indStr "$fileName,$btnName"
button $newFr.b$ind \
-text $btnName \
-bg $gMenu($indStr,fore) \
-activebackground $gMenu($indStr,high)
if { [info exists gMenu($indStr,menu)] } {
$newFr.b$ind configure -command \
"buildPanel $fr $gMenu($indStr,menu) $gMenu($indStr,back)"
poToolhelp:AddBinding $newFr.b$ind "Goto: $btnName"
} else {
$newFr.b$ind configure -command \
[list execCmd $gMenu($indStr,cd) $gMenu($indStr,cmd)]
poToolhelp:AddBinding $newFr.b$ind "Exec: $btnName"
}
grid $newFr.b$ind -sticky news -row $row -column $col -padx 10 -pady 10
incr col
if { $col >= $noCols } {
set col 0
incr row
}
incr ind
}
}
proc execCmd { dir cmd } {
if { [string compare $cmd ""] == 0 } {
tk_messageBox -message "No command specified. Check config file." \
-icon warning -type ok
}
cd $dir
eval exec $cmd &
}
# Start of program. Check command line arguments first.
set optFullScreen 0
set inFile ".menu"
set curArg 0
if { $argc >= 1 } {
while { $curArg < $argc } {
set curParam [lindex $argv $curArg]
if { [string compare -length 1 $curParam "-"] == 0 } {
if { [string compare $curParam "-f"] == 0 } {
# Fullscreen mode does not work correctly under Linux/KDE.
set optFullScreen 1
}
} else {
set inFile $curParam
}
incr curArg
}
}
if { ![file readable $inFile] } {
tk_messageBox -message "Start configuration file $inFile not found." \
-icon warning -type ok
exit 1
}
# Set initial values for variables needed for reading the config files.
set gCurDir [pwd]
set gFile $inFile
set gBtn "tkButtonFly"
set gScanned [list]
# Now (recursively) read all config files and enter all relevant information
# into array gMenu.
readFile "" $inFile
# Create window title and main frame.
wm title . "tkButtonFly"
wm minsize . 100 100
wm geometry . "400x300"
frame .f
pack .f -expand 1 -fill both
# Exit tkButtonFly with Escape.
bind all <KeyPress-Escape> exit
# Build the first button panel.
buildPanel .f $inFile
if { $optFullScreen } {
set xmax [winfo screenwidth .]
set ymax [winfo screenheight .]
bind all <Button-3> exit
wm geometry . [format "%dx%d+0+0" $xmax $ymax]
wm overrideredirect . 1
}
update
raise .
# We are in the event loop now.Test files for Linux:
# Save this in a file called 'menu'
# Check for tabs on indented lines.
Editors
.menu. editors
Image viewers
.color. 0 0.6 0
.backcolor. 0 0.8 0
.highcolor. 0 0.8 0
.menu. imageViewers
View system files
.color. 0.6 0 0
.backcolor. 0.8 0 0
.highcolor. 0.8 0 0
.menu. systemDir
# Save this in a file called 'editors'
# Check for tabs on indented lines.
Graphical ViM
gvim
Emacs
emacs
KDE Editor
kedit
KDE Write
kwrite
# Save this in a file called 'imageViewers'
# Check for tabs on indented lines.
Start gimp.
gimp
Start xv.
xv
# Save this in a file called 'systemDir'
# Check for tabs on indented lines.
User related files.
.menu. systemUser
Network related files.
.menu. systemNet
# Save this in a file called 'systemUser'
# Check for tabs on indented lines.
View password file.
gvim -R /etc/passwd
View group file.
.cd. /etc
gvim group
Go back to main panel.
.menu. menu
# Save this in a file called 'systemNet'
# Check for tabs on indented lines.
View hosts file.
.cd. /etc
gvim hosts
View fstab file.
gvim /etc/fstab
Go back to main panel.
.menu. menu
