• Keine Ergebnisse gefunden

?-' Coordinai'es in Drawing (oni'exi

Im Dokument PenPomt GO (Seite 138-142)

Another vital property of the system drawing context is its arbitrary coordinate system. You can choose whether one unit in your drawing {as in "draw a line one unit long" is one point, 0.01 mm, 0.001 inch, 1120 of a point, one pixel on the final device. You can then scale units in both the X and Y direction; one useful scaling is to scale them relative to the height and width of your window. You can even rotate your coordinate system. What this gives you is the precision of knowing that your drawing will be an exact size. It also gives you the freedom to use any coordinate system and scale that suits your drawing. The default

coordinates are one unit is one point (approximately 1172 of an inch), and the origin is in the lower left corner of your window.

Hello World (custom window) uses the default units, but scales its coordinate system so that its text output remains at a regular aspect ratio.

"., When to Paint

Windows need to repaint themselves when they first appear on the screen, when they are is resized, and when they are exposed after other windows have covered them. Windows receive msgWinRepaint when the window system determines that they need to repaint, and windows must respond to this.

clsHello Win

only

paints in response to msgWinRepaint. The way most windows work is that they repaint dirty areas rather than paint newones. When awindow wants to draw something new, it can dirty itself and hence will receive

msgWinRepaint. clsHelloWin has no need to dirty itself since it doesn't change . what it paints.

".When 10 Creale Things?

The need to manage a separate object (a drawing context) introduces two crucial questions you need to consider when designing an application:

• When do I create and destroy an object (or resource)?

• When do I file it, if at all?

An application can create objects at many stages in its life. It can create objects at installation, at initialization (or at restore time), when opening, or when painting its windows. If your application can create an object just before it needs it, the less memory it consumes in earlier stages. But it takes time to create objects, so you must trade off memory savings with speed.

To decide when to create objects, you need to work backwards from when they are needed. In this case, Hello World only needs a drawing context in its window's repaint routine. Creating a DC every time you need to repa~nt is OK, but it is a fairly expensive operation. Besides, realistic applications often use a DC in input processing as well, to figure out where the user's pen is in convenient coordinates. However, we do know that a DC will never be needed when the view doesn't exist.

8.2.2

8.2.3

8.3

clsHello could create the DC and pass it to clsHelloWin, but it's usually much more straightforward for the objectthat needs another object to create that object.

Hello World creates its window when it receives msgAppOpen and destroys its window when it receives msgAppClose. These ~re reasonable times for the window to create its DC, so clsHello Win creates a DC when it receives msglnit and destroys the DC when it receives msgFree.

".. Instance Data

In our example, clsHello Win creates its DC in advance. This means that it has to store the UID of the DC somewhere so that it can use ir during msgWinRepaint.

In typical DOS C programs, you can declare static variables to hold information.

It is possible to do this in PenPoint, but in general you should not do it in object-oriented code.

Instead, you should store the information inside each object, in its instance data.

Up until now our classes have not had to remember state, so they haven't needed their own instance data. (Even if the class you create does not define instance data for its objects, its ancestors define some instance data, such as the document name and the label of the toolkit field.)

Specifying instance data is easy. You just tell the Class Manager how big it is (in the class. size field) when you create your class. You would typically define a typedef for the structure of your class's instance data, then give the size of this as the class.size. In the case of dsHelloWin, we define·a structure called

"

INSTANCE_DATA:

typedef struct INSTANCE_DATA {

SYSDC dc;

} INSTANCE_DATA, *P_INSTANCE_DATA;

and then in CIsHello WinInit:

STATUS ClsHelloWinlnit (void)

(

CLASS NEW new;

STATUS s;

II Create the class.

ObjCallWarn(msgNewDefaults, clsClass, &new);

new.object.uid = clsHelloWin;

new.cls.pMsg = clsHelloWinTablei new.cls.ancestor = clsWin;

new.cIs.size = SizeOf(INSTANCE_DATA);

new.cls.newArgsSize = SizeOf(HELLO_WIN_NEW);

ObjCallRet(msgNew, clsClass, &new, S)i

".. Is It msgNew or msglnit?

.& we discussed, dsHelloWin creates its DC when it is created. It does this by responding to msgInit.

Note that dsHelloWin responds to msglnit, not msgNew. When you create an object, you send its class msgNew. No classes intercept this message, so it goes up

8.3.1

8.3.2

CHAPTER 8 I CREATING A NEW CLASS (HELLO WORLD: CUSTOM WINDOW) 131 When to Create Things?

the ancestor chain to clsClass, which creates the new object. The Class Manager then sends msgInit to the newly-created object, so that it can initialize itself.

". Window Initialization

Here's the Hello WinInit code which creates the Hello Window in response to msgInit:

clsHelloWinInit declares an instance data structure. It does this because the pointer to instance data passed to message handlers by the Class Manager (pData, unused in this routine) is read-only.

It then initializes the instance data to zero. It's important for instance data to be in a well-known state. This isn't necessary in the case of clsHelloWin, since the <:>nly instance data is the DC UID that it will fill in, but it is good programming practice.

II

Null the instance data.

memset(&data, 0, sizeof(INSTANCE_DATA»;

clsHello Win then creates a DC:

II

Create a dc.

ObjCallWarn(msgNewDefaults, clsSysDrwCtx, &dn);

ObjCallRet(msgNew,clsSysDrwCtx, &dn, s);

When msgNew returns, it passes back the UID of the new system drawing context. This is what clsHello Win wants for its instance data:

data.dc = dn.object.uid;

clsHelloWin sets the desired DC state (including the line thickness) and binds it to self (the instance that has just been created when HelloWinInit is called):

II

Rounded lines, thickness of zero.

ObjectCall(msgDcSetLineThickness, data.dc, (P_ARGS)O);

i f (DbgFlagGet (' F', Ox40L» (

Debugf("Use a non-zero line thickness.");

ObjectCall(msgDcSetLineThickness, data. dc, (P_ARGS)2);

II

Open a font. Use the "user input" font (whatever the user has

ObjCallJmp(msgDcOpenFont, data. dc, &fs, s, Error);

8.3.3

II II

Scale the font. The entire DC will be scaled in the repaint

II

to pleasingly fill the window.

fontSeale.x = fontSeale.y = FxMakeFixed(initFontSeale,O);

ObjeetCall(msgDeSealeFont, data.de, &fontSeale);

II

Bind the window to the de.

ObjeetCall(msgDeSetWindow, data. de, (P_ARGS)self);

At this point, clsHello Win has set up its instance data in a local structure. It calls ObjectWrite to get the Class Manager to update the instance data stored in the Hello Window instance:

II

Update the instance data.

ObjeetWrite(self, etx,&data);

return stsOK;

".. Using Instance Data

Accessing instance data is easy. The Class Manager passes a read-only pointer to instance data into the class's message handlers.

The Class Manager has no idea what the instance data is, so it just declares the pointer as a mystery type (p _DATA, which is defined as P _UNKNOWN). The MsgHandler macro names the pointer pData.

c1sHello Win needs to access its instance data during msgWinRepaint handling so it can use the DC. It knows that the instance data pointed to by pData is type INSTANCE_DATA, so it uses the MsgHandlerWithTypes macro, which allows it to provide the.types (or casts) for the argument and instance data pointers:

MsgHandlerWithTypes(HelloWinRepaint, P_ARGS, P_INSTANCE_DATA)

You can pass the pData pointer around freely within your code, but whenever you want to change instance data, you must dereference it into a local (writable) variable, modify the local variable, and then call ObjectWrite. c1sHelloWin creates its DC when it is created, and never changes it, so it doesn't have to worry about de-referencing its instance data into local storage. But c1sCntr, described in Chapter 9, does have to do this .

. ", No Filing Yet

On a page turn, the process and all objects associated with a Hello World (custom window) document are destroyed. Normally this means that objects have to file their state. However, since clsHello Win destroys its DC when it is destroyed and never changes its DC's state, it doesn't have to file its DC.

The application does not file its view-it creates it at msgAppOpen to draw, then destroys it at msgAppClose, and there's no useful state to remember from the DC. You could imagine an application which would want to remember some of the state of its DC. For example, if the user could choose the font in Hello World (custom window), then the program would need to remember what the font was

8.4

8.4.1

CHAPTER 8 / CREATING A NEW CLASS (HELLO WORLD: CUSTOM WINDOW) 133 Debugging Hello World (Custom Window)

so that when the user turns back to the application's page the application continues to use the same font.

Im Dokument PenPomt GO (Seite 138-142)