text:
Programming Mac OS X with Cocoa for Beginners/Document-based applications

Previous Page: Graphics - Drawing with Quartz | Next Page: Implementing Wikidraw

By now we have seen that Cocoa supports a "document based" multiple window type of application, which we will be basing our drawing program, Wikidraw, on. A better understanding of this will help us work through our design implementation.

A document is not itself a window. A window is used to view the contents of the document, and in many straightforward applications, there is a one-to-one mapping from NSDocument, the class that manages the document, and NSWindow, the window class. However, this is not the only approach - an application could offer several alternative ways of looking at a document, and therefore support more than one window per document. This is one reason why two different objects are used.

In Wikidraw, we will subclass NSDocument so that we have a place to store our drawing data as we draw it, and a place to place the various actions that we will need, such as Cut and Paste, etc. The view in our window simply provides a means of displaying the current state of the document. Ideally, we'd like to minimise the interdependence between the two classes, so that the internal workings of the document are not exposed to the view, and vice versa, in accordance with the principle of encapsulation. Because the view provides not just the drawing of the objects, but also the interaction with the user, the view will need to have some knowledge of the objects, so that it can pass on mouse clicks and so forth to them.

Delegation and protocols
Cocoa makes much use of a common design pattern called delegation. This is where an object relies on another object to supplement some of the details about its state, etc. The helper object is known as the delegate of the first object. Unlike subclassing, the delegate can be any other suitable object, and the runtime binding of methods means that the original object only needs to have the vaguest knowledge about the delegate to get information from it.

In Wikidraw, we can make the document a helper of our view. That way, the view can ask its delegate for basic information such as the list of objects in the drawing, or the list of selected objects, without requiring too much knowledge about the document object itself. Both objects agree to conform to a protocol for exchanging information. This is merely an agreed convention for some of the method names. Because the protocol is just a casual agreement, it's called an informal protocol. Objective-C supports a stricter type of protocol, called a formal protocol, using the '@protocol' keyword, but we won't be using that here.

Implementing MyDocument
In setting up our document-based application, Xcode and Interface Builder have already provided a starting point for us, a class called 'MyDocument'. This is a subclass of NSDocument that we will use to manage each Wikidraw document. If you find this file in XCode (Wikidraw->Classes->MyDocument.h) and select it, you'll see that it's an empty subclass of NSDocument. Select the .m file to show the current implementation. It contains a number of skeletal methods to get you started, though enough has been included to make the basics actually work, such as showing the window.

Now we need to add some data members and methods to MyDocument.h so that it can start to handle the task of managing our drawing.

@interface MyDocument : NSDocument {	NSMutableArray*	_objects; NSMutableArray*	_selection; }

/* drawing maintenance */

- (void)		addObject:(id) object; - (void)		removeObject:(id) object; - (NSArray*)	objects;

/* selection maintenance */

- (void)		selectObject:(id) object; - (void)		deselectObject:(id) object; - (void)		selectAll; - (void)		deselectAll; - (NSArray*)	selection;

/* clicks */

- (id)			objectUnderMousePoint:(NSPoint) mp;

@end

We'll add more later, but these are the basic methods we'll need. There are two data members, each a mutable array. One contains all the objects in the drawing, the other just the objects in the selection. When we create a new object, it is added to the _objects list using addObject:, if we delete an object, it is removed using removeObject: We can make these actions undoable easily by recording the state of the _objects array before each of these operations. We'll come to that later, but for now it's only important to understand that to make Undo work simply, defining a couple of obvious places where the data is changed is needed. We'll do the same in our actual objects where they are edited, for example by having their size or position changed.

The selection works similarly. Objects are added to the selection array or removed from it. When the view comes to draw the drawing, it checks both arrays. If an object is found in both, it "knows" that the object should be drawn in its selected state. The object itself handles its own drawing, so all that is needed is for the view to request that it is drawn with selection highlighting or not. This way, the view simply acts as a mediator between the document, which maintains WHICH objects are selected, and the actual objects, which know HOW to portray the selected state. The view itself is not interested in what is selected. We'll see that this approach is quite straightforward and elegant, though it is not the only possible one. (A common alternative is for the selected state to be a boolean flag within each stored object, but that isn't usually the best approach in an application like this. Many document-level commands apply to the set of selected objects, so to implement these with our scheme involves a simple iteration of the _selection array. Using the flag approach, we'd have to go through the entire list, checking which objects had the flag set, and ignoring the others. In general this can hamper performance once the drawing gets large).

The 'objectUnderMousePoint:' method is provided as a simple way for the view to determine what was clicked. The view itself will handle the basic mouse events, such as clicking and dragging, and this method allows it to determine which object the operation is dealing with. Once it has that information, the view is able to implement the clicks and drags by simply handing them off to the object itself, which knows what to do (resize, move, etc). The document doesn't need to know what the user is doing, it just needs to track what's in the drawing. You'll see that we are pushing as much of the 'smarts' into the drawing objects themselves as possible.

Saving and restoring from files
A key ability of a document is to convert its internal representation to and from a file representation of the same data. You'll see in MyDocument.m that two skeleton methods to do this have been provided:

- (NSData*)  dataRepresentationOfType:(NSString*)  aType - (BOOL)      loadDataRepresentation:(NSData*) data ofType:(NSString*) aType

These are overrides from NSDocument, so are not declared in our class definition. The first is responsible for saving to a file, the second reading in a file. The 'type' string is used when we have more than one type of file we can read and write to — many real applications will support different formats. Here we won't, we'll just have our own. The string passed is actually the file extension of the file, so if you opened a JPEG file, the type would be the string "jpg".

So what do we do to make the file representations, or read from a file? It's easy. Cocoa supports this using something called an archiver. Most objects can be archived, meaning that they already know how to read and write themselves to a file stream. Doing this requires no more than writing values to a dictionary using a key, which we already know how to do. Each object in the drawing is asked to archive itself, so it must record as much information as necessary to completely recreate that object later — its position, size, colour, etc. Each value is given a unique key, usually just a string. When the file is read back in later, each object is reinstantiated and given the opportunity to re-establish its data values from the stream. Using the same keys, the values are read back in, thus restoring the object to the saved state.

At the document level, all we need to do is to archive the '_objects' array. When an array is archived, all of the objects it contains are also automatically archived. We'll find that at this level, there is almost nothing to do — all the hard work is, once again, done by the drawing object itself. The selection state is not saved, so we don't need to care about that.

Since the entire _objects array will be recreated from the file, MyDocument needs a way to set the whole array in one go. The obvious method would be one called setObjects, so let's add that to our definition.

- (void)		setObjects:(NSMutableArray*) arr;

For now, this is enough to get started on our document implementation. We need to expand the methods we've declared in the implementation file. One way is to cut and paste them from the header, then add curly braces to turn them into actual methods. Do that now.

The implementation is pretty straightforward. In the init method, we allocate _objects and _selection. We need a dealloc method to make sure they get released when our document is deallocated.

addObject: and removeObject: simply map to the NSMutableArray methods, though we additionally check that we are not adding the same object more than once. When an object is removed, it is also deselected, to make sure that we don't leave a stale reference in the selection list to an object that has gone altogether. objects simply returns the array itself as a read-only type (this gets around Objective-C's lack of a 'const' operator.) setObjects: replaces the entire array, which we'll use in a moment when we read a file.

The selection methods work similarly. selectObject: merely adds the object to the selection list, first checking that it isn't already selected and is indeed part of the drawing. deselectObject: removes it from the selection list. selectAll makes the selection list match the entire drawing using setArray:, and deselectAll simply makes the list empty.

The objectUnderMousePoint: method gives us a chance to use an iterator. We iterate over the objects array, calling containsPoint: on each object. As soon as one of them return YES, the method returns that object. It is up to the object itself to implement containsPoint: in a sensible manner. Why do we iterate here in reverse? It's because in the drawing, objects can be placed such that they overlap. To the user, this will appear as if some objects are behind others. The view will draw the list of objects in forward order, meaning that objects at the end of the list will get drawn 'on top of' any earlier ones. By detecting clicks in reverse, we make sure that we find the topmost objects before any underlying ones, if the click occurs on an overlapping area. It means that the user gets the expected behaviour—they can click on what they can see. If nothing gets clicked, nil is returned.

The methods that implement archiving and dearchiving a file are straightforward. Most of the magic is hidden from us at this level. We use NSKeyedArchiver's factory method 'archivedDataWithRootObject:' to pass it our objects array. It does its magic and returns us an NSData object, which we return. That's all we need to do, Cocoa handles the rest, writing the data to the file. When we go the other way, we use NSKeyedUnarchiver's 'unarchiveObjectWithData:' factory method to reverse the process. What we get back is an autoreleased NSArray object (because we saved an NSArray object earlier). You may ask how the archiver and unarchiver know how to encode or decode WKDShape objects. The answer is simple: they don't. As usual in object oriented programming, the WKDShape object is responsible for encoding and decoding itself. The (un)archiver will try to call corresponding methods of WKDShape which are not yet implemented. This is done in the chapter Archiving. At this point you may save a document and open a saved document, but it will be blank and not contain any shapes.

