Draft – Apr 8, 2004

Chapter 4 Analysis and Design of a Graphical Editor

This tutorial introduces the analysis and design of a simple windows-based graphical program, named Graphics Editor. It uses the Use Case diagramming convention from the Unified Modeling Language (UML) to express the program’s requirements. It then develops the design using UML Class and Sequence diagrams.

Once the design is complete it is shown how to implement it in two ways, the first is by using C++ and the Microsoft Foundation Classes (MFC). The Visual Studio .net Interactive Development Environment (IDE), along with App Wizard facilitate the programming.

The second implementation is using Java. This time, Sun Microsystems Software Development Kit (SDK) including its Swing GUI classes, and the open source Eclipse IDE are used. The Java implementation has the advantage of being cross-platform, i.e. it will work on Unix and MacIntosh machines as well as on Microsoft Windows ones.

©2004 by Michael M Werner, Wentworth Institute of Technology

Introduction to the Problem

By following this tutorial you will build a Windows graphical application program. The Graphics Editor will allow you to draw circles (nodes) on a screen, and connect them with lines (links). You will also be able to move the nodes, and the links will follow. If you delete a node, all links attached to it will also be deleted.

Similar to applications such as MS Word, you will be able to work with multiple drawings (documents) at a time, and also open multiple windows (views) on the same document. Your program will be interactive; it will have menus and respond to mouse clicks and movements.

Here is a screen shot showing the running program:

The drawing shows a graph. Having a program like this is useful in data structures courses whenever trees or other graphs need to be drawn. There are actually two windows open on the same graph, labeled Ged1:1 and Ged1:2. If you make a change in one window, such as moving a node, it is automatically made in the other, since the windows are two views on the same document.

Your program will be able to print the model, as well as save and later reload it.

Analysis

We begin the analysis by identifying and describing the basic functionality that will be provided to the end user, the person building a model made from nodes and links. This is best done using a Unified Modeling Language (UML) Use Case Diagram.

Each use case may be tightly described in terms of pre and post conditions. The precondition identifies the necessary state of the application in order to begin the use case. The postcondition identifies what is guaranteed to be true for the application after the use case has ended. Use cases are also described by scenarios, listing in plain English, the sequence of events that occur in carrying out the use case.

Before describing the use cases in detail, we need to talk about how the modeler will interact with the application. Typically a computer user interacts with a program by pressing keys or by pressing and releasing mouse buttons and moving the mouse.

In the Graphic Editor program, the modeler will construct models primarily by mouse actions, for example, to insert a node the modeler will click on an empty spot, to delete a node she will click on a node, to insert a link she will drag the mouse from one node to another. So the same mouse actions, such as depressing the left button, can be used to achieve different effects. To make sense out of this, we introduce the concept of mode. If the modeler wants to insert a new node, she first switches to the INSERTNODE mode, then pushes the left mouse button where she wants the node to appear. To delete that same node, she switches to DELETENODE mode and pushes the left mouse button while over the node.

These modes are defined:

  • INSERTNODE
  • INSERTLINK
  • DELETENODE
  • DELETELINK
  • MOVENODE

Use Case / Pre and Post Conditions / Flow of Events
Insert Node / Precondition:
Mode = INSERTNODE
Mouse is over an empty area
Postcondition:
New node inserted in the model / Modeler presses and releases left mouse button over a graph point.
Node appears centered at the point. It also appears in all other windows on the same model.
Insert Link / Precondition:
Mode = INSERTLINK
Model contains 2 or more node
Postcondition:
New link inserted in the model / Modeler presses and holds left mouse button over one node.
She drags to a second node.
She releases mouse button.
Link is drawn in all windows on the model.
Delete Node / Precondition:
Mode = DELETENODE
Mouse is over a node
Postcondition:
Node is deleted from the model.
All links to and from the node are deleted. / Modeler clicks left mouse button over a node.
All windows on the model are redrawn minus the node and all links to and from it.
Delete Link / Precondition:
Mode = DELETELINK
Mouse is over a link handle
Postcondition:
Link is deleted from the model. / Modeler clicks left mouse button over a link handle.
All windows on the model are redrawn minus the link.
Move Node / Precondition:
Mode = MOVENODE
Mouse is over a node
Postcondition:
Node in new location.
All links to and from the node are moved. / Modeler presses and holds left mouse button over a node.
She drags the node to a new location.
She releases mouse button.
Print Model / Precondition:
Model exists
Printer selected
Postcondition:
Model is printed / Modeler chooses Print from the File menu or presses the Printer icon on the toolbar.
Modeler selects a printer in the print dialog window.
Modeler presses OK button.
Save Model / Precondition:
Model exists
Postcondition:
Model is saved in a file / Modeler chooses Save or Save as from the File menu or presses the Save icon on the toolbar.
Modeler selects a file to save to in the save dialog window.
Modeler presses OK button.
Load Model / Precondition:
Model has been saved in a file.
Postcondition:
Model is loaded and drawn / Modeler chooses open from the File menu or presses the open button on the toolbar.
Modeler chooses the file to open.
Modeler presses the OK button
The saved file is drawn in a new child window.

