|
U S I N G T H E C S 3 2 1 G R A P
H I C S S H E L LTo simplify development of
graphical programs and to provide a more robust and uniform interface for
products a specialized graphical shell has been developed for CS321. It
uses Motif and the X Window System to provide a simple graphical user
interface (GUI) with a drawing area, buttons, menu, and command line. Your
project WILL BE REQUIRED to work within this shell.
All of the code and details necessary to run this shell can be found
in the directory /usr/local/include/cs321. As with most C++ projects it
is composed of a series of classes which are designed to interact with
each other. Most of the classes are defined using a set of files as
follows:
- *.cpp - the C++ source code with the definition of the class
- *.h - the C++ header file with the declaration of the class
- *.o - the compiled version of the cpp file
- *.uil - the Motif interface description for the graphical objects
in this class
- *.uid - the compiled version of the interface description
The following C++ classes have been developed and have the
following relevant interface:
- MRMbase: <mrmbase.h> - a base class used by other class
objects to access UID files (MRM = Motif Resource Manager)
- Shell: <shell.h> - a base class which builds a functionless
graphical user interface
- It's constructor requires the unix command line parameters (argc
and argv) and the path to the applications context file. Calling the
constructor (which all derived classes must do) will draw the shell
and enable most of the built-in features.
- Once created the shell is dormant until Activate() is called.
This member function does not return since it turns control over to
the X Toolkit event handler (see below).
If you want multiple Shells construct them all before
calling Activate() for any one of them.
- Internally the Shell class provides a number of data members to
facilitate X Windows System programming. The only ones of real
interest are display, drawable, window, and screen which should be
familiar to most X programmers.
- Additional functionality of the shell is directly accessed using
the following member functions:
- int Left_Pixel() - Determine the left-most pixel displayed
in the drawing area.
- int Top_Pixel() - Determine the top-most pixel displayed in
the drawing area.
- void Log_Cmd(const string& cmd) - Add a text command to the
recall list accessed through the recall up arrow.
- Functionality is added to the Shell by deriving a customized class
from it which overrides the following virtual methods:
- void Erase() - Erase the picture
- void Zoom() - Zoom in
- void Unzoom() - Zoom out
- void Reset() - Revert picture to original state
- void Cmd_Parse(const string& cmd) - Parse text from the
Shell's command line
- void Draw(int tag, XEvent *ev) - Process
event
from drawing window. The tag has one of four values:
- G_EXPOSE - the drawing area has been exposed
- G_INPUT - the mouse has generated an event
- G_RESIZE - the drawing area has been resized
- G_MOTION - the mouse has moved while a button is pressed
- const string Help_Text() - Returns text on how to use the
derived Shell's features.
- void Read_File(const string& fn) - Read data from file fn
- void Write_File(const string& fn) - Write data to file fn
The derived class constructor can be used to add extra GUI
elements.
Other members of the Shell class should not be overridden.
Message: <message.h> - a class for displaying a text
message in a pop-up window. Only one object of this class is allowed so
messages will share the same window. This class has three methods:
- void Show_message(Widget w, const string& title, const
string& msg) - This might typically be invoked as:
Message::Show_message(parent, "Hello Window", "This message says
hello.\n");
- void Append_message(const string& msg) - Add a message to the
existing message window. This might by invoked as:
Message::Append_message("This appears at the end\n");
- void Cleanup() - this method is called: Message::Cleanup() to
destroy the single object of this class. It may be called safely even
if Show_message was never used. The Shell class automatically does
this for you in its destructor.
Color: <color.h> - this class manages a RGB
color editor that can be used to dynamically change the color of drawn
objects. It requires that a read/write color cell be allocated use
XAllocColorCells before each color editor is created. Plane masks are
not necessary for these colors. See xcolor.html
for help with colors.
- Color editors are often created dynamically and should not be
constructed until the first call to Draw. The typical usage would be:
Color* myeditor = new Color(parent, pixel, "This is my color");
- Like all dynamic objects your derived Shell destructor must delete
them. This includes both the color cell (using XFreeColors) and the
color editor.
- There are some minor bugs in eXcursion that may not have color
changes appear until the next Exposure.
- Multiple editors can be created; each should use a different name
to avoid confusion.
Button: <button.h> - this is a base class for adding
customized buttons to the Shell.
- Buttons are best added in the derived Shell class constructor.
- Functionless Buttons are constructed using Button(user_button,
"MyButton")
- Each Button should have its own text to avoid confusion.
- As with all objects they should be destroyed.
- Derived Button classes should override the void Pressed(Widget w,
int* tag, XmPushButtonCallbackStruct *cb) method to add functionality
when the button is pressed.
Slider: <slider.h> - this is a base class for adding
customized slider controls to the Shell.
- Sliders are best added in the derived Shell class constructor.
- Functionless Sliders are constructed using Slider(parent, name,
min, max, init, size, orientation); where:
- name - the string name to place on the slider window
- min - the minimum integer value on the slider
- max - the maximum integer value on the slider
- init - the initial integer value of the slider
- size - the size in pixels of the slider window
- orientation - either Slider::HORIZ or Slider::VERT
- Each Slider should have its own name to avoid confusion.
- As will all objects they should be destroyed.
- Derived Slider classes should override the void New_value(Widget
w) method to add functionality when the Slider values changes. The
value of a Slider can be accessed using void XmScaleGetValue(Widget w,
int* valptr);.
File: <file.h> - this class manages the file choice dialog. It
should not be used. While this all sounds rather complicated a
functionless shell (i.e. one without derived classes) can be created and
run by copying the following files from: /usr/local/include/cs321/
- cs321.cpp - a sample main() which creates and activates a Shell
- Makefile - the building instructions for the shell
Copies
these files to your working directory and execute the command make. This
will build the executable cs321 which can be run. As each of the elements
of the Shell are activated those requiring functionality will simply write
a text message to cout. As you add functionality using derived classes you
will want to make changes to the Makefile and to cs321.cpp to create the
proper type of Shell.
The CS321 Shell is built around an event driven model
which means that as the user activates Shell controls or exercises the
mouse in the drawing window area the X Toolkit automatically dispatches
calls to the various methods within the classes described above. One of
the features provided by the basic shell code is to automatically map the
Shell event to the proper object and member function. Unfortunately the
work does not end here. Since each of the events is essentially
asynchronous (from the program's perspective) each of the methods (e.g.
Draw) must communicate data and state information with the other methods.
This is relatively easy to do by adding data members to derived classes to
share this information, however, care should be taken to avoid a
proliferation of "global" data elements by using static local variables
that allow a method to share state information with future calls to
itself.
The drawing area has the most complex assortment of events which are
classed as follows:
- Exposure - whenever a portion of the drawing area is uncovered the
operating system will ask it to redraw its data. This is best done by
simply redrawing all the data after first making sure that the exposure
count is zero (for efficiency). The first time Draw is called with an
exposure event will signify that the drawing window has been created and
it is now safe to draw images. To do this a graphical context will need
to be generated using:
XGCValues val; val.foreground =
WhitePixelOfScreen(screen); val.background =
BlackPixelOfScreen(screen); gc = XCreateGC
(disp,draw,GCForeground|GCBackground,&val); Where gc is a
private data object of the derived Shell class of type GC. As with color
cells all graphical contexts created by your program should be freed in
the destructor (using XFreeGC).
Known Bugs:The following bugs have been observed while using the
shell and do not necessarily indicate an error in shell coding, but rather
an X Window System implementation problem.
- Bugs while using eXcursion
- Despite using Read/Write color cells in the X colormap it is not
unusual for objects immediately drawn in the new color specified
by the built-in color editor to be drawn in the previous color. This
is often fixed by forcing an Exposure event in the drawing window.
This bug is erratic and does not always occur.
- Bugs while using eXcursion with Windows95 only
- Attempting to draw objects with coordinates that overflow the int
data type may cause the Windows95 machine to crash requiring a reset.
This page is maintained by Henry
Welch; it was last updated on September 22, 1999. Copyright ©1999 by Henry L. Welch |