However we need the array obtained from the unarchiver to be mutable, so we use mutableCopy: to make a mutable copy. This is passed to the setObjects: method replacing any objects we already have. Because we made an explicit copy, we must release the object here. setObjects: retains it anyway, so it still sticks around, but we don't want to let it remain retained twice, otherwise we'll have a potential memory leak. setObjects: also deselects everything, since we are starting with a new freshly opened document—this just makes sure that no stray objects are left selected. This could happen after a revert, for example.

File types
How does Cocoa know what type of file we are saving or reading? It doesn't yet - if we Build and Go now, choosing Open gives us the file picker but we won't be able to pick any files. To set this, in the left hand panel of Xcode, Drill down from Targets to Wikidraw. Select Wikidraw and choose Get Info. In the dialog that opens, select the 'properties' tab. In the 'Document types' area, edit the first and only item so that Name is 'Wikidraw drawing', Extensions is set to 'wkd', OSTypes is set to '.wkd' and class is MyDocument. This sets up a mapping between files of type'.wkd', which will be our drawing file type, and the MyDocument class, which knows how to handle that file type. (Please note: dataRepresentationOfType: is Deprecated in Mac OS X v10.4. Use dataOfType:error: instead. for save to work)

While we are here, lets also change a few other settings. Change 'Identifier' to 'com.wiki.wikidraw', and Creator to 'WIKD'.

Close the Get Info window, and Build and Go again. We still have no files available in the open dialog, because there are no files with an extension .wkd yet. So let's save one. Do a Save As and save the file as 'wikidrawtest' or something similar. The open window should change to the name you choose. Now when you try an Open, you should be able to select and open this file. If you locate the file in the Finder, do a Get Info, you should be able to check that it does indeed have an extension of '.wkd'. Note that the file will not contain anything yet, since we don't have enough of the application built to actually create any drawings.

We'll tackle that next.

Previous Page: Graphics - Drawing with Quartz | Next Page: Implementing Wikidraw


text:
User:Gunblade~enwikibooks

de:User:gunblade tr:User:gunblade fr:User:gunblade es:User:gunblade als:User:gunblade simple:User:gunblade

license: Creative Commons - Attribution Share-Alike - https://creativecommons.org/licenses/by-sa/4.0/

title: User:Gunblade~enwikibooks


text:
Programming Mac OS X with Cocoa for Beginners/Implementing Wikidraw

Previous Page: Document-based applications | Next Page: Wikidraw's view class

So far we have managed to get a very long way with hardly any code. However, our application still doesn't do much, and doesn't look too much like a real drawing program. So now we'll write the code at the heart of the application, which implements the actual shapes we can draw. We have deferred a lot of functionality by design to this class, so this will be a moderately complex object. However, once we are done, we'll have something very close to a drawing program!

Up until now we have used Interface Builder to create skeletal code files for us. This time, we'll have to do it manually. In XCode, find the file 'WKDDrawView.m' and select it. Then choose File->New File... In the assistant, locate and select 'Objective-C class', then click Next. Change the filename to 'WKDShape.m', then click Finish. A new .h and .m file are added to the 'Classes' list. You'll see that WKDShape is a subclass of NSObject, which is ideal.

Design of the shape class
Each shape will be an instance of the WKDShape class, but each type of shape will be a subclass, for example WKDRectShape and WKDOvalShape. In the Shape class itself, we need to put as much functionality that is common to all shapes as possible. In fact we'll see that this is almost all of it.