Design

Design first, then code. The more complex the application, the more important this is. We will use the internationally recognized Unified Modeling Language (UML) to express our design.

Designing the application involves a number of activities:

  • Identifying needed classes, and within each class the operations required to support the use cases. We will use UML class diagrams.
  • Understanding the Windows event model, and describing how the application will capture and respond to events.
  • Understanding how to draw in the client part of a window so that the drawing will appear the same on different computers and when printed on paper.
  • Planning for storing a model using serialization and later reloading it.
  • For each use case, describe the message passing needed to carry out its flow of events scenario. We will use UML sequence diagrams.

Identifying Classes – First Try

It is time to start introducing classes to represent the objects drawn as part of the graph, namely nodes and links. We will be following some standard naming conventions. They are modeled after the so-called Hungarian Notation introduced by Charles Simonyi.

  • Class names start with “C”, as in “CNode”
  • Member variables start with “m_”. If they are pointers they start with “m_p”. The rest of the variable name is syllable-capitalized, i.e. each syllable begins with a capital letter.
  • Functions will also be syllable-capitalized. Their names should reflect actions in response to events, such as “OnSave()”.

A model created by the user consists of two collections, one of nodes and the other of links. It is these collections that need to be saved in order to save the model. We will use a subclass, CGraphicsEditorDoc of the MFC class CDocument to anchor these collections.

Here is an initial class diagram showing the CGraphicsEditorDoc, CNode and CLink classes, as well as the associations between them.

  • CGraphicsEditorDoc has associations to CNode and CLink.
  • The 1 label at the CGraphicsEditorDoc end and the 0..n label at the CNode end indicates that the association is one-many, i.e. one CGraphicsEditorDoc to many CNodes.
  • The association arrow indicates that it is navigable from CGraphicsEditorDoc to CNode
  • The role label m_Nodes at the CNode end will be used as a data field name when we actually implement the application in C++ and Java, namely the CGraphicsEditorDoc class will have a member field named m_Nodes whose type is a collection of references to CNode.
  • But the CNode class will not have a member of type CGraphicsEditorDoc.

The association from CGraphicsEditorDoc to Clink is similar. A Clink is a line segment from one CNode object to another. When a Clink needs to draw itself, it has to retrieve the CNodes to find their center points. Thus the need for the one-way associations labeled m_pFrom and m_pTo. On the other hand, CNodes have no need to access their links.

Windows Event Model

A Windows application is very complex. It must handle any number of user-initiated events, such as resizing, minimizing, responding appropriately to keystrokes, mouse clicks, etc. Windows need to be redrawn frequently as users switch between applications and drag things around the desktop. The application needs to be able to respond to user requests for printing, storing, loading, etc. whether made via menu selections or by pressing hot keys.

One standard approach to building a windows application is to use the Model-View-Controller architecture first developed by Smalltalk programmers. As applied to windows this requires:

A Document object. This is the model. This is where all data is stored. It is this class, which is responsible for saving and loading the model.

Zero or more View objects. A view shows part of the model in a window. Several views can be open on the same model. Windows passes events such as mouse actions to the topmost view to be handled.

An Application object. This is the program that is run from the operating system level, i.e. as by choosing it from the Start button. It puts up a basic menu that is expanded with more choices as soon as an existing document is loaded or a new one is started.

Control is first and foremost the responsibility of the Windows operating system, since it initially captures all user events. Typically, mouse events in a drawing window are handed off to the view object to handle. Menu choices are normally handed off to the Document object or the Application object to handle.

Identifying Classes – Second Try

The class diagram is augmented to show additional classes designed to support a Windows application. The naming follows the conventions of Microsoft Foundation Classes (MFCs).

The five boxes towards the top and right side of the diagram show base MFC classes. CMDIFrameWnd holds the application. It has menus, toolbars and a client area in which to open up child windows. Each child window is of type CMDIChildWnd. These two classes do not require customization.

CGraphEditorApp, CGraphEditorDoc and CGraphEditorView inherit most of their functionality from the base MFC classes, but are customized for this application. The point to the application is for the user to construct graphs using nodes and links. These graphs are stored with CGraphEditorDoc. This document class has a collection of CNodes. Each CNode has two collections of CLinks, one for outgoing and one for incoming links. A CNode has a center of base type CPoint.

