[Sarnold] This is a GUI tool for launching system commands. You can type command arguments in a text field like you would in a shell. [http://wiki.tcl.tk/_repo/images/Blauncher0.3.png] ---- Documentation You can use it as a GUI front-end for tools like [bc], and pressing Escape cause the last result to be recalled as command argument. It stores a table of command prefixes in its configuration. You can use a few command modifiers to perform special tasks: ====== @dir Title -> prompts for a directory choice and replace them by this choice @temp .... -> stores remaining arguments in a temporary file and replace them by its path (currently under /tmp) @end -> ignores further arguments @bg -> runs command in background (appends '&' to the command line) @wdir /path/to/dir -> runs command with /path/to/dir as work directory, only for the current command ====== ---- News 2010-Apr 4: [Sarnold] releases v0.3. ---- Discussion ---- The source is there: ====== proc DefaultConfig {args} { global HANDLER HELP ASK set HANDLER "" set HELP "" foreach {key val usage} { {Calculator} {bc @temp} {Enter a math expression\nlike 3+2*4 (requires the program 'bc')} {Tfu} {tfu @dir Source @dir Destination @end} {The tfu script which keeps two directories in sync.} } { dict set HANDLER $key $val dict set HELP $key $usage } return "Configuration reset done" } # intercepte l'erreur et relance $msg proc Rethrow {body msg} { if {[catch {uplevel 1 $body} res]} {error $msg} set res } proc Debug {message {level INFO}} { set level [dict get {INFO 0 WARNING 1 ERROR 2} $level] if {$level < 1} return tk_messageBox -message $message set message } proc CommandList {} {lsort [dict keys $::HANDLER]} set DIALOG() "" proc Dialog {name {cmd ""}} { destroy $name if {$cmd ne ""} { eval $cmd } array unset ::DIALOG * } proc CreateAction {} { global HANDLER DIALOG HELP llength $DIALOG(prefix) set cmd $DIALOG(prefix) dict set HANDLER $DIALOG(name) $cmd dict set HELP $DIALOG(name) $DIALOG(usage) } proc ModifyAction {} { NewAction [GetAction $::Current] } proc GetAction {name} { global HANDLER HELP list name $name prefix [dict get $HANDLER $name] usage [dict get $HELP $name] } proc NewAction {{dict ""} {frtitle "Edit action"}} { toplevel .new wm title .new $frtitle global DIALOG foreach {name key} {{Command name} name {Command line prefix} prefix {Usage (help)} usage} { set fr [ttk::frame .new.$key] set DIALOG($key) [Get $dict $key] ttk::label $fr.label -text $name -width 20 ttk::entry $fr.entry -textvariable ::DIALOG($key) -width 36 pack $fr.label $fr.entry -side left -padx 2 -pady 4 -fill both pack $fr } pack [set fr [ttk::frame .new.confirm]] pack [ttk::button $fr.ok -text Ok -command {Dialog .new CreateAction}]\ [ttk::button $fr.delete -text Delete -command {Dialog .new DeleteAction}]\ [ttk::button $fr.cancel -text Cancel -command {Dialog .new}] -side left -padx 2 -pady 4 -fill both } proc Get {dict key {default ""}} { if {[dict exists $dict $key]} {return [dict get $dict $key]} set default } proc SaveConfig {} { catch { set fd [open ~/.blauncherrc w] foreach {key val} $::HANDLER { puts $fd [list $key $val [Get $::HELP $key]] } close $fd } } proc LoadConfig {} { if {![file exists ~/.blauncherrc]} { DefaultConfig SaveConfig return } global HANDLER HELP set HELP "" catch { set fd [open ~/.blauncherrc] while {![eof $fd]} { lassign [gets $fd] key val usage if {$key eq ""} continue dict set HANDLER $key $val dict set HELP $key $usage } close $fd } } proc ResetConfig {} { Confirm [txt "Sure you want to reset?"] { DefaultConfig Show [txt "Reset done"] } } proc Cwd {} { set dir [Ask [txt "Choose working directory"] on] if {$dir ne ""} {cd $dir} } proc Show {text} { tk_messageBox -type ok -message $text -title [txt Message] } # creates a temporary file # with an id (unique within a command Exec) # and a text to fill with proc MakeTemp {id text} { set fname /tmp/blaunch[pid].$id set fd [open $fname w] puts $fd $text close $fd set fname } proc txt arg {set arg};#i18n proc Exec args { set tmp 0 set background no set wdir [pwd] for {set i 0} {$i<[llength $args]} {incr i} { lassign [lrange $args $i end] descr txt if {$descr eq "@dir"} { set f [Ask $txt] if {$f eq ""} {error "Directory choice aborted by user."} set args [lreplace $args $i [expr {$i+1}] $f] } if {$descr eq "@wdir"} { cd $txt set args [lreplace $args $i [expr {$i+1}]] incr i -1 } if {$descr eq "@temp"} { set args [lreplace $args $i end [MakeTemp [incr ::tmp] [lrange $args [incr i] end]]] break } if {$descr eq "@end"} { #ignores remaining arguments set args [lrange $args 0 [expr {$i-1}]] break } if {$descr eq "@bg"} { set args [lreplace $args $i $i] set background yes incr i -1 } } if {$background} {lappend args &} exec {*}$args cd $wdir } proc ? {val def} {expr {$val eq ""?$def:$val}} proc txt? {val def} {? $val [txt $def]} proc Ask {txt {mustexist no}} { if {![info exists ::choosedir]} { tk_chooseDirectory -initialdir [pwd] -title [txt? $txt "Choose directory"] -mustexist $mustexist } else { exec $::choosedir $txt } } proc Blaunch {} { global Current Command HANDLER HISTORY lappend HISTORY $Current $Command if {[catch {Exec {*}[dict get $HANDLER $Current] {*}$Command} result]} { Show "An error occured, see the message below." PutText .result $result return } lappend HISTORY $result PutText .result $result set Command "" } proc PutText {path text} { $path delete 1.0 end $path insert end $text } proc Paste {} { lappend ::Command {*}[split [lindex $::HISTORY end] \n] .command.entry icursor end } proc Quit {} {SaveConfig; exit} proc Exit {} { Confirm [txt "Sure you want to quit?"] Quit } proc UpdateCombobox {args} { .actions.choose configure -values [CommandList] if {$::Current ni [CommandList]} {set ::Current [lindex $::HANDLER 0]} } proc DeleteAction {} { Confirm [format [txt "Are you sure you want to drop action '%s'?"] $::Current] {dict unset ::HANDLER $::Current} } proc Confirm {txt body} { if {[tk_messageBox -message $txt -type okcancel -title [txt Question]] eq "ok"} {eval $body} } proc History {} {Show "Not implemented yet"} proc Clear {} {set ::Command ""} proc Help {} {Show "$::VERSION An enhanced command launcher written in Tcl/Tk. (c) Stéphane Arnold 2010 New BSD license (like Tcl) Press Enter to launch, and Escape to paste last result."} proc Usage {} { global HELP Current Show [dict get $HELP $Current] } proc Focus {} {focus .command.entry} set HISTORY "" set VERSION "Blauncher v0.3" LoadConfig set Current [lindex $HANDLER 0] set Command "" # option database option add *button.padx 7 option add *button.pady 7 option add *button.width 12 option add *entry.font {Courier 10} wm title . $VERSION pack [ttk::frame .actions] pack [ttk::combobox .actions.choose -textvariable ::Current -state readonly -values [CommandList] -width 30]\ [ttk::button .actions.new -text "New action" -command NewAction] \ [ttk::button .actions.modify -text [txt Edit/Delete] -command ModifyAction]\ [ttk::button .actions.reset -text Reset -command ResetConfig]\ [ttk::button .actions.history -text "Usage" -command Usage] -side left -fill both -padx 2 -pady 4 pack [ttk::frame .command] pack [ttk::button .command.clr -text Clear -command Clear]\ [ttk::entry .command.entry -textvariable ::Command -width 40] \ [ttk::button .command.ok -text Blaunch! -command Blaunch] -side left -fill both -padx 2 -pady 4 pack [text .result -width 64 -height 8 -background #fafafa -font {Courier 10}] pack [ttk::frame .sys] pack [ttk::button .sys.recall -text "Paste result" -command Paste] \ [ttk::button .sys.cwd -text "Work directory" -command Cwd] \ [ttk::button .sys.exit -text Exit -command Exit] \ [ttk::button .sys.help -text Help -command Help] -side left -padx 2 -pady 4 trace add variable ::HANDLER write UpdateCombobox bind . Focus bind . Blaunch bind . Paste # avoids selection in combobox (default Tk behavior) bind .actions.choose <> {.actions.choose selection clear} Focus # program name for choosing a directory # comment it for standard tk_chooseDirectory #set choosedir choosedir # Save config on exit (when delete main window) wm protocol . WM_DELETE_WINDOW Exit ====== <> Shell|GUI