What properties does each shape need? There are graphical properties such as the colours to fill and stroke the shape, and whether the shape has a stroke or fill, and what thickness the stroke should be. There are geometrical properties such as the location and size of the shape, and if we were implementing rotation, a rotation angle (for simplicity we won't be doing this, but you might like to consider how it could be added as a further exercise). What we don't need to include is state information such as whether the object is selected - it doesn't need to know and it's already taken care of by the document/view combination.

Cocoa embodies colours in the NSColor object, so we can specify our stroke and fill colours using these. We can use the absence of either (a value of nil) to mean not to perform a stroke or a fill, allowing us to have hollow as well as solid shapes. The stroke thickness is a simple floating point value. The position of the object can be specified using an NSPoint, and the size using an NSSize. We'll decide that the location of the object refers to the top, left corner of its bounding box. An NSRect contains both an NSPoint specifying the origin of the rect, and an NSSize for its width and height, which is exactly what we need. A shape is drawn so that it fits this bounding box rectangle.

So let's add these data members:

@interface WKDShape : NSObject {	NSRect		_bounds; NSColor*	_strokeColour; NSColor*	_fillColour; float		_strokeWidth; }

@end

These immediately suggest the first set of methods we'll need, which simply set and get these properties. We also, for convenience, define some methods for setting the location and size independently. We also know from developing our document class that we need a 'containsPoint' method, returning a BOOL value:

- (NSRect)         bounds; - (void)           setBounds:(NSRect) bounds; - (void)           setLocation:(NSPoint) loc; - (void)           offsetLocationByX:(float) x byY:(float) y; - (void)            setSize:(NSSize) size; - (BOOL)           containsPoint:(NSPoint) pt;

- (NSColor*)       fillColour; - (void)           setFillColour:(NSColor*) colour; - (NSColor*)       strokeColour; - (void)           setStrokeColour:(NSColor*) colour; - (float)          strokeWidth; - (void)           setStrokeWidth:(float) width;

Now let's think about what we need the shape object to do. The most obvious thing is to draw itself, so we'll need a 'draw' method. This will be called by the view for each object in turn. In addition, the view will inform us whether we are selected or not, so we need a way to pass this information in so we can draw accordingly.

The next thing is user interaction with the object. The view will pass us clicks and drags, allowing the user to resize or reposition an object directly. We can figure out ourselves which operation is intended from where the mouse point was initially clicked. A view handles mouse operations by implementing three methods for mouse down, mouse dragged and mouse up. We can follow the same model here, making it very easy for the view to pass these things on to us. We'll need to track what operation we are performing (resize or reposition) so that when we receive a drag message, we continue doing the right thing! We'll track this state in a simple integer data member.

When an object is selected, it is drawn with "selection handles" around its edges. If we drag a handle, we want the size of the shape to be altered. If we drag the object itself, it should remain the same size but move to a new position. When a handle is dragged, the opposite handle becomes an 'anchor point' for the resize, so we need a way to distinguish between handles to work out which anchor to use. We'll use the integer data member we already have for this - its value will be set to the number of the handle we originally clicked. We'll also need to record the original mouse click position so we can work out how far things have been moved. We'll provide a separate method for drawing the handles as sensible factoring of code, as well as a number of utility methods for drawing and hit testing each handle.

Finally we need to allow subclasses to actually provide the detail of the shape being drawn. We could rely on subclasses reimplementing draw as needed, but a better approach which keeps subclasses smaller and simpler is to ask for the shape's path as an NSBezierPath object, then implement everything else in the common shape object. All the subclass needs to do is to use the current value of bounds to return an appropriate path whenever asked.

So let's add these methods:

- (void)           drawWithSelection:(BOOL) selected; - (void)           drawHandles; - (void)           drawAHandle:(int) whichOne; - (int)            handleAtPoint:(NSPoint) pt; - (NSRect)         handleRect:(int) whichOne; - (NSRect)         newBoundsFromBounds:(NSRect) old forHandle:(int) whichOne withDelta:(NSPoint) p;

- (void)           mouseDown:(NSPoint) pt; - (void)           mouseDragged:(NSPoint) pt; - (void)           mouseUp:(NSPoint) pt;

- (NSBezierPath*)  path;

We also add an int data member, _dragState, and an NSPoint data member, _anchor.

When we begin to draw a shape, it will be created and added to the drawing at the point where we clicked. It will then be in exactly the same state as if an existing object were being resized, so there is no difference between creation and later editing - we just arrange things so that new objects get created under the mouse. The view will deal with this.

Shape implementation
Now we can implement the methods for WKDShape. We'll supply a default path method so that using an unsubclassed shape object works - in fact, we can make the generic shape object handle the simple rectangle case.

Cut and Paste the method prototypes into the WKDShape.m file. Here's a quick trick for expanding them, which will work at this stage since there is no code written yet. Expand one method by replacing the trailing semicolon with a pair of curly braces and an extra line. Select and copy just the braces and the extra line. Open the Find/Replace dialog and do a Replace All on a semicolon with the lines you copied (paste the lines into the Replace field). This replaces all semicolons with the empty method bodies and an extra line, thus expanding all the method prototypes to full methods.

Here is the implementation for the property methods and initialisation:

- (id)			init {	if ((self = [super init]) != nil ) {		[self setFillColour:[NSColor whiteColor]]; [self setStrokeColour:[NSColor blackColor]]; [self setStrokeWidth:1.0]; _bounds = NSZeroRect; _dragState = 0; _anchor = NSZeroPoint; }	return self; }

- (void)		dealloc {	[_fillColour release]; [_strokeColour release]; [super dealloc]; }

- (NSRect)		bounds {	return _bounds; }

- (void)		setBounds:(NSRect) bounds {	_bounds = bounds; }

- (void)		setLocation:(NSPoint) loc {	_bounds.origin = loc; }

- (void)		offsetLocationByX:(float) x byY:(float) y { _bounds.origin.x += x;	_bounds.origin.y += y; }

- (void)		setSize:(NSSize) size {	_bounds.size = size; }

- (BOOL)		containsPoint:(NSPoint) pt { return NSPointInRect( pt, [self drawBounds]); }

- (NSColor*)	fillColour {	return _fillColour; }

- (void)		setFillColour:(NSColor*) colour {	[colour retain]; [_fillColour release]; _fillColour = colour; }

- (NSColor*)	strokeColour {	return _strokeColour; }

- (void)		setStrokeColour:(NSColor*) colour {	[colour retain]; [_strokeColour release]; _strokeColour = colour; }

- (float)		strokeWidth {	return _strokeWidth; }

- (void)		setStrokeWidth:(float) width {	_strokeWidth = width; }

These should be straightforward. In our init method, we call our own set...Colour methods to set the default colours to a white fill and a black border, as well as setting a stroke width of 1.0 and setting the other data members to zero. NSZeroPoint, NSZeroSize and NSZeroRect are all handy constants in Cocoa representing those structures with all-zero members. Note that our set...Colour methods retain before release. We retain these objects because we are creating new references to them within the data members of our shape object.

The drawWithSelection: method comes next:

- (void)		drawWithSelection:(BOOL) selected {	NSBezierPath* path = [self path]; if ([self fillColour]) {		self fillColour] setFill];		[path fill];	}	if ([self strokeColour])	{		[[self strokeColour] setStroke];		[path setLineWidth:[self strokeWidth;		[path stroke];	}	if ( selected )		[self drawHandles]; }

First we call our own path method to get the path of the shape. Subclasses of WKDShape will be overriding this to provide us with other shapes, but the default one will give us a basic rectangle. Having got the path, we can stroke and fill it if we have colours for them - the absence of a colour means don't perform the operation. Finally, we use the selection flag we are passed to determine if we should draw the selection handles or not. Let's look at how the handles are drawn.

- (void)		drawHandles {	int h;	for( h = 1; h < 9; h++ ) [self drawAHandle:h]; }

- (void)		drawAHandle:(int) whichOne {	NSRect hr = [self handleRect:whichOne]; [[NSColor redColor] set];	NSRectFill( hr ); }

The method drawHandles iterates a short loop, calling drawAHandle for each one. We identify each handle using the numbers 1 to 8, with 1 meaning the top, left handle, number 2 the top centre handle, and so on round the edges of the shape in a clockwise direction. The numbering is arbitrary, but we need to be consistent. We did not use zero as a handle ID, because we use 0 elsewhere to mean 'drag the whole object' rather than drag a particular handle. This scheme is very simple, though other numbering schemes like it could be devised that could make the later handle code a little bit more compact. However, for this exercise this is entirely adequate and will function just fine.

The drawAHandle method calls handleRect: to obtain a rectangle representing the handle, then simply blocks it in using a bright red colour.

- (NSRect)		handleRect:(int) whichOne {	NSPoint	p; NSRect	b = [self bounds]; switch( whichOne ) {		case 1: p.x = NSMinX( b ); p.y = NSMinY( b ); break; case 2: p.x = NSMidX( b ); p.y = NSMinY( b ); break; case 3: p.x = NSMaxX( b ); p.y = NSMinY( b ); break; case 4: p.x = NSMaxX( b ); p.y = NSMidY( b ); break; case 5: p.x = NSMaxX( b ); p.y = NSMaxY( b ); break; case 6: p.x = NSMidX( b ); p.y = NSMaxY( b ); break; case 7: p.x = NSMinX( b ); p.y = NSMaxY( b ); break; case 8: p.x = NSMinX( b ); p.y = NSMidY( b ); break; }	b.origin = p;	b.size = NSZeroSize; return NSInsetRect( b, -kHandleSize, -kHandleSize ); }

HandleRect: consists of a big switch statement which sets up the position of the handle based on the current bounds of the shape. This is simple to understand but a bit dumb - we could have used bitfields to combine handles that share a side into a more compact function, but one that would be much harder to understand at a glance. This way is much easier. We use Cocoa's utility functions NSMinX, NSMinY, MSMidX, NSMaxY, etc to obtain particular corners of the bounding rect which is where we will locate the handles. Finally, we expand these points into a small rectangle and return it.

Now let's look at how we handle interaction with the mouse. The first thing that happens when the mouse is clicked is that the view determines which, if any, shape was hit, and passes the click down to its mouseDown method:

- (void)		mouseDown:(NSPoint) pt { _dragState = [self handleAtPoint:pt]; _anchor = pt; }

All we do is call handleAtPoint: to see if any of the handles were hit. If a handle was hit, its number is returned and stored in _dragState. If no handle was hit, 0 is returned. This means 'drag the whole object'. We can be sure that we were hit at all because this method wouldn't even be called if we hadn't been. We also record the initial mouse click point in _anchor. At this stage, we only determine what a subsequent drag is going to do - we don't need to actually do any more. Let's look at how handleAtPoint: works.

- (int)			handleAtPoint:(NSPoint) pt { int		h; NSRect	hr; if ([self bounds].size.width == 0 && [self bounds].size.height == 0 ) return 5; else {		for ( h = 1; h < 9; h++ ) {			hr = [self handleRect:h]; if (NSPointInRect( pt, hr )) return h;		} }	return 0; }

It's quite straightforward. We loop through the handles, obtaining the rect of each one, using handleRect: that we used before for drawing. If the rect contains the point, we return the handle's index number immediately. Otherwise, if we can't find any handles that the mouse has hit, we return 0. So what's this other bit, that checks the size? This is a little bit of a hack which simplifies code elsewhere. When an object is initially created, it has zero size and is placed under the mouse. From then on, the creation of the object proceeds just the same as for editing an existing one. However, at the moment of creation, we need to trick the system into thinking that we are dragging the bottom right corner of the shape, so that the user gets the expected behaviour. The index number for the bottom right corner's handle is 5, so if we have zero size, we immediately return 5 as if it were this handle that had been clicked.

After the view calls our mouseDown method, it will continually call our mouseDragged method until the user lets go of the button, at which point it will call our mouseUp method.

- (void)		mouseDragged:(NSPoint) pt { NSPoint np; np.x = pt.x - _anchor.x;	np.y = pt.y - _anchor.y;	_anchor = pt; if ( _dragState == 0 ) {		// dragging the object [self offsetLocationByX:np.x byY:np.y]; }	else if ( _dragState >= 1 && _dragState < 9 ) {		// dragging a handle NSRect nb = [self newBoundsFromBounds:[self bounds] forHandle:_dragState withDelta:np]; [self setBounds:nb]; } }

- (void)		mouseUp:(NSPoint) pt { _dragState = 0; }

mouseDragged: first calculates how far the mouse has moved since last time. _anchor is used to store the previous mouse position, as all we need to do is to subtract the co-ordinates of the new point from it, then update _anchor to the new point ready for next time.

Next we look at _dragState. If it's zero, we are dragging the whole object, so we can simply offset our position bodily using offsetLocationByX:byY: using our calculated delta. Otherwise if _dragState is one of the handle index values, we know we are dragging a handle, and which one it is. We defer to another method, newBoundsFromBounds:forHandle:withDelta: to calculate what our new bounding rectangle will be given the old bounds, the handle index number being dragged, and the delta offset of the handle. It returns the recalculated bounds, so we can simply call setBounds to make it take effect.

mouseUp: doesn't need to do much here - it just sets _dragState to 0. This isn't absolutely necessary, but we do it just in case someone else is using this information - since we've finished dragging, we forget about what handle, if any, we dragged.

The code for newBoundsFromBounds:forHandle:withDelta: does most of the hard work in determining how a handle drag affects the bounds. Here it is:

- (NSRect)		newBoundsFromBounds:(NSRect) old forHandle:(int) whichOne withDelta:(NSPoint) p { // figure out the desired bounds from the old one, the handle being dragged and the new point. NSRect nb = old; switch( whichOne ) {		case 4: nb.size.width += p.x;			break; case 6: nb.size.height += p.y;			break; case 2: nb.size.height -= p.y;			nb.origin.y += p.y;			break; case 8: nb.size.width -= p.x;			nb.origin.x += p.x;			break; case 1: nb.size.width -= p.x;			nb.origin.x += p.x;			nb.size.height -= p.y;			nb.origin.y += p.y;			break; case 3: nb.size.height -= p.y;			nb.origin.y += p.y;			nb.size.width += p.x;			break; case 5: nb.size.width += p.x;			nb.size.height += p.y;			break; case 7: nb.size.width -= p.x;			nb.origin.x += p.x;			nb.size.height += p.y;			break; }

return nb; }

Similar to handleRect:, it uses a switch statement to figure out which edges of the rectangle get moved according to the handle index. It's straightforward - if a right or bottom edge moves, the delta can simply be added to the width or height. If a left or top edge moves, we need to adjust both the width or height and the origin position of the rectangle.

That's pretty much it. When we look at how our view works, we will find we need to add one or two additional minor methods here to help things along, but we'll worry about that later. The final thing to look at here is the implementation of path. You'll recall that this is what subclasses are intended to override to supply the particular shapes that we will be drawing. The default version looks like this:

- (NSBezierPath*)	path {	return [NSBezierPath bezierPathWithRect:[self bounds]]; }

All it does is use NSBezierPath's factory method for making a path from a rectangle, in this case our bounds. The path returned is already autoreleased, so we can just use it and forget it, which is what our drawWithSelection: method does. Other shape objects can return any path that they want, provided they honour the one rule that we define here - that bounds fully encloses the shape.

Previous Page: Document-based applications | Next Page: Wikidraw's view class


text:
Programming Mac OS X with Cocoa for Beginners/Wikidraw's view class

Previous Page: Implementing Wikidraw | Next Page: More shapes

We have our basic shape class in place, and a document structure to store them. The final main piece of the jigsaw is the view, which pulls everything together to allow the user to create drawings interactively. Let's consider its design.

Design of the view class
A generic NSView provides two main pieces of functionality - the ability to draw graphics in a window, which we have looked at already, and the ability to handle the user's mousing, which is something new. We've already discussed that this comes in three parts - a mouse down event, a series of mouse dragged events, and a mouse up event. Our shape class is all ready to handle these when they are passed along from our view.

The view will need to do more than just pass along these events however, since it will need to deal with other aspects of user interaction, such as the fact there will be many different shapes in the drawing, and the need to manage the selection of them as the user clicks.

In addition, the creation of shapes will depend on which tool is currently selected in the tool palette (which we haven't designed yet). Since shape creation is also accomplished by using the mouse and drawing in the view, it is the view that will need to provide a means of distinguishing between the various tools.

When it comes to drawing, the view needs to render the entire drawing, and also determine if an object is selected. While the shape itself handles the actual rendering of itself, the view must make sure that every object's drawWithSelection: method is called, and pass along the selection state.

Where does the view obtain the information from the document? We'll set things up so that the view is able to call upon the document as a delegate. We established the informal protocol for getting the objects and the selection using the document's objects and selection methods. Provided we set up a valid delegate, the view can call it using this informal protocol, and get the information it needs. NSView doesn't have a delegate by default, so we need to add that ourselves. Add a _delegate data member to the WKDDrawView class, with type 'id'. Then add methods for setting and getting this - setDelegate, and delegate. Note that the view doesn't need to retain its delegate in this case - it is an exception to the usual rule about creating new references to object. In this case it's OK because we know that the document and the view will always be created and destroyed together.

- (void)	setDelegate:(id) aDelegate {	_delegate = aDelegate; }

- (id)		delegate {	return _delegate; }

Now we need to make sure the delegate gets set to the document when the view is created. To do this, we'll create a link in Interface Builder so that the document can find the view, then call its setDelegate method, passing itself. This seems a bit roundabout, but it prevents too much interdependency between the view and the document.

In MyDocument.h, add a data member as follows:

IBOutlet id	    _mainView;

Then Save the file. What we've done here is create a reference to an object called _mainView. We've also flagged it as an IBOutlet. Earlier we saw how a return type of IBAction can be used to provide information to Interface Builder. This is similar. It tells IB that this particular data member can be an outlet, that is, one end of a connection to another object, which can be accessed using the control-drag connection method. In Xcode, IBOutlet is defined as nothing, so it doesn't affect the code in any way.

Drag MyDocument.h to the MyDocument.nib window open in Interface Builder. It will reparse the file, and pick up the new outlet. Now we need to connect this outlet to the window's view (WKDDrawView).

You might be wondering how we can do this, since the MyDocument class doesn't seem to be respresented in Interface Builder. Indeed this class is created by Cocoa when we open a file or choose New, so how do we solve this one? This is where the mysterious "file's owner" icon comes in. The object that owns 'MyDocument.nib' is in fact, the MyDocument class. So file's owner represents it. Having dragged in MyDocument.h you'll notice a small exclamation mark on the File's Owner icon. This means that there are unconnected outlets in that object, which is true, since we just added the outlet _mainView, but haven't connected it yet.

Control-drag from File's owner to the view within the window. Make sure it is WKDDrawView that is highlighted, not the enclosing scroller. When you let go, the Inspector shows the Connections, and switches to Outlets. Select '_mainView' and click connect. Save the file and return to Xcode.

Now when MyDocument is instantiated, the data member _mainView will be automatically set up pointing to the WKDDrawView in the window. So all we now need to do is to use this reference to set the view's delegate. When MyDocument is instantiated, one of the last methods called as part of the initialisation is 'windowControllerDidLoadNib:'. At this point all connections declared in the .nib file, like the one we just made, are guaranteed to be in place, so we can simply add our code here:

- (void)		windowControllerDidLoadNib:(NSWindowController *) aController {   [super windowControllerDidLoadNib:aController]; [_mainView setDelegate:self]; }

From now on, whenever the view needs anything from the document, it calls its delegate method to obtain the document, and the informal protocol to obtain what it wants. The view doesn't otherwise know anything about the document, and vice versa - overcoupling has been avoided. Note that the view could be used in another application that used an entirely different object but implementing the same informal protocol to get its work done. This is one way that you can design classes for reuse among a variety of projects.

Rendering the document contents
The first job our view has to do is to render the contents of the document. Here's the code:

- (void)	drawRect:(NSRect) rect {	NSColor whiteColor] set];	NSRectFill( rect );	NSArray*		drawList = [[self delegate] objects];	NSArray*		selection = [[self delegate] selection];	NSEnumerator*	iter = [drawList objectEnumerator];	WKDShape*		shape;	while( shape = [iter nextObject])	{		if ( NSIntersectsRect( rect, [shape drawBounds]))			[shape drawWithSelection:[selection containsObject:shape;	} }

As before, we start by erasing the part of the background that is to be redrawn. Next we ask our delegate for the objects and the selection using the informal protocol we designed earlier. Then it's simply a case of iterating over the objects and drawing each one. For efficiency, we also check whether the refresh rect intersects the bounds of the object, and if not we don't bother - this just avoids drawing anything that isn't visible or affected by the refresh, speeding up drawing. The selection state is determined simply by seeing if the object is in the selection array.

Sharp-eyed readers will notice that a method called drawBounds is being called on the shape object. We have not so far discussed this, but we need to. If we were to use the shape's bounds, we'd find that small parts of the things it draws will not always get erased when they need to, because some things are actually drawn outside of the area defined by bounds. The selection handles for example, are centred ON the bounds, so at least half of the handle is outside the bounds. Likewise, when we stroke the shape, the bounds defines the centre of the stroked lines. We need to take these into account so that we can correctly refresh the entire area that is drawn. the method drawBounds returns a rectangle that is based on bounds, but slightly expanded from it:

- (NSRect)		drawBounds {	return NSInsetRect([self bounds], -kHandleSize - [self strokeWidth], -kHandleSize - [self strokeWidth]); }

This takes into account both the size of the handles and the stroke width so that everything is guaranteed to fall within this area. Any drawing that simply repaints or refreshes an object will use drawBounds rather than bounds, but it must be remembered that bounds is the strictly mathematical boundary of the shape.

Handling the mouse
That's all there is to drawing. The case of handling the mouse is more involved. Let's look at that now.

Three methods are involved — mouseDown, mouseDragged and mouseUp. Each is passed an NSEvent object, representing the mouse event that triggered the call. The first thing we need to do is to adjust the mouse coordinates from the window to our local view. NSEvent always stores mouse coordinates relative to the window, and this information is extracted using the locationInWindow method. NSView's convertPoint:fromView: will translate the coordinates, and a fromView: of nil means from the window itself.

In mouseDown:, once we are in local coordinates, we can query the document (the delegate) for the object under the mouse, using the method we wrote earlier. This either returns the topmost object hit, or nil meaning nothing was hit. We store the result in a data member called _dragShape. This keeps track of which shape we hit when later the mouseDragged and mouseUp methods are called.

If we hit empty space, we will (for now) use this to mean create a new object according to the current tool. Later we'll refine this so that we implement the ability to select objects by dragging a marquee (selection box) over them. So let's see what happens when we create a new shape object. First we call another method, shapeForCurrentTool, to make us the appropriate shape. This is a factory method so the returned object will be autoreleased. We immediately add the new object to the document, and set its location to be where our mouse point is. If we had hit an existing object, this step of creating the object and adding it to the document is skipped.

Next we test whether the shift key is down. If it is, we want to shift-select the item, which involves not deselecting everything first. Then we select the object we hit. Again, we use the shift key to add another refinement — if shift is down, we toggle the selection state of an object, otherwise we simply select it and deselect everything else. Finally, we pass the click on to the object's own mouseDown method, which we've already looked at. Because we have changed the selection state of possibly any number of objects which could be anywhere in the drawing, we flag the need to refresh the entire thing to make sure that all these changes have an immediate effect on screen. Note that this approach is simple, but not necessarily optimum. Once the drawing gets complex, refreshing the entire thing every time can become very expensive and slow. Instead it can pay to track which objects are actually changed, and only refresh those areas. However for this simple tutorial, we won't attempt to complicate things by doing that.

mouseDragged: converts the coordinates as before, and passes the drag down to the current object as indicated by _dragShape, which we set up in mouseDown: Because a drag can move and resize an object, we need to refresh the area affected by both the new position of the object, and its old position. Thus we record the state of its bounds before and after the drag, combine them together and refresh that area. If we didn't do this, we'd find that objects would leave ugly trails behind them as they were dragged.

mouseDragged: doesn't currently attempt to deal with the situation where more than one object can be dragged. In most apps of this type, if there are two objects selected and one is dragged, both are dragged together. As an exercise, you might want to think about how mouseDragged: can be modified to achieve this.

Finally, mouseUp: cleans up by passing the call to the dragged object, refreshing the screen at that place, and then setting _dragShape to nil.

Handling the tools
As we saw, the shape to be added to the drawing is dependent on shapeForCurrentTool. This simply needs to look at which tool we have selected, and create the right sort of object. However, we don't yet have an interface for selecting a tool, so let's make one so that we can implement this. Since the tool palette is common to all documents in the application, we'll add it to MainMenu.nib.

One of the key design paradigms of Cocoa is its adherence to the model-view-controller (MVC) approach to object-oriented design. So far we haven't really encountered this directly, but we will have to say something about it now. MVC is a good method of separating functionality between the model, which manages the actual meaningful data your application handles, and the view, which is the visual representation onscreen of that data, and the controls that manipulate it. Sitting between them is the controller which does the job of mapping the view to the data and vice versa. So far we have used MyDocument as a combined model and controller, but with the tool palette, we'll be using a more strictly delineated MVC approach.

The tool palette itself will be the view. We'll need a controller to handle the selection of the tool button in the palette, and pass this along in the form of the selected tool. The model will be just a single variable, storing the current tool choice.

The view can be entirely created in Interface Builder. The controller can be partially set up in Interface Builder, supplemented by a little code we'll need to write. First let's look at our "model". Because our WKDDrawView is going to take on the task of creating objects in response to the chosen tool, the obvious approach would be for the view to simply ask the tool controller what the current tool is whenever it needs to know. However, there is a snag. There is no easy way for the view (which is only one among many possible, given that we can have many documents open) to locate the tool controller without having an explicit reference to it. One solution would be a global variable which referred to the tool controller, and that is a viable approach. However, globals are discouraged in an object-oriented application, and besides, simply setting up this global would complicate things in a way that we'd rather avoid in this tutorial. Instead, we can declare a static variable in our view class which will store the ID of the chosen tool. Being a static variable, it can be accessed by any view (static variables are like globals that are only visible to code in the same file that they are declared in). This variable thus contains a COPY of the tool ID that the tool controller has. Normally we'd avoid this because copies of the same data need careful management to ensure they don't get out of step, but in this case it's fairly simple.

So how do we synchronise the tool ID when the user clicks the palette button? The answer is using a notification. A notification is Cocoa's way of passing information around without having explicit object references. The controller will post a notification every time the tool changes. Anyone interested in this can subscribe to the message and respond in a suitable way. In this case each view will subscribe to the message, but set the static variable in response. There is some overkill here - if there are two views, each will receive the message and the variable will get set twice to the same value. In this case we can tolerate this because it's only a single simple integer we are talking about.

What about when a new view is created, but the tool is set to some odd value? The view will still know about the tool, because it is a static variable, shared among all instances of the view. Thus we don't have to worry about the case of a new view never receiving a notification before it can use the correct tool, as we would if the variable was local to each view.

OK, let's make it happen.

Open 'MainMenu.nib' in IB. Select the Windows panel in the widgets palette (fourth button from the left) and drag a PANEL into the main window. Double-click the new icon to open the panel. In the Inspector, delete the window's title. Use the Size panel to set min w. to 60, and min h. to 100. Resize the panel to be narrow and tall, and position it at top left of the screen. Use the Attributes inspector to check 'Utility Window (panel only)' and 'Non activating panel (panel only)'. Make sure the 'Minimize' and 'Zoom' boxes are unchecked.

Switch to the Controls palette and drag a large square button to the window. Press the option key and drag the selection handle at the bottom downwards until you have four vertically organised buttons. Option-dragging a button creates what is known as a MATRIX of buttons, rather than a single button. With the matrix selected, use the attributes inspector to set the behaviour to 'Radio'.

In new versions of the interface builder you create the list of buttons option-dragging the buttons. Then select all of the buttons and use Layout->Embed Objects in->Matrix. This will create a Matrix encompassing all the buttons.

Double-click each button in turn and set the behaviour of each one to On/Off. Notice as you go through that the 'tag' field gives a different value for each button. This tag value is what will become our tool ID. Once done, we'll have a crude but functional tool palette interface. You can test its operation in IB by choosing File->Test Interface. Verify that the tool palette operates as a set of radio buttons with only one selected at a time, and that you can tell from the highlighting which one it is. To return to IB, choose 'Quit'.

Now we need a controller for the tool palette. IB again helps us by actually making this object for us, and creating skeletal .h and .m files that we can add code to later.

In the 'Classes' tab of the main window, locate the NSWindowController class (it is a subclass of NSResponder). Highlight NSWindowController and choose Classes->Subclass NSWindowController. A new name is added; name it ToolsController. Now we can ask IB to actually create this object, giving us an actual instance we can hook up to the view. To do this, highlight the new class, and choose Classes->Instantiate ToolsController. A new object is added to the main window, which looks like a box and has the name ToolsController. Go back to the 'Classes' view (a quick way is to double-click the new object). In the Inspector, use the Actions panel to add a new action: click Add, then type the action name selectionDidChange: (remember the colon!). This makes two actions, there should already be a showWindow: action.

Go back to Instances. control-drag FROM the ToolsController object TO the tools window (you can drag to the title bar of the actual window, or just to the icon in the main window). Connect this to the 'window' outlet. Next, control-drag FROM the NSMatrix (the four buttons - make sure you have the four buttons selected as a set not just one of them - the easiest way is to start the drag from a blank area just off the edge of one of the buttons) TO the ToolsController. Connect that to the action you just added, 'selectionDidChange:'.

Finally, we need a way to show the palette when we need it. Drag a new menu item to the Windows menu, placing it below Zoom item. Name it 'Show Tools'. control-drag FROM the menu item to the ToolsController. Connect it to the action 'showWindow:'

Save the file. With ToolsController selected, choose Classes->Create Files for ToolsController. Add the .h and .m files to Wikidraw. We're done in IB, so return to Xcode.

Coding the controller
Add an int data member to our ToolsController class, called _curTool. Add a method to return it, called currentTool. Your class definition should look like this:

@interface ToolsController : NSWindowController {	int		_curTool; }

- (IBAction)	selectionDidChange:(id) sender; - (int)			currentTool;

@end

extern NSString* notifyToolSelectionDidChange;

We also declare an extern string variable that will contain the name for our notification.

Now switch to the implementation. We don't have an 'init' method here, since the one we inherit from NSWindowController is fine, but we do need to do a little bit of set up after we've been created from the .nib file. We can do this by implementing an 'awakeFromNib' method. It's just a method (with no parameters) called when we are brought to life from the .nib file. Here's what we need:

- (void)	awakeFromNib {	[(NSPanel*)[self window] setFloatingPanel:YES]; [(NSPanel*)[self window] setBecomesKeyOnlyIfNeeded:YES]; _curTool = 0; }

We need to tell our window to be a floating panel, and not to become key unless absolutely necessary. This is to prevent clicks in the tool palette taking focus away from the current document, which would be rather odd behaviour. We also take the opportunity to initialise _curTool to 0.

The main method of our controller is the action selectionDidChange;. Here it is.

- (IBAction)	selectionDidChange:(id) sender {	_curTool = [[sender selectedCell] tag];	[[NSNotificationCenter defaultCenter] postNotificationName:notifyToolSelectionDidChange object:self]; }

You'll recall that the sender of an action was an NSMatrix. The matrix will track the currently selected item and return it when we call selectedCell. Then we need to obtain the tag value associated with that item, so we call 'tag'. Then it's just assigned to _curTool.

The interesting thing comes next. We post a notification which tells any interested object that the tool selection was just changed by the user. We use something called the 'default notification center' to actually transmit this message. This is a global Cocoa object that exists for this purpose - to forward messages on to anyone who is interested. Because it's visible to all classes (in a way that our ToolsController isn't), it's an ideal way to pass information to objects we don't know about. The postNotificationName:object: method does the job. The notification name is just a string that we define which uniquely indicates what's going on, and object is the controller itself. The name is set up as a string:

NSString* notifyToolSelectionDidChange = @"toolSelectionChanged";

The string itself is really irrelevant, though it can be useful to give it a descriptive name because you later might want to log what notifications you receive somewhere as part of debugging - if the names are meaningful you'll be easily able to tell where it came from.

That's our controller in its entirety - all it does is to receive the change of the buttons and forward that as a notification.

Receiving the notification
Back in WKDDrawView.h, we need to declare a method to handle reception of notifications. We'll call it toolChange:, and it looks like this:

- (void)		toolChange:(NSNotification*) note;

At the top of the.m file, outside the implementation, add:

static int	sCurrentTool = 0;

This will be our local copy of the current tool ID. We declare it as static so that it's visible to any instance of our view class. Next we need to register for the notification. We do this as part of our init method:

- (id)		initWithFrame:(NSRect)frameRect {	if ((self = [super initWithFrame:frameRect]) != nil) {		_delegate = nil; [[NSNotificationCenter defaultCenter] addObserver:self											 selector:@selector(toolChange:)											  name:notifyToolSelectionDidChange											  object:nil];	}	return self; }

- (void)		dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self];	[super dealloc]; }

We use addObserver:selector:name:object: to register an interest in this notification. When deallocation the object we have to deregister to avoid an exception for any notification sent after the deallocation. When a notification matching the name is sent, the nominated method whose selector we passed is called:

- (void)		toolChange:(NSNotification*) note {	sCurrentTool = [[note object] currentTool]; }

All we do is ask the notification which object sent the message ([note object]), and since we know that it is a ToolsController, we can then simply ask it for the tool ID, which we store in sCurrentTool. Thus sCurrentTool should always match the current value of the actual selected tool. Should the view need to know at any time which tool is selected, it can simply look at sCurrentTool rather than trying to figure out where the ToolsController is and asking it.

Of course, for this to compile we need to #import the ToolsController.h file into WKFDDrawView.m:

 * 1) import "ToolsController.h"