A CGraphEditorView object shows part or all of a graph. It also responds to user mouse actions. A view object draws the graph by retrieving the collection of nodes from the document and calling their onDraw operations, passing them the graphical device context for the view. The node objects ask each of their outgoing links to draw themselves. They then draw themselves as ellipses in a square bounding rectangle.

Drawing Objects in a Window

The graphic editor will require that shapes and lines be drawn in a window or printed on paper. Your users may employ a variety of monitors, printers, graphics cards and display settings. When programming the application you won’t be aware of these details. For this reason, Microsoft® Windows® provides a device context (DC), which is a data structure that maintains information about the attached devices at run-time. Windows® also provides a graphics device interface (GDI); with a variety of operations for selecting pens, drawing lines and shapes, and so forth. Your application will draw (or print) itself by making calls on the GDI operations.

Locations on a drawing canvas are specified using a coordinate system, similar to the algebraic coordinate systems you used in High School. There are several coordinate systems to choose from. In Windows® they are called mapping modes. The simplest is MM_TEXT. In this mode, the origin is located at the upper left corner of the canvas, the X-Axis increases to the right and the Y-Axis (unlike in High School) increases downwards. Measurement is in pixels. So, the point (100,20) is located 100 pixels to the right, and 20 pixels below the upper left corner.

Unfortunately, MM_TEXT won’t be useful for us. The same mapping mode will be used for both drawing on the screen and printing. Since print resolutions are normally much higher than screen resolutions, drawings that seem well sized on the screen will come out tiny when printed. To get around this, we require a mapping mode that uses absolute coordinates measured in inches or centimeters.

One such mode is MM_LOENGLISH. Each logical unit measures 0.01 inches. Like in High School, the X-Axis increases to the right and the Y-Axis increases up. However, all drawing is done in the fourth quadrant, the one where x is positive, and y negative.

Storing and Loading Using Object Serialization

A model consists of a collection of nodes and links. When a model is loaded, these nodes and links are represented as objects in classes CNode and CLink. They are stored at various memory locations. Some Objects contain references (C-pointers) to other objects. These references are stored as memory addresses.

A problem comes about when a model is stored in a file and then reloaded. Even though it is easy to reconstruct stored objects from saved data, they will not be in the same locations as formerly. It is a difficult programming job to provide for proper storage and reconstruction of object systems. The general name for this is serialization. Objects are written to a file (stream) in some sequence, and later read back in the same order. References have to be stored and correctly reconstructed.

Fortunately, Java and MFC both provide support for serialization, although in slightly different ways. Serialization will be used to support the Save Model and Load Model use cases.

Message Passing Required to Carry Out Use Cases

Carrying out a use case requires the cooperation of a number of classes. Objects in these classes provide some services themselves and send messages (i.e. call functions) in other classes to provide additional services. This message passing is best illustrated using a UML sequence diagram. Normally, at least one sequence diagram is prepared for each use case. The diagrams capture the use case’s flow of events, both in terms of user actions and internal calls. For use cases that may run into exceptional behavior, there is normally one sequence diagram for the happy case scenario, and one for each exceptional scenario.

The On Draw Sequence Diagram

Windows frequently need to be redrawn: The user may have altered the model through one of the use cases, or the user may have taken some window action such as resizing or moving a blocking window to expose the one underneath. The On Draw Sequence Diagram illustrates the message passing involved in redrawing a model in a view.

  1. Windows sends an OnDraw message to CGraphEditorView passing a graphical device context, CDC, which can be drawn upon.
  2. In turn, CGraphEditorView calls CGraphEditorDoc’s OnDraw.
  3. CGraphEditorDoc iterates through its lists of CNode and Clink, calling their OnDraw functions. CNode draws a filled circle and Clink a line segment, using CDC operations.
The Insert Node Use Case Sequence Diagram

  1. The mode must be set to INSERTNODE.
  2. The modeler clicks the left mouse button.
  3. Windows calls the OnLButtonDown operation of CGraphEditorView, passing it the CPoint.
  4. CGraphEditorView constructs a CNode and calls CGraphEditorDoc’s AddNode operation.
  5. CGraphEditorView tells CGraphEditorDoc to update all views. CGraphEditorDoc in turn sends OnUpdate messages to each CGraphEditorView.
The Move Node Use Case Sequence Diagram

When a view object intercepts a mouse action, it is usually because the user wants to alter the diagram in some way, say by moving a node. The view figures out what changes need to be made, and appropriately updates the collections of nodes and links held in the document. The document then calls UpdateAllViews, so that all views open on the document are properly redrawn. This is illustrated in the UML sequence diagram below: