ActionPackageDemo

-- 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