Before we continue, let's build and go and check that we are in business as far as it goes. You should be able to choose "Show Tools" from the Window menu to make the palette visible, and clicking on the buttons should select them. We can't tell yet whether it's doing anything useful, as we haven't written code that acts on the value of the chosen tool. We can however, log the reception of the notification. Logging this sort of thing is always handy when developing and.or debugging an application.

To log anything, use the NSLog function. This accepts a NSString constant which will be written out to the log. The log itself is visible as the main window in Xcode when the application is running. The string passed to NSLog accepts printf-like formatting instructions, so let's add:

NSLog(@"tool changed to %d", sCurrentTool);

To the end of the toolChange: method. Build and Go again, and verify that clicking the tool buttons causes the tool ID to be written to the log.

Previous Page: Implementing Wikidraw | Next Page: More shapes


text:
Diplomacy

This book is a guide to the game of Diplomacy, a turn-based, strategy wargame played by seven people, each taking the role of a Great Power in Europe during the World War I era.

Contents

 * 1) Basics
 * 2) /Introduction/
 * 3) /Rules/
 * 4) /Geography/
 * 5) Countries
 * 6) /Austria/
 * 7) /England/
 * 8) /France/
 * 9) /Germany/
 * 10) /Italy/
 * 11) /Russia/
 * 12) /Turkey/
 * 13) Strategies
 * 14) /Opening Principles/
 * 15) /Middle Game Strategies/
 * 16) /End Game Strategies/
 * 17) /Alliances/
 * 18) /Stabs/
 * 19) /Negotiation/
 * 20) /General Tactics/
 * 21) /Sample Games/
 * 22) Variants
 * 23) /Variants/
 * 24) References
 * 25) /Links/
 * 26) /Books/
 * 27) /Software/

