Version 6 of ActionPackageDemo

Updated 2008-09-19 18:43:30 by hae

-- Bryan Schofield 25 May 2004 --

This page contains source to a demo program that uses an action package.

See Actions for an introduction to the action concept.

See ActionPackage for source code to the implementation of an action package that this demo program uses.

 # action-demo.tcl --

 source action.tcl

 package require Tk 8.4
 package require action


 set smile {R0lGODlhDwAPAMYAAAAAAH1/ZsLGnOPns9/jrX1/XwgICJ6hfdDUp+vvwPf7xvv/xvv/wvn9
 verur8fLk5mcd/v/ydvfsvv/y+frr/v/uvv/t4eKYdTYo/v/w/D0vSYnHu/zwuntsy4vIvD0
 sPr/tOLnoYiLZvv/vfv/wOntteHluN3hqBESDejtp/r/sfr/r3R3UM3Rl/v/vPP3uQUGBO/z
 wOzwsCgpHPL3qvr/rfr/q9nelPT5sPr/tvv/ueHlqNrfl/r/qvr/qPr/p+XqmOjtpPr/o8HF
 fbC0evr/rDIzIvr/oPn/jvn/kfn/mD5AJvn/nfr/nn6BUOjtncjMgzs9JsfMecfMbjEzHbq/
 b+zyko+SXvn+or7Ddjg6IS8xGwgJBL7Da/n/lKarZcHFe+jukfX7levxitDWd+3zgfn/it3j
 fpKWVYGFTtvhgMfMbXp9Qv//////////////////////////////////////////////////
 /////////////////////////yH+FUNyZWF0ZWQgd2l0aCBUaGUgR0lNUAAh+QQBCgB/ACwA
 AAAADwAPAAAHsoB/goIAhYWDiH+FAQIDBAWHiAYHCAkKCwwNDg8AgwAQCxESE5gUFRYXnYoY
 GRobHAsdHh8gIZ0AIiMkJQAmCycoKSorLIUtFS4vMDELMjM0NTY3hTg5Fjo7GQsgPD0+P0CF
 QSrk5D1C6OhDhUQ1RUY/R0hJSktMTU63Tz9QUVJIU6hUUWJFFYArWI5k0bKFS5ckXr6oUgQm
 jJgxZMqYOYNmIiEAadQgWcMmUiJFhjz+CQQAOw==}
 set bigsmile {R0lGODlhDwAPAMYAAAAAAH1/ZsLGnOPns9/jrX1/XwgICJ6hfdDUp+vvwPf7xvv/xvv/wvn9
 verur8fLk5mcd/v/ydvfsvv/y+frr/v/uvv/t4eKYdTYo/v/w/D0vSYnHu/zwuntsy4vIvD0
 sPr/tOLnoYiLZvv/vfv/wOntteHluN3hqBESDejtp/r/sfr/r3R3UM3Rl/v/vPP3uQUGBO/z
 wOzwsCgpHPL3qvr/rfr/q9nelPT5sPr/tjExMfv/ueHlqNrfl/r/qvr/p+XqmOjtpMDEifr/
 o8DDfsHFfbC0er+/v2JiYhwcHDo6Ovr/nn6BUOjtnTIzIczMzP///+Xl5ZKSkuzyko+SXvn+
 omRmS+rq6tnZ2V5fSfn/lKarZcHFe+jukd3jfpKWVYGFTtvhgPn/jsfMbXp9QgAAAAAAAAAA
 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
 AAAAAAAAAAAAAAAAAAAAAAAAACH+FUNyZWF0ZWQgd2l0aCBUaGUgR0lNUAAh+QQBCgB/ACwA
 AAAADwAPAAAHsIB/goIAhYWDiH+FAQIDBAWHiAYHCAkKCwwNDg8AgwAQCxESE5gUFRYXnYoY
 GRobHAsdHh8gIZ0AIiMkJQAmCycoKSorLIUtFS4vMDELMjM0NTY3hTg5Ojs8GQsgPT46P0CF
 QSo6OkIqPkNE5UNFhUY1OkdIOklKSEc6S0y3TU5PUKIEhCJFxxRVAKhUsfLkCpQrWLJo2aJK
 EZcuhgp5+VKREAAwYcSMIRMpkaKMiQIBADs=}
 set frown {R0lGODlhDwAPAMYAAAAAAH1/ZsLGnOPns9/jrX1/XwgICJ6hfdDUp+vvwPf7xvv/xvv/wvn9
 verur8fLk5mcd/v/ydvfsvv/y+frr/v/uvv/t4eKYdTYo/v/w/D0vSYnHu/zwuntsy4vIvD0
 sPr/tOLnoYiLZvv/vfv/wOntteHluN3hqBESDejtp/r/sfr/r3R3UM3Rl/v/vPP3uQUGBO/z
 wOzwsCgpHPL3qvr/rfr/q9nelPT5sPr/tvv/ueHlqNrfl/r/qvr/qPr/p+XqmOjtpO3yp9HW
 ju7zm/r/o8HFfbC0evr/rL/Dfzg6JC8xGwgJBb7DdPn/m/n/nfr/nn6BUOjtncjMgzs9JsfM
 efn/jsfMbjEzHbq/b/n/mOzyko+SXvn+ovr/oPn/lfn/h/n/jfn/kfn/lKarZcHFe+jukfX7
 lfj+kvn/iPn/it3jfpKWVYGFTtvhgMfMbXp9Qv//////////////////////////////////
 /////////////////////////yH+FUNyZWF0ZWQgd2l0aCBUaGUgR0lNUAAh+QQBCgB/ACwA
 AAAADwAPAAAHtIB/goIAhYWDiH+FAQIDBAWHiAYHCAkKCwwNDg8AgwAQCxESE5gUFRYXnYoY
 GRobHAsdHh8gIZ0AIiMkJQAmCycoKSorLIUtFS4vMDELMjM0NTY3hTg5Fjo7GQsgPD0+P0CF
 QSrkKkJDREXqRUaFRzVIPUlKS0xNTk9QUbdSP1NUVaxcwZJFyxZVALh08eLkixUwYcSMIaNK
 URkzZ9BYSaNmDZuKhAC0cWPlDZxIiRQZAvknEAA7}
 set sick {R0lGODlhDwAPAMYAAAAAAF5wSpGucqbKgaLFe1luQwgICHuRY6G9gLPUjrndkLjgj7XeirDa
 hKPMeYmtZXqOZMbnocPlnsDkmb3ilLTdibHbhK7agKvZfFt0QqfCh8fnoxsfFrXWkiYtHiMs
 G6TOeRAVDKnXeZfBa2l8VMPlncTmn6O/hT5IMq3OiqXNexYcEJS9aabWdaTVc0xiNZy6fL/j
 mMDkmi84JrTTkiYsHio2H5jEayAqFqLUcKHTb4y3X7fbkLzhlK3af57SapDAYavQhbngj7jf
 jrffjKbOe4exXY3CWJbNX5jPY5rPZJvQZnegToGdYrbei7LchoWmYyYxGx8oFQUHA3GcR5TM
 XZfOYUxoMqbNfbHchYyuaCkzHYWrX6LTb3ynU6wTDaVrRZPMW43CWWV9S67ZgazZfqnYep3R
 ariEU8c6IGKIPYWoYp/Jc6XTdqTUc57Sa4e4WFh4OFdvPpC7ZX+oVUxmMsjopMjopMjopMjo
 pMjopMjopMjopMjopMjopMjopCH+FUNyZWF0ZWQgd2l0aCBUaGUgR0lNUAAh+QQBCgB/ACwA
 AAAADwAPAAAHs4B/goIAhYWDiH+FAQIDBAWHiAYHCAkKCwwNDg8AgwAQERITFAsVFhcYGZ2K
 GhscHR4LHyAhIiOdACQlJicoKQsqKywtLi+FMDEyMzQ1CzY3ODk6O4U8PdfXPj/b20CFQUJD
 RBVFRkdISUpLTIVNThVPUFFSU1RVSFZXuFhZWltcXbx8ARNGzCoAY8iUMdOiyxk0aaqoWaVo
 DZs2brq8WQInDkVCAOTM6UKnTqREigx9/BMIADs=}



 # create some images
 image create photo bigsmile -data $bigsmile
 image create photo smile  -data $smile
 image create photo frown -data $frown
 image create photo sick -data $sick


 # make the demo look nice
 if {$tcl_platform(platform) == "unix"} {
    set palette grey80
    tk_setPalette $palette
    option add *background $palette
    option add *foreground black
    option add *Labelframe.relief groove
    option add *Labelframe.borderWidth 2
    option add *Cheeseframe.relief groove ; # our custom widget class
    option add *Cheeseframe.borderWidth 2 ; # our custom widget class
    option add *Button.borderWidth 1
    option add *Radiobutton.borderWidth 1
    option add *Menu.borderWidth 1
    option add *Menu.activeBorderWidth 1
    option add *Entry.borderWidth 1
    option add *Entry.background white
    option add *Entry.foreground black
 }

 option add *Label.anchor w
 option add *Cheeseframe.cmd.text "Invoke"
 option add *Cheeseframe.hlp.text "This is a custom \"Cheeseframe\" class widget\nwith registered action applicators and options"


 # this is a command that we'll associate with actions later.. it's just
 # something to do
 proc mbox {act} {
    tk_messageBox \
        -parent . \
        -title "Action Message Box" \
        -icon info \
        -message "A message box from action \"$act\"\n\nConfiguration:\n[::action::configure $act]" \
        -type ok
 }


 # this allows the ui to apply preferences to an action. by configuring the
 # action here, those options will propogate to various widgets that the action
 # has been applied
 proc applyAction {act} {
    ::action::configure $act \
        -text $::actionText($act) \
        -image $::actionImage($act) \
        -command $::actionCmd($act) \
        -cheesecolor $::actionColor($act) \
        -state $::actionState($act)
    return 1
 }


 # creates frame and some controls to configure the action options, such as
 # images, text, and command
 proc actionConfigFrame {f act} {

    set ::actionCmd($act) [::action::cget $act -command]
    set ::actionImage($act) [::action::cget $act -image]
    set ::actionText($act) [::action::cget $act -text]
    set ::actionColor($act) [::action::cget $act -cheesecolor]
    set ::actionState($act) [::action::cget $act -state]


    labelframe $f \
        -text "Action: $act"
    label $f.l1 -text "Text:"
    entry $f.e \
        -textvariable ::actionText($act) \
        -validate all \
        -vcmd [list applyAction $act]
    label $f.l2 -text "Image:"
    foreach img {bigsmile smile frown sick} {
       radiobutton $f.r$img \
           -image $img \
           -variable ::actionImage($act) \
           -value $img \
           -command [list applyAction $act]
    }
    label $f.l3 -text "Command:"
    radiobutton $f.rbell \
        -text "Bell" \
        -variable ::actionCmd($act) \
        -value "bell" \
        -command [list applyAction $act]
    radiobutton $f.rmbox \
        -text "Message Box" \
        -variable ::actionCmd($act) \
        -value [list mbox $act] \
        -command [list applyAction $act]
    label $f.l4 -text "Cheeseframe:"
    foreach color {palegreen skyblue} {
       radiobutton $f.$color \
           -text "$color" \
           -value $color \
           -variable ::actionColor($act) \
           -command [list applyAction $act]
    }

    label $f.l5 -text "State:"
    foreach state {normal disabled} {
       radiobutton $f.$state \
           -text "$state" \
           -value $state \
           -variable ::actionState($act) \
           -command [list applyAction $act]
    }


    grid $f.l1 $f.e         -         -         -        - -stick ew -padx 4 -pady 4
    grid $f.l2 $f.rbigsmile $f.rsmile $f.rfrown $f.rsick x -stick ew -padx 4 -pady 4
    grid $f.l3 $f.rbell     - $f.rmbox          -        - -stick w  -padx 4 -pady 4
    grid $f.l4 $f.palegreen - $f.skyblue -      -        - -stick w  -padx 4 -pady 4
    grid $f.l5 $f.normal    - $f.disabled -     -        - -stick w  -padx 4 -pady 4
    grid columnconfigure $f 5 -weight 1
    return $f
 }


 # this creates our custom class of widget... a Cheeseframe
 proc createCheeseFrame {f} {
    labelframe $f \
        -class Cheeseframe
    label $f.hlp
    label $f.img
    button $f.cmd 
    grid $f.hlp -      -stick ew -padx 4 -pady 4
    grid $f.img $f.cmd -stick ew -padx 4 -pady 4
    grid columnconfigure $f 1 -weight 1
    return $f
 }


 # this has the smarts to apply an action to our custom Cheeseframe class
 # widgets
 proc cheeseframeApplicator {w act} {
    # let's parse thru the current configuration and only use what is available
    # to us. By doing reacting to options instead of looking for specific
    # options, this allows us to not really care if one of the options is
    # missing. For example, if the -cheesecolor option is removed, this proc
    # will still function just fine, only the -cheesecolor will never be
    # processed. Of course, if we really had to have it, we could generate an
    # error if we didn't find it.   
    foreach optSet [::action::configure $act] {
       switch -- [lindex $optSet 0] {
          -text    {$w configure -text [lindex $optSet 1]}
          -image   {$w.img configure -image [lindex $optSet 1]}
          -command {$w.cmd configure -command [lindex $optSet 1]}
          -state   {$w.cmd configure -state [lindex $optSet 1]}
          -cheesecolor {
             set color [lindex $optSet 1]
             #$w configure -background $color
             foreach child [winfo children $w] {
                $child configure -background $color
             }
          }
       }
    }
 }

 # this little proc will be used to validate a color is a color when setting
 # the -cheesecolor option. This prevents someone from doing this:
 #    -cheesecolor 1
 # The "configure" will return an error.
 proc cheesecolorValidator {color} {
    if {[catch {winfo rgb . $color} err]} {
       return -code error "invalid cheesecolor $color"
    }
 }


 # add our custom -cheesecolor option for Cheeseframes and set the applicator
 # proc
 ::action::addOption -cheesecolor palegreen cheesecolorValidator
 ::action::setApplicator Cheeseframe cheeseframeApplicator



 # create a couple of actions and a little ui to configure what the actions do
 # notice we are not setting -cheesecolor, these actions will pick up the
 # default value, though we could just as easy set the value here.
 action::create uno -text "Uno!" -image "smile" -command "bell" -cheesecolor skyblue
 action::create dos -text "Dos!" -image "bigsmile" -command [list mbox dos]
 actionConfigFrame .cfguno uno
 actionConfigFrame .cfgdos dos


 # create a menu bar so we can show off actions in a Menu class widget
 menu .m -relief flat
 . configure -menu .m
 .m add cascade \
     -label "File" \
     -menu .m.file
 .m add cascade \
     -label "Actions" \
     -menu .m.acts

 menu .m.file
 menu .m.acts


 # create some buttons so we can show off actions in Button class widgets
 labelframe .buttons \
     -text "Buttons with Actions"
 grid [button .buttons.uno] [button .buttons.dos] -stick ew -padx 4 -pady 4
 grid columnconfigure .buttons {0 1} -weight 1


 # create our custom Cheeseframe widgets so we can show off the flexible nature
 # of the action framework for allowing everything to be configurable.
 labelframe .cheese \
     -text "Custom widgets that accept actions"
 grid [createCheeseFrame .cheese.uno] [createCheeseFrame .cheese.dos] \
     -stick ew -padx 4 -pady 4
 grid columnconfigure .cheese {0 1} -weight 1


 # apply the actions to the widgets
 ::action::apply uno .buttons.uno .m.acts .cheese.uno
 ::action::apply dos .buttons.dos .m.acts .cheese.dos


 # lastly, create an "exit" action and a button
 ::action::create exit \
     -text "Exit" \
     -command "exit"
 button .exit
 ::action::apply exit .exit .m.file


 # oh, and stick in a label that explains what we are doing
 label .help -text {
 This demo shows the practical use action framework. We have create two actions
 and have allowed these actions to modified through the user interface.  Each
 action has been applied to a menu, a button, and a custom widget.  Options
 and action applicators for the menu and button are standard in the action
 package. For the custom widget, we have added a color option to action
 framework, provided a custom color validator and a widget applicator for our
 custom widget class.

 You will notice by configuring the action, the options will automatically be
 propogated and be appropriately applied to each type of widget.
 } -justify left

 grid .help    -       -stick {} -padx 4 -pady 4
 grid .cfguno  .cfgdos -stick ew -padx 4 -pady 4
 grid .buttons -       -stick ew -padx 4 -pady 4
 grid .cheese  -       -stick ew -padx 4 -pady 4
 grid .exit    -       -stick ew -padx 4 -pady 4
 grid columnconfigure . {0 1} -weight 1