With a compiler, and a modified tclAppInit, it is of course pretty easy to make a executable to run a particular set of tcl/tk code. However, it is nice to be able to write pure tcl/tk as much as possible to accomplish the same task. There are a few aspects to this: * provide an icon for the application which can be double-clicked on to run * allow drag-n-drop of documents onto the icon to interact with them * allow other applications to send files to the application. * ... Obviously there may be some platform-specific aspects to correct "native" behaviour as well (for example on Windows, we might want to add a shortcut to the application and its help/readme to the Start->Programs menu). '''Windows''' On Windows, any .tcl file can be double-click to launch wish by default. We can also easily add the application to the Programs menu as follows: windows::CreateGroup Alphatk ~/Apps/Alphatk8.1 alphatk.tcl AlphaCore/alpha.icr package require dde proc windows::CreateGroup {name root script icon} { # This won't work with a scripted document. In that case # we will have to be a bit cleverer... windows::ProgmanExecute CreateGroup $name windows::ProgmanExecute AddItem [file join $root $script] \ $name $iconfile windows::ProgmanExecute AddItem [file join $root Readme.txt] \ Readme } proc windows::ProgmanExecute {name args} { eval [list windows::DdeExecute PROGMAN PROGMAN $name] $args } proc windows::DdeExecute {service topic name args} { set cmd "\[$name\(" set subcmds [list] foreach a $args { lappend subcmds "\"$a\"" } append cmd [join $subcmds ","] "\)\]" dde execute $service $topic $cmd } But how can we allow 'drag-n-drop' onto an icon for our application? '''Unix''' ''please fill in'' '''MacOS''' ''please fill in'' '''MacOS X''' The WishShell application on MacOS X is actual a 'bundle' -- it's a directory of stuff pretending to be an application. If you go inside that directory (which you can do from the command-line shell), you will find a directory 'Scripts'. Place a file "AppMain.tcl" in there, and that file will be sourced when you run the application -- you now have a new application. You can also replace the application icon (there is Wish.icns in the bundle). I'm not sure whether you can change the application menu name from "Wish" without recompiling, however... If you write a proc "proc tk::mac::OpenDocument {args} {...}" it will be called with the list of file names any time files are dropped onto the Wish application, or "open document" (odoc) events are sent. ''please add more'' ---- [[Wasn't someone intending to write up a bunch of [options] to mollify Tk's appearance? I remember things like fiddling with borderwidths on Windows, and so on ...]] ---- '''Windows look and feel''' ''Button behaviour in a modal dialog'' Here is a simple dialog with three buttons, one disabled, complete with accelerator keys for all of the buttons. To give the widget some substance (and to test the button behaviour more completely) I also added a check box, and an entry, plus a label to set the frame size. Using -uniform, the buttons are all the same width. The accelerators are hard-coded, which is bad for internationalization. They are also bound to all, which will not work when there are multiple dialogs. Please correct appropriately. The position of the check box square is different from a similar box in the IE internet options dialog, where the square is flush left with the preceding and following lines. Using "checkbutton .af.activate -bg blue \" will show that the offending space is in the checkbutton. I can't get rid of it by setting -padx 0. Keyboard traversal works, but there is a problem: the button which will be activated when return is pressed should be the one which has -default active, and no others. I've hacked around this with complicated dialog specific FocusIn/FocusOut bindings, but that is truly gross. Unlike the usual Windows dialogs, this one can be resized. proc inform {msg} { tk_messageBox -type ok -message $msg } set data {} set activate_apply false labelframe .af -text Hello label .af.msg -text "Here is a longish label so that I have room for buttons." entry .af.data -textvariable data checkbutton .af.activate \ -text "Some action" -underline 0 -variable activate_apply \ -command { if { $activate_apply } { .bf.apply conf -state normal } else { .bf.apply conf -state disabled } } grid .af.msg -sticky w grid .af.data -sticky ew -padx 3 grid .af.activate -sticky w grid columnconfigure . 0 -weight 1 frame .bf button .bf.ok -text Ok -default active -command { inform "ok pressed" } button .bf.cancel -text Cancel -command { inform "cancel pressed" } button .bf.apply -text Apply -underline 0 -state disabled \ -command { inform "apply pressed" } grid .bf.ok .bf.cancel .bf.apply -padx 3 -pady 3 -ipadx 3 -ipady 2 -sticky ew grid columnconfigure .bf {0 1 2} -weight 1 -uniform a grid .af -sticky news -padx 3 -ipadx 3 -pady 3 -ipady 3 grid .bf -sticky e grid columnconfigure . 0 -weight 1 grid rowconfigure . 0 -weight 1 # Accelerator bindings: How do we make these local to the dialog? bind all { .bf.cancel invoke } bind all { .bf.ok invoke } bind all { .bf.apply invoke } bind all { .af.activate invoke } # Yuck! Move the "active" square to the current widget if it is # a button, leave it blank if it is a check button, otherwise # move it to the default button. bind all { if {[winfo class %W] eq "Button"} { .bf.ok configure -default normal %W configure -default active } elseif {[string match -nocase "*button" [winfo class %W]] } { .bf.ok configure -default normal } else { .bf.ok configure -default active } } bind all { .bf.ok configure -default active if {[winfo class %W] eq "Button"} { %W configure -default normal } } # Default button bindings: Return activates the button bind Button { %W invoke ; break } bind Checkbutton { %W invoke ; break } # I bet Radiobutton needs this too! For a dialog with only two inputs, this is altogether too much work. Surely this code can be streamlined.