title: Diplomacy


text:
Diplomacy/Turkey

Turkey/Ottoman Empire/دولتِ عَليه عُثمانيه (Ottoman Turkish)

 * Leader in 1901: Sultan Mehmed V;
 * Head of Government: Grand Vizier Mehmed Said Pasha;

Color: Yellow

Neighbors: Russia, Austria, and Italy

Home supply centers: Constantinople, Ankara, Smyrna

Turkey has obvious natural strength—any beginner will be able to point out Turkey's strong defensive position, both from land and sea. Turkey has the greatest staying power of any on the board. Turkey can stalemate her enemies with six well-placed fleets and armies. Turkey's natural defensive strength, however, is the cause of its offensive weakness. And Turkey's defensive position is not as powerful as it initially appears. Russia has a fighting chance to wrest BLA from Turkey in the early years, and an alliance between Italy and Austria can land an Italian Army in SMY by Autumn 1902. Either of these cases mean curtains for Turkey. Because of its defensive position, Turkey is nicknamed "The Sick Man of Europe" for many. However, using this "weakness", and given some good fighting, Turkey can be the best man in Europe.

18 Center Goal
3 home supply centers, The Balkans (4), Austria (3), Russia except St. Petersburg (3), Italy (3), and Tunis makes 17. If Turkey has built many fleets, usually as part of an alliance with Russia, the 18th center can come from the Western half of the Mediterranean—MAR and SPA are tough, but possible. Otherwise, Turkey must mass armies and cross the stalemate line to capture MUN. BER and STP are too easily held from the north to be included, though if even a single Turkish army breaks into Scandinavia, victory can almost be guaranteed.

Opening Strategy
Turkey has by far the smallest opening book out of any Great Power. The army in CON always goes to Turkey's one natural SC, and the fleet in ANK almost always heads to BLA, whether Russia has agreed upon a bounce or not. Turkey fundamentally has two choices: attack Russia immediately with A SMY - ARM, or attempt to gain two builds with A SMY - CON.

A SMY - ARM: The Russian Attack
Perhaps Turkey's best shot at winning, this opening aims at Turkey's most dangerous enemy—Russia. Unfortunately, Russia is Turkey's only possibility for a long-term ally, so the attack on Russia can leave Turkey friendless and surrounded in the East. The opening is quite deadly when Turkey takes the Black Sea, as an opportunistic Austria may be willing to offer Turkey assistance for a share of the Russian spoils. Without a fleet in BLA, the opening loses a good portion of its teeth, as gaining SEV is going to be impossible.

A SMY - CON: The Bulgarian Push
Turkey's best shot at two builds in 1901 is a push through BUL, followed by either a move to GRE or SER, or RUM with Austrian support. Although Turkey rarely gets two builds—most Austrians move to ALB and SER in S 1901, and thus can pick up both SCs, and are wary of giving support to Turkey, Austria's natural enemy, the opening is safe and allows Turkey to work with or against any of his neighbors.

Unorthodox Openings
Several unusual openings have also sprung up. These are not as forceful as the main lines, and therefore require more diplomacy, more trust, better intelligence gathering, and stronger tactical skill.

A SMY H with A SMY - SYR: The Syrian Sidestep
Spring 1901: A SMY H, A CON - BUL, F ANK - BLA. Fall 1901: A SMY - SYR, F ANK - CON, A BUL H (or other). Very rarely played, this opening has decent surprise value (first played in game 1977KJ, first discussed in Diplomacy World #47). Turkey delays choosing Austria or Russia as his ally and leaves both coasts open for a build. Convincing Russia to stay out of BLA in Fall 1901 by suggesting or demanding F SEV - RUM or F SEV S A UKR - RUM is a major diplomatic victory. If instead F SEV - BLA, a Russian unit (new or existing) in SEV virtually guarantees war and requires Turkey to build F ANK.

A SYR can be useful in several ways:
 * 1. Moving to ARM against Russia.
 * 2. Convoying to GRE against Austria.
 * 3. Discouraging Italy from playing a Lepanto.

This opening fails in the face of a determined Austro-Russian alliance, and should not be used if this alliance is expected. The Bulgarian Push offers a safer and more direct counter.

F ANK - ARM, A SMY - CON: Ship of the Desert
Invented and first discussed by Richard Sharp, this opening can be played as an attack on Russia, or to disguise a Turk-Russian alliance. These approaches require essentially the same diplomacy:

 * 1. Come to an arrangement with Russia over BLA.  Tell Russia that you want to bounce in BLA if you plan to attack him.  If Russia anticipates a bounce, he will be much less likely to play A MOS - SEV, which would spell curtains for you as A MOS - SEV is often part of an Austro-Russian anti-Turk alliance.  If you are working with Russia, tell him you want to shepherd his fleet through CON and into the Med (ordering it as you tell him, in return for doing the same for him with your A BUL).  You will temporarily trade CON for SEV, simulating a Turk-Russian war and taking everyone by surprise in 1902.
 * 2. Tell Austria you want to partner with him against Russia.  Let him suggest who gets what Balkan territories, but demand GRE and justify yourself by saying you will need GRE as a springboard to force Italy out of ION once Russia is gone.  If he refuses, then demand he support you into RUM in F1901.  The idea is to get Austria firmly on your side if you plan to attack Russia, and to lull him into a false sense of security if you are working with Russia.
 * 3. Tell Russia, Austria, and Italy (contact them all separately) that it will take them a long time to eliminate you if they are planning to attack, and that in the meantime they'll be open to a stab by anyone else.

You want the following in the first couple of years:

Anti-Russian
S1901
 * Turkey: F ANK - ARM, A CON - BUL, A SMY - CON
 * Russia: F SEV - BLA, A MOS - UKR, A WAR - GAL
 * Austria: A VIE - GAL, A BUD - SER, F TRI H or F TRI - VEN (but expect F TRI - ALB)
 * Italy: anything but a Lepanto.
 * England, France, and Germany: infighting, but preferably EG vs. F

F1901
 * Turkey: F ARM - ANK, A CON - ANK, A BUL - RUM
 * Russia: F BLA - RUM, A UKR - SEV, A WAR - GAL (altough A WAR - UKR is more likely)
 * Austria (assuming F TRI - ALB in S1901): A SER S A BUL - RUM, F TRI - GRE, A VIE - GAL (although - BUD or - TRI is also likely)
 * Italy, England, France, and Germany: same as before

W1901: Hope like mad Russia didn't play F BLA S F ARM - ANK. Build F ANK (or F CON if he played F BLA S A CON - ANK).

Russian Partnership
S1901 - Same moves as Anti-Russian.

F1901
 * Turkey: F ARM - SEV, A BUL - GRE, A CON - ANK
 * Russia: F BLA - CON, A UKR - RUM, A WAR - GAL
 * Austria: A VIE - GAL, A SER S F ALB - GRE, F ALB - GRE
 * Italy, England, France, and Germany: same as before.

W1901: Build F SMY, and sail into the Med. with Russia.

This opening has the advantage of allowing Turkey to justify keeping a fleet in BLA for at least a few turns. When SEV and CON are swapped back (presumably in F1902), Turkey can argue that moving to BLA is the fastest way to get to the Med., and the best way to protect both BUL and RUM. This allows Turkey a fair amount of protection against a delayed Russian stab. Also, if you can lobby hard enough, Turkey would ideally like to see:

F1901
 * Turkey: F ARM - SEV, A BUL - RUM, A CON - BUL
 * Russia: A WAR - GAL, A UKR S A WAR - GAL, F BLA - CON

This can be justified by claiming Turkey needs to build an additional unit in ANK in W1901 to quickly secure ION. Russia can always capture RUM in 1902 or 1903, by which time Turkey should have captured GRE, and probably SER too. Meanwhile, securing GAL is a strong medium-term strategic blow to Austria, who soon be surrounded by units in GAL, RUM, BUL, and GRE. GAL is then a stepping-off point for Russia's attack on Austria. A direct Turk will tell Russia he knows Russia often gets the upper hand in a RT, and that Turkey needs to expand as quickly as possible (i.e., as fast or a little faster than Russia) in the beginning to compensate for the difficulty he will have in capturing additional centers later.

Black Sea Strategy
The Black Sea is an important sea space for Turkey—a Turkey that loses the Black Sea is usually gone within a year or two. However, many Russias (both friendly and enemy) will demand that Turkey not enter BLA in S1901 as part of an alliance. It is much safer for Turkey to arrange a bounce, but most Russias consider it advantageous to grab RUM as quickly and as surely as possible, which is facilitated by the lack of a Turkish fleet in BLA.

Sleeping Giant Strategy
Turkey is naturally inhibited by the games geography. This requires them to play passively for the first two to four years. The best plan is either to negotiate for Greece or Romania, and simply wait. Then, while Austria is off fighting other enemies such as Italy, Germany, or Russia. Sweep in and take all their stars.

title: Diplomacy/Turkey


text:
Diplomacy/Introduction

History
The game of Diplomacy was developed by Allan B. Calhamer while he was a student at Harvard Law School during the 1950's. While only a minor commercial success, Diplomacy has a small, devoted fanbase. Many famous politicians have stated that Diplomacy was their favorite game. John F. Kennedy and Henry Kissinger were both fans of the game, as was news anchor Walter Cronkite.

The major difficulty for playing the game is finding the seven players required. The game also takes a very long time to complete—usually three to six hours, or even longer if time for diplomacy is not capped. Although face-to-face diplomacy provides a more nuanced and exciting game, as players can actually see the discussions between two powers, if not overhear them, the difficulty of finding seven players in one's area has made mail, and now e-mail, the two most popular ways to play Diplomacy.

There has been a push in the past decade for better Artificial Intelligence for the game. Computers continue to improve, and have even become pretty good at no-press variants of the game, but cannot yet compete with humans in games with communication.

Overview
Diplomacy is a zero-sum, turn-based, strategy wargame played by seven people, each taking the role of a Great Power in Europe during the World War I era. The seven countries are ../Austria/ (or Austria-Hungary), ../England/, ../France/, ../Germany/, ../Italy/, ../Russia/, and ../Turkey/. The game year is divided into five phases: Spring movements and retreats, Fall movements and retreats, and Winter adjustments. Each power controls only two types of units: armies and fleets. Fleets can occupy ocean spaces and coastal provinces, and can convoy armies across ocean spaces. The board is divided into 75 spaces, 56 land and 19 sea. 34 land spaces are called supply centers. Powers capture supply centers by having their troops occupy the territory after fall retreats. Powers maintain ownership over every province they have conquered until another power is able to move one of his or her own units into the province during the fall (although keeping track of the ownership of non-supply center provinces is essentially irrelevant). Owning more supply centers than units after the fall retreats allows a player to build a unit in one of his or her unoccupied home centers during the winter adjustments. A player wins by taking 18 supply centers. Achieving solo victory is extremely difficult, and most games end in draws ranging in size from two to four players.

Diplomacy is played on the board showing the political map of Europe immediately prior to World War I. Diplomacy has a very subtle and complex geography, with criss-crossing stalemate lines from east to west, north to south, and in corners. Geography is an important factor in determining a country's winning strategy—Turkey will never view England as an objective for conquest except in extraordinary circumstances. More important, however, is diplomacy. A loyal ally will work around geographical conflicts to find a mutually satisfactory solution.

title: Diplomacy/Introduction


text:
Applied Ecology

Main Contents

 * /Introduction/
 * /Conservation Management/
 * /Habitat Creation/
 * /Agro-Ecological Systems/
 * /Wetland Engineering/
 * /Disease Transmission/
 * /Breeding and Reintroduction of Rare Species/
 * /Environmental Valuation/
 * /Nature Tourism/
 * /The Endangered Resources/
 * /New Societies and Cultures/
 * /Case Studies/
 * Asian Rainforest Politics
 * British Limestone Grasslands

title: Applied Ecology


text: User talk:202.124.195.2

title: User talk:202.124.195.2


text:
User talk:220.239.59.41

Please stop adding nonsense to Wikibooks. It is considered vandalism. If you would like to experiment, use the sandbox. Thank you.

title: User talk:220.239.59.41


text:
Programming Mac OS X with Cocoa for Beginners/More shapes

Previous Page: Wikidraw's view class | Next Page: An Inspector calls

Before we can act upon the different tools, we'll need the classes that implement the different shapes. We have four tool buttons at the moment, so it would be nice to have one being a selection tool, and three being shape drawing tools. We already have the rectangle shape so we need two more. We'll create an oval shape and a slightly more complex polygonal shape. You can go on to create any number of additional shapes if you want, and just extend the tool palette to access them.

Select WKDShape.m and choose File->New File... Choose Objective-C class, and set its name to "WKDOvalShape.m". By the way the only reason to select the WKDShape file before you do this is to make sure the new files go in the same group - unfortunately Xcode doesn't automatically subclass existing classes, so we'll have to do that by hand.

Open the WKDOvalShape.h file and add #import "WKDShape.h" to the top of the file. Then change NSObject to WKDShape. Your class definition looks like this:

 * 1) import
 * 2) import "WKDShape.h"

@interface WKDOvalShape : WKDShape { }

@end

The only method to be implemented is 'path', and we don't need to declare it. Just add it to the implementation, like this:

@implementation WKDOvalShape

- (NSBezierPath*)	path {	return [NSBezierPath bezierPathWithOvalInRect:[self bounds]]; }

@end

Now repeat the whole exercise again, this time making files for a class called 'WKDPolyShape'. Here's the implementation we need:

@implementation WKDPolyShape

- (NSBezierPath*)	path {	NSBezierPath* path = [NSBezierPath bezierPath]; NSRect	br = [self bounds]; NSPoint p;	[path moveToPoint:br.origin]; p.x = NSMidX( br ); p.y = NSMidY( br ); [path lineToPoint:p]; p.x = NSMaxX( br ); p.y = NSMinY( br ); [path lineToPoint:p]; p.x = NSMaxX( br ); p.y = NSMaxY( br ); [path lineToPoint:p];

p.x = NSMidX( br ); p.y = NSMidY( br ); [path lineToPoint:p]; p.x = NSMinX( br ); p.y = NSMaxY( br ); [path lineToPoint:p]; [path closePath]; return path; } @end

Now return to WKDDrawView.m. Add imports for both of these new files to the top of the file. Add the following to the method 'shapeForCurrentTool'.

- (WKDShape*)	shapeForCurrentTool {	switch([self currentTool]) {		default: return nil; case 1: return [[[WKDShape alloc] init] autorelease];		case 2:			return [[[WKDOvalShape alloc] init] autorelease];		case 3:			return [[[WKDPolyShape alloc] init] autorelease];	} }

- (int)			currentTool {	return sCurrentTool; }

It's simple - depending on the current tool, we make one of the three different kinds of shapes. We return 'nil' as the default case, which we'll use to mean the selection tool. Build and Go and check that three kinds of shapes can be drawn. The tool with ID 0 currently will log an error because we haven't adjusted our mouseDown: method to handle the nil case as a selection yet. Let's fix that next.

Selection marquee
The idea of a selection marquee is very simple - the user clicks in the view with the selection tool, and drags a rectangular box over a set of objects. Any objects that the box touches are selected, and any others outside the box are deselected.

To make this work, we'll need a way to tell which objects are touched by the marquee and which are not. This is straightforward, as the utility function NSIntersectsRect, which we've already seen, will figure it out. We just need to iterate over our objects, retrieve their bounds (or more accurately, the drawBounds) and test for an intersection with the marquee. If it intersects, it's selected, if not, it isn't.

To draw the marquee itself, we'll need to track its original anchor point, and use this to form a rectangle. All of the required code goes in the view class. We also add a pair of NSPoint data members, _anchor and _marquee, to track the current size of the marquee.

- (void)		updateMarquee:(NSPoint) newPoint {	_marquee = newPoint; NSRect mr = NSRectFromTwoPoints( _anchor, _marquee ); NSEnumerator* iter = [[[self delegate] objects] objectEnumerator];	WKDShape* obj;	NSRect		br;	[[self delegate] deselectAll];	while ( obj = [iter nextObject])	{		br = [obj drawBounds];		if ( NSIntersectsRect( mr, br ))			[[self delegate] selectObject:obj];	}       [self setNeedsDisplayInRect:mr]; }

- (void)		drawMarquee {	NSRect mr = NSInsetRect( NSRectFromTwoPoints( _anchor, _marquee ), 1, 1 ); [[NSColor grayColor] set];	NSFrameRect( mr ); }

updateMarquee: does most of the work. It sets the _marquee data member, and then uses this and _anchor to calculate the rectangle of the marquee. Then it deselects everything, and iterates over the objects, testing each one's drawBounds against the marquee rect. If they intersect, the object is selected.

drawMarquee also calculates the marquee rectangle, and frames it in grey. Both of these methods call the utility NSRectFromTwoPoints. In fact Cocoa doesn't provide this function, so instead we define it ourselves. Note that this is a plain C function, outside of the class implementation. It should be placed above the @implementation declaration in WKDDrawView.m.

NSRect	NSRectFromTwoPoints( NSPoint a, NSPoint b ) {	NSRect r;	r.origin.x = MIN( a.x, b.x ); r.origin.y = MIN( a.y, b.y ); r.size.width = ABS( a.x - b.x ); r.size.height = ABS( a.y - b.y ); return r; }

Now we need to modify our mousing methods to handle the marquee. All three methods are affected, but mouseDown: is the most complex. The logic of the method needs to be changed so that a more consistent behaviour is implemented, now we have a working tool palette. Rather than immediately locate the object under the mouse, we need to determine the current tool first - we only need to look for an object under the mouse if the selection tool is active. Otherwise, we are creating a new object. Thus the order of the logic at the top of the method needs to be altered. Here's the modified method:

- (void)	mouseDown:(NSEvent*) evt {	NSPoint pt = [self convertPoint:[evt locationInWindow] fromView:nil]; _dragShape = [self shapeForCurrentTool]; if (_dragShape == nil) {		_dragShape = [[self delegate] objectUnderMousePoint:pt];		if ( _dragShape == nil )		{			// marquee drag			_anchor = pt;			[self updateMarquee:pt];			return;		}	}	else	{		[[self delegate] addObject:_dragShape];		[_dragShape setLocation:pt];	}	if (([evt modifierFlags] & NSShiftKeyMask) == 0)	{		[[self delegate] deselectAll];		[[self delegate] selectObject:_dragShape];	}	else	{		if ([[[self delegate] selection] containsObject:_dragShape])		{			[[self delegate] deselectObject:_dragShape];			_dragShape = nil;		}		else			[[self delegate] selectObject:_dragShape];	}	[self setNeedsDisplay:YES];	[_dragShape mouseDown:pt]; }

- (void)	mouseDragged:(NSEvent*) evt {	NSPoint pt = [self convertPoint:[evt locationInWindow] fromView:nil]; NSRect update = [_dragShape drawBounds]; if ( _dragShape ) [_dragShape mouseDragged:pt]; else [self updateMarquee:pt]; [self setNeedsDisplayInRect:NSUnionRect([_dragShape drawBounds], update)]; }

- (void)	mouseUp:(NSEvent*) evt {	NSPoint pt = [self convertPoint:[evt locationInWindow] fromView:nil]; if ( _dragShape ) [_dragShape mouseUp:pt]; else {		[self setNeedsDisplayInRect:NSRectFromTwoPoints( _anchor, _marquee )]; _anchor = _marquee = NSZeroPoint; }		[self setNeedsDisplayInRect:[_dragShape drawBounds]]; _dragShape = nil; }

If the selection tool is current, we will have a nil dragShape, so we use that to subsequently detect the marquee case by testing if the mouse hit empty space or an existing object. For mouseDown:, we set the value of _anchor, then call for an update of the marquee. On mouseDragged:, we detect the marquee case and update it again, changing the selection as needed. On mouseUp:, we refresh the marquee area and set the marquee points to zero. This will erase the marquee, leaving anything it selected still selected. The only remaining task is to make the marquee visible. To do this, we simply add a call to drawMarquee to our drawRect: method.

- (void)	drawRect:(NSRect) rect {	NSColor whiteColor] set];	NSRectFill( rect );	NSArray*		drawList = [[self delegate] objects];	NSArray*		selection = [[self delegate] selection];	NSEnumerator*	iter = [drawList objectEnumerator];	WKDShape*		shape;	while( shape = [iter nextObject])	{		if ( NSIntersectsRect( rect, [shape drawBounds]))			[shape drawWithSelection:[selection containsObject:shape;	}	[self drawMarquee]; }

Now we'll find that when we compile and run the application, we have a much more standard behaviour for the selection tool.

There are some fairly serious inefficiencies here - every time the marquee size changes, the selection is recalculated entirely from scratch. For a few objects this isn't a problem, but if our drawing became complex it could well be. Once we add the inspector which will respond to selection changes, performance could suffer even more. We won't be worrying about this now, but perhaps you could think of a few simple ways to improve the efficiency?

Previous Page: Wikidraw's view class | Next Page: An Inspector calls


text:
User:Stennie

Stennie is a Perl developer living in Sydney, Australia. You might recognise him from such wikis as perl.net.au.

title: User:Stennie