The following sections will examine the Pipe and Filter style as a running example. I won't show all the new code, but will go over most of the important pieces so its clear what has to be done and why. Here are the main steps that you take:
In the PF style, the following new sub-classes were created:
class CFamPFFilter CFamComponent
class CFamPFPipe CFamConnector
class CFamPFReadPort CFamPort
class CFamPFWritePort CFamPort
class CFamPFSource CFamRole
class CFamPFSink CFamRole
class CFamPFAggregate CFamAggregate
method CFamPFFilter addPort
method CFamPFPipe addRole
method CFamPFAggregate attach
method CFamPFAggregate bind
In the PF style, the bulk of the new code handles visualizations of the new port and role types and their corresponding binding classes. The the visual classes for pipes and filters do nothing but modify the double-click mouse binding to invoke the modified workshops. Workshops are discussed in Section 6.
The new code for source and sink roles is fairly simple. The render routine is modified so that sources are filled black and sinks are filled red. In addition, the stickTo method, which is called when a role is moved over something it can attach to, like a port or a binding, is modified to change the outline that the role is drawn in.
The new code for ports is somewhat more complicated. Because ports are now arrows, they must know what side of a component's box they live on. Therefore, we add a lot of code to change the way the arrow is drawn for each of four possible sides. In addition, the fill colors are modified to match those of sink and source roles.
Along with the new port/role classes is a pair of new binding classes to make the visualizations consistent. The new bindings just make sure to draw themselves in the right color.
Finally, aggregates are modified so that they create the right binding classes. This is important because the visualizations of bindings in a PF aggregate must also have the appropriate color.
Most of this information is passed to the user interface via the defClass command. The syntax for the defClass command is:
defClass <symbolic name> <attribute name> <attribute value>This command defines various attributes for symbolic "classes" that are presented to users in place of the actual classes defined in Fcl code. This is to make life easier for the user. Here is an example definition from the pf.map file. The following code defines a symbolic class called "Input." The bold keywords are the name the attributes of this symbolic class.
defClass Input dbclass CFamPFReadPort
defClass Input visclass CVisPFInput
defClass Input bitmap port.bit
defClass Input callback __makePort__
defClass Input activeCheck uic_compSelectedp
You will notice that only classes representing Components, Connectors, Ports and Roles show up in the editing palette. The other classes, representing various sorts of aggregate and external representations are still needed in other situations, but since they don't show up in the main palette, some fields like the bitmap field are not relevant.
The last line of code in the pf.map file specifies what should be available in the palette of each editing window. It does this through the global variable vis_paletteArgs.
set vis_paletteArgs { {Components {StdFilter StderrFilter}} {Connectors {Pipe}} {Ports {Input Output}} {Roles {Source Sink}} }This says the palette should have four sections labelled Components, Connectors, Ports and Roles and it specifies a list of symbolic class names to appear in each section. When an new editor window is created, this information is used to set up the palette buttons with the right bitmaps and callbacks.
The rest of the code in this file just defines the palette callbacks.
The form for the generic component workshop starts like this:
set __cws__CompWSForm { {-type group -name top -pack_opts {-anchor w -side top -expand 1}} {-type group -name bot -pack_opts {-anchor n -side top -pady 2m}} {-type group -name bot.left -pack_opts {-side left }} {-type group -name bot.left.list -pack_opts {-side top \ -anchor w -padx 2m}} {-type group -name bot.left.buttons \ -pack_opts {-anchor w -padx 2m -side top -pady 2m}} {-type group -name bot.right -pack_opts {-side left }} {-type group -name bot.right.list \ -pack_opts {-side top -anchor w -padx 2m}} {-type group -name bot.right.buttons \ -pack_opts {-anchor w -padx 2m -side top -pady 2m}}This code sets up all of the frames that the form will be using to hold entry boxes and buttons. We set up a top and bottom frame, each of which contains a left and right sub-frame. The -name switches are used to define names that we can use later in pack options. The type of each of these is a "group", which just means a frame. Perhaps frame would have been a better name.
{-type vis_ed -win_name top.thumbnail -name ThumbNail -mk_proc {wsc_mkThumbNail %f.topFrame.top.thumbnail %o %v} -widget_opts { -height 200 -width 380 -relief sunken -bd 2} -pack_opts {-expand 1 -fill x -side top -padx 1m -pady 1m}}This sets up the first form element in the dialog. Its just a canvas to draw the thumbnail in. The -mk_proc option specifies a script to run to draw the initial image in the canvas. The other options specify a window path, options for the widget and options to give the packer when packing the widget.
{-type text_entry -win_name name_entry -name Name -get_proc {trans {%o getName}} -set_proc {wsc_setName %c %v %o "%t"} -pack_opts {-expand 1 -fill x -in %f.topFrame.top -side top} -entry_pack_opts {-expand 1 -fill x}}A text box entry. These and buttons are the most common elements. The key thing here is the -get_proc and -set_proc options, which tell the form how to fetch an initial value for the entry and how to update the database object when the user edits the entry. In addition, note that the -pack_opts option applies to the frame containing the label and the entry box, whereas the -entry_pack_opts options applies to the entry itself within this frame. This is an awful hack, but what can you do.
{-type list_box -win_name portsListBox -name ports -get_proc {trans {__cws__getPortNames %o}} -select_proc {trans {__cws__editPort %o %i %X %Y %v}} -widget_opts {-geometry 20x5 -relief sunken -bd 2} -pack_opts {-in %f.topFrame.bot.left.list -anchor n -side top}}A list box provides access to object slots that are list values, and lets the user pick objects from these lists to look at more closely. All the options here are generic except the -select_proc options, which specifies a script to run when an item receives a double-click event. This list box provides the user with a way to look at and manipulate the ports of a component. The two library routines __cws__getPortNames and __cws__editPort are provided by the UI library and can be used in other style-specific workshops as well.
{-type menu_button -name "New Port" -value_expr "Port" -command_proc {__cws__newPort %c %v %o %tA menu button takes a list of menu entries and dispatches what the user picks to the script specified in the -command_proc option. We pass along the text of the item that was picked so the dispatch procedure can tell what to do. This menu allows the user to create new ports on a component. The list of menu items is specified in the -value_expr option. In this case, we can only make generic ports. In a style, you would add any other symbolic classes that you may have defined back in the ".map" file.
%f.topFrame.portsListBox.listBox} -widget_opts {-state normal} -pack_opts { -in %f.topFrame.bot.left.buttons -side left}}
{-type button -name "Delete Port" -command_proc {__cws__deletePort %c %v %oThis defines a command button. Control is sent to the script specified in the -command_proc option, with all needed substitutions.
%f.topFrame.portsListBox.listBox} -widget_opts {-state normal} -pack_opts { -in %f.topFrame.bot.left.buttons -side left}}
{-type list_box -win_name repsListBox -name reps -get_proc {trans {wsc_getRepNames %o}} -select_proc {wsc_editRAttrs %o %f %f.topFrame.repsListBox.listBox} -widget_opts {-geometry 20x5 -relief sunken -bd 2} -pack_opts {-in %f.topFrame.bot.right.list -side top }}Another list box for manipulating representations.
{-type menu_button -name "New Rep" -value_expr "Aggregate External" -command_proc {wsc_newRep %v %o %t %f.topFrame.repsListBox.listBox} -pack_opts { -in %f.topFrame.bot.right.buttons -side left}}A menu button just like the "New Port" button.
{-type button -name "Delete Rep" -command_proc {trans {wsc_deleteRep %v %oThis works the same way as the "Delete Port" button.
%f.topFrame.repsListBox.listBox}} -pack_opts {-in %f.topFrame.bot.right.buttons -side left}}
{-type button -name "Edit" -command_proc {trans {wsc_OpenExternalEditor %v %oThis buttons lets you fire up an external editor on an external rep. or a recursive edit session on an aggregate.
%f.topFrame.repsListBox.listBox}} -pack_opts {-in %f.topFrame.bot.right.buttons -side left}} }
The code for the filter workshop in the PF style is strikingly similar to the specification above. In fact, the main difference is the following change to the two menu buttons.
{-type menu_button -name "New Port" -value_expr "Input Output" -command_proc {__cws__newPort %c %v %o %t \ %f.topFrame.portsListBox.listBox} -widget_opts {-state normal} -pack_opts { -in %f.topFrame.bot.left.buttons -side left}} {-type menu_button -name "New Rep" -value_expr "Aggregate External WrightSpec" -command_proc {wsc_newRep %v %o %t %f.topFrame.repsListBox.listBox} -pack_opts { -in %f.topFrame.bot.right.buttons -side left}}Notice that the items in the menu have changed to reflect what is reasonable to do in the PF style. Its unfortunate that you have to do this by hand, but, well, this is a prototype. The other main change is to the behavior of the "Edit" button:
{-type button -name "Edit" -command_proc {trans {__fws__editRep %v %o %f.topFrame.repsListBox.listBox}} -pack_opts {-in %f.topFrame.bot.right.buttons -side left}}Here we make sure to call the style specific routine __fws__editRep which does a few things differently for the external representations available in PF. In particular, for representations managed by the Synthesizer Generator, this routine is creates the appropriate empty Synthesizer Generator file. Actually, it does this correctly for filter language representations, but not for FDR representations, even though you can use the Synthesizer Generator tool to edit FDR specs. Future version of the style should fix this by creating a special class of representation for FDR specifications, rather than just using a semantically meaningless external representation.
Most of the other workshops make simple changes to the main form. It seems wasteful to copy all of this forms code around. On the other hand, it gives us the flexibility to make forms that look nothing like the ones we currently use, if that is appropriate.
Different styles will initialize global data differently, and may fire up more or fewer background tools. The PF style only does the former. It overrides the two global variables uiStyleMakeToplevel and uiStyleTopLevelTag so that PF designs will be created and recognized correctly in the database. It also redefines the procedure
uic__setDefaultExtTool to allow externals to be edited by the synthesizer generator by default. The rest of the file is the same as the generic style, except for the code added to load up the PF style itself.
Finally, main.tcl should also set as global variable designating the style. This is the prefix that will be used for design names in the database and in other places as an internal identifier. The variable is __globalStyle__. For the PF style the command is:
set __globalStyle__ "pf"In order for the Aesop Design Manager to be able to recognize and work with designs in your new style, you'll also need to edit the
styles.dir
file in the Aesop lib directory FCL_LIBRARY
.
The file has the following form in the default version of Aesop:
ui "Generic Style" pf "Pipe and Filter Style" unix-pf "Unix Pipe and Filter Style" msg "Real Time Style"That is, each line designates a style where the first item on each line is the name of the unix directory that contains the style code and the the second is a quoted human readable name of the style. In order to use a newly defined style this file must contain an entry for it.
This is for the Pf style
proc __uic__makeToolsMenu {mb w c} { set canvas "$c.c" menubutton $mb.tools -text "PF-Tools" menu $mb.tools.menu $mb.tools.menu add command -label "Build" \ -command "__pf__buildPFCommand $w" pack append $mb $mb.tools {left} $mb.tools configure -menu $mb.tools.menu $mb.checks.menu add command -label "Check invalid connectors" \ -command "checkForFiles $w $canvas" $mb.checks.menu add command -label "cycle-checker" \ -command "checkForCycles $w $canvas" }