The CommonInteract-II User's Guide
==================================

Introduction

CommonInteract-II (CI2) is a C++ framework for building applications
containing direct manipulative graphics. It is a simplified implementation
of the CommonInteract specification limited to 2-D graphics and without
really dividing the framework into a control part and a presentation part.
(If you are interrested in the whole specification read the paper
'CommonInteract: An Object-Oriented Architecture for Portable, Direct
Manipulative User-Interfaces', Journal of Object-Oriented Programming,
June 1993, Vol.6, No.3.)

One of the most important features of CommonInteract is, that it specifies
'intelligent' objects: CI objects do not only contain information
how to present them but also how to handle a variety of user interaction.
This is possible by having some generalized conceptions to define the possible
states and the expected behavior of each object:

* Interaction primitives: Are describing the possible manipulations of
  an object by the user. Currently defined are:
  Mark - identifies an object as a target for subsequent operations
  Move - change the location of an object
  Assign - change the location and the parent of an object
  Resize - change the object's size
  Select - the meaning of this operation can be specified by the application
  Activate - like select, but triggered by different user interaction

  How these primitives and their feedback are implemented is not important
  for an application, future versions if CI will allow to choose among
  different interaction styles. See the reference guide for a description
  of the current implementation.

* Object attributes: Are divided into appearance attributes and behavior
  attributes. Appearance attributes can be subdevided into:
  basic attributes - they are common to each displayable 2d-object (like
    location, shape, size, color, fill style, etc.)
  individual attributes - subclass specific object data (like text and font
    for objects which are able to show labels)
  Behavior attributes allow to control an object's response to user action
  with minimal effort for the application. They are mapped to the interaction
  primitives. Most of them are boolean and simply allow/deny an operation
  (markable, moveable, selectable, activateable). The resizeable attribute
  is splitted that the direction of an allowed resize operation can be 
  defined (resizeLeft, resizeLeftTop, resizeCenterTop,...). Assignment is
  controlled by two lists of names in every object (Each CI object class and
  each object instance must have a unique name.) One list contains possible
  targets to which the object may be assigned, the other list contains
  possible children which may be assigned to the object. Both lists may
  contain class names and instance names, wildcards are supported. This way it
  is easy to control assignment operations and the user can get feedback
  showing which assignments are allowed while moving objects around on the
  screen.

* Callbacks: A CI object manages callback lists for every interaction
  primitive. An additional 'change contents' callback list was added for the
  assign primitive, it is responsible for notifying parent objects of
  loosing or getting new children. Callback functions can be members of any
  subclass of CiObject or XmObject and they are invoked after the user
  performed the respective interaction. (As they work exactly like their Xm
  conterparts see the Xm++ user guide for general information on setting
  callbacks.)

* Messages: This facility is provided for easy inter-object communication.
  It extends the polymorphy of C++ objects by implementing Smalltalk-like
  message passing. Thus it is possible to invoke member functions of unknown
  CI-objects by name (and parameter types if any). The advantage over the
  virtual function mechanism is that there is no need for a common superclass
  of all objects implementing a specific method. This allows the design of
  independend frameworks of CI-objects which can be used together without
  any change. An example: Consider that someone has implemented a copier
  object which wants an assigned object to release a copy of it. When using
  virtual functions this would mean that there must exist a common base
  class (i.e. CopyableObject) with a virtual copy() function. All objects
  which ever should be useable with the copier have to inherit from this
  base class and to override the copy function. The real problem comes up
  when reusing many CI-objects from independend source - you may have to
  inherit a table of hundreds of virtual functions although you need only
  a few of them. The CI message facility allows to avoid this by allowing
  the selective definition of messages to which an object should react. How
  this is realized will be explained later in this document.
  

Getting Started

The CommonInteract World

CI is built upon a Xm++ class (XmDrawing) which implements all the low level
graphics and event handling needed for a direct manipulative application.
Therefore the root level object ('World') of a CI application is a subclass
of XmDrawing class which itself inherits from XmControl. This allows the
CI-world to be attached to any Xm++ parent object. (Like XmWindow,
XmDialogWindow, XmUserDialog, XmGroupBox, etc.)

In our example we use it as a subpane of a XmWindow:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

#include "xmCi2.h"

class CiTest : public XmWindow
{
    void initialize();
public:
    CiTest() : XmWindow("CommonIteract-II Test Window")  { }
};

void CiTest::initialize()
{
	XmCiWorld* world = new XmCiWorld("World");
	world->addChild(new CiObject(new Rectangle(10, 10, 50, 100), "Obj1"));
	world->addChild(new CiObject(new Rectangle(80, 10, 50, 100), "Obj2"));
    addSubpane(world);
}

void XmApp::initialize()
{
    (new CiTest)->open();
};

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

This little program creates an application window containing a CI-World
with two rectangular CI-objects of the specified size and with default
object attributes - they can be moved around, resized in any direction
but not assigned to each other.

Creating CI-objects

Following the CI specification CI objects are representing only the
control part of an application and are designed primary to implement
interaction semantics. CI objects hold only the few generalized basic
attributes mentioned above which are only sufficient to keep track of
manipulation issues, but not for defining what should be presented to the
user. By definition the presentation of an object has to be implemented in a
separate part of the application - the presentation part. In the control
part the object's presentation is supplied only as a parameter on the
same level as it's name, size or other attributes.

In CI-2 this is implemented the way you could see in the first example
above: The constructor of CiObject takes an object of a drawing primitive
class to define the object's shape. This simple classes are supplied
from Xm++ along with the XmDrawing class and they cover the basic graphic
shapes: Dot, Line, Rectangle, Circle, Ellipse, PolyLine (also useable for
drawing polygons).
The CI specification says that the presentation part should consist of media-
dependend frameworks extendable by application programmers. This is not
fulfilled by the CI-2 implementation (all drawing and event sampling is done
inside the XmDrawing class). But additional primitives for displaying bitmaps, 
more complex graphics in different formats (i.e. EPS) and Xm++ controls
will be added in the future and should meet most application's needs.

To have a sample of currently available primitives modify the initialize()
method of the example to look like this:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

void CiTest::initialize()
{
	XmCiWorld* world = new XmCiWorld("World");
	world->addChild(new CiObject(new Line(10, 10, 50, 100), "Line"));
	world->addChild(new CiObject(new Rectangle(80, 10, 50, 100), "Rect"));
	world->addChild(new CiObject(new Circle(10, 120, 40), "Circle"));
	world->addChild(new CiObject(new Ellipse(80, 120, 60, 40), "Ellipse"));
	Polygon* aPolygon = new Polygon;
	aPolygon->setPoints(3, 80, 200, 120, 260, 40, 260);
	world->addChild(new CiObject(aPolygon, "Polygon"));
    addSubpane(world);
}

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Object Attributes

Let's go back to the first version of the example. It showed the usage
of some basic object attributes like shape, location, size (all specified
by the drawing primitive), and name. They all were passed as constructor
arguments. Except the shape (which cannot be redefined) all attributes
may be changed later using the appropriate member functions of CiObject.
First we try to change some appearance attributes - replace the initialize()
method again:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

void CiTest::initialize()
{
	XmCiWorld* world = new XmCiWorld("World");
	CiObject* anObj;

	anObj = new CiObject(new Rectangle(10, 10, 50, 100), "Obj1");
	anObj
		->lineWidth(5)
		->lineStyle(50)
		->lineColor(GPR_YELLOW)
		->fillColor(GPR_GREEN)
		->linePattern(GPR_DGRAY)
		->fillPattern(GPR_LGRAY)
		->setText("Object")
		->setFont("variable")
		->textHorzAlign(GPR_BEGINNING)
		->textVertAlign(GPR_CENTER);

	world->addChild(anObj);
	world->addChild(new CiObject(new Rectangle(80, 10, 50, 100), "Obj2"));
    addSubpane(world);
}

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Now the left object will look very different from the right. For a list
of currently valid values consult the reference guide. Note that all
attribute-setting functions return 'this' and can be cascaded like
shown above.

Now we go to test behavior attributes: 

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

void CiTest::initialize()
{
	XmCiWorld* world = new XmCiWorld("World");
	CiObject* anObj;
	ciAttrib attr = MARKABLE | RESIZE_BOTTOM_CENTER;

	world->addChild(new CiObject(new Rectangle(10, 10, 50, 100), "Obj1", attr));
	anObj = new CiObject(new Rectangle(80, 10, 50, 100), "Obj1", 0);
	attr |= MOVEABLE;
	anObj->setAttributes(attr, TRUE);
	world->addChild(anObj);
    addSubpane(world);
}

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

This causes both objects to be resizeable in height, but only the right
object is allowed to be moved. Behavior attributes are (except the assignment
rules which are described later) integer constants which can be ored together.
They may be passed to the constructor, if omitted the CI_DEFAULT_ATTRIBUTES
constant (which can be overridden by a #define inside your application) is
used as a default argument. The setAttributes() function allows selective
set/reset of a behavior attribute mask.

Adding Action

..........

(to be continued)