\subsubsection{Idea}

The goal is to implement a screen grab utility for X11 using GNUstep. This
application, or tool rather, should collect data for all windows that are
currently visible, be it partially or complete, into a tree that represents
the parent-child relationship between them. The most important item per window
is the image that it displays. Trees map naturally onto browsers, so we will
let the user navigate the window tree with an {\tt NSBrowser.} The application
has one window, whose upper portion displays the browser. The lower portion
contains a scrollview, whose document view is a special imageview that shows
the image in the window at the time the snapshot was taken (just after
launch). We read image data only for the onscreen rectangle of the window. The
user can save images to TIFF files. There are two phases to this
application. First it queries the X server for the contents of the onscreen
windows and builds a window tree; the user views and perhaps saves window
contents of the retrieved windows during the second phase.

We'll be working with three classes. The class {\tt XWinData} encapsulates the
attributes for an X window and an {\tt NSImage}, which holds the current image
obtained for the window. The class {\tt ImageView} is a simple replacement of
{\tt NSImageView} and it provides for fast display of an image that is
represented by a bitmap and it will be the documentview of the scrollview. The
third class is the class {\tt Controller}, which collects the data from the X
server, builds the tree of {\tt XWinData} objects starting with the root
window and acts as a passive delegate for the browser. It implements a method
to save images.

\subsubsection{{\tt XWinData}}

The program starts with the necessary headers, notably the one for {\tt
Xlib}.

\begin{lstlisting}{}
#include <Foundation/Foundation.h>
#include <AppKit/AppKit.h>
#include <X11/Xlib.h>
\end{lstlisting}

The class {\tt XWinData} encapsulates data that describe an X window as well
as the image contained in the window. An X window is on a certain display and
has a parent. We record these for easy reference. We also record the
attributes of the window. We'll be using the location and the size attributes,
as well as the map state of the window (whether it is viewable or not). The
instance variable {\tt children} makes {\tt XWinData} into a tree structure,
with the individual objects being the nodes. We need the tree data to make the
set of windows easy to navigate and provide data for the browser.

On of the first things that we'll do is compute the portion of the window that
is actually on the screen, in screen coordinates. We only process windows of
which at least some part is on the screen. An {\tt XWinData} object also holds
a description of itself, which is the name of the window if it has one, and
the geometry otherwise. Finally we have the image, which is an {\tt NSImage}
object that holds the image that we retrieved for the window.

\begin{lstlisting}{}
@interface XWinData : NSObject
{
    Display *display;

    Window window, parent;
    XWindowAttributes attribs;

    NSMutableArray *children;
    
    NSRect onscreen;

    NSString *desc;
    NSImage *image;
}
\end{lstlisting}

The intializer is straightforward. Note that it doesn't require the array of
children or the image, which are computed later in the program, and only if
necessary.

\begin{lstlisting}{}
- initWithDisplay:(Display *)disp 
	   window:(Window)win
	   parent:(Window)par;
\end{lstlisting}

We need access to the attributes of the window. The methods for this return
instance variables. The method {\tt onScreen} indicates whether any portion at
all of the window is on the screen.

\begin{lstlisting}{}
- (NSString *)description;
- (int)map_state;
- (int)xclass;
- (BOOL)onScreen;
\end{lstlisting}

The next two methods let us set and retrieve an array of children for the
receiver.

\begin{lstlisting}{}
- setChildren:(NSMutableArray *)cs;
- (NSMutableArray *)children;
\end{lstlisting}

The entries of the browser's columns should be in alphabetical order so we
need a comparison method between two {\tt XWinData} instances that we'll use
to sort them.

\begin{lstlisting}{}
- (NSComparisonResult)cmpdescriptions:(XWinData *)other;
\end{lstlisting}

If it turns out that some part of a window is on the screen, possibly
obscured, then we compute an image from the contents of the window. There is
an accessor for this instance variable.

\begin{lstlisting}{}
- computeImage;
- (NSImage *)image;
\end{lstlisting}

The last method sends appropriate release messages to the array of children
and the description (a string) when the object is being deallocated.

\begin{lstlisting}{}
- (void)dealloc;

@end
\end{lstlisting}

We now discuss these methods in detail. The initializer stores its arguments
in the appropriate instance variables. It then tries to obtain the window's
attributes from the server and raises an exception if it fails.

\begin{lstlisting}{}
@implementation XWinData

- initWithDisplay:(Display *)disp 
	   window:(Window)win
	   parent:(Window)par
{
    display = disp;
    window = win;
    parent = par;

    children = nil;
    
    if(!XGetWindowAttributes(display, window, &attribs)){
	NSString *fmt = 
	    @"couldn't get window attributes: 0x%x";
	[NSException raise:NSGenericException
                     format:fmt, (unsigned)window];
    }
\end{lstlisting}

The next step is to translate the coordinates of the window's origin into
screen coordiates (the values in the attribute structure are in the parent's
coordinate system).

\begin{lstlisting}{}
    int tx, ty; 
    if(window==attribs.root){
	tx = 0; ty = 0;
    }
    else{
	Window child_return;
	XTranslateCoordinates(display, parent, attribs.root, 
			      attribs.x, attribs.y, &tx, &ty,
			      &child_return);
    }
\end{lstlisting}

We may compute the portion of the window that is on the screen once the
coordinates have been translated. We intersect the screen rectangle with the
window's rectangle for this purpose. We'll be using the result when we
retrieve the image and decide whether the window should be included in the
tree.

\begin{lstlisting}{}
    NSRect 
	srect =
	NSMakeRect(0, 0,
		   attribs.screen->width, 
		   attribs.screen->height),
	wrect = 
	NSMakeRect(tx, ty,
		   attribs.width, 
		   attribs.height);

    onscreen = NSIntersectionRect(srect, wrect);
\end{lstlisting}

Next the initializer builds the description and then it exits. If the name
property of the window is set, then we use it as the description. Otherwise we
record that there was no name, namely by giving the root window the name {\tt
ROOT WINDOW} and other unnamed windows the name {\tt \{no title\}}. The
description of unnamed windows includes the geometry of the window.

\begin{lstlisting}{}
    char *cname;
    XFetchName(display, window, &cname);

    if(cname!=NULL){
	desc = [NSString stringWithCString:cname];
	XFree(cname);
    }
    else{
	NSString *fmt = @"%s %dx%d+%d+%d 0x%x";
	desc = 
	    [NSString stringWithFormat:fmt,
		      (window==attribs.root ? 
		       "ROOT WINDOW" : "{no title}"),
		      attribs.width, attribs.height,
		      attribs.x, attribs.y, 
		      (unsigned int)window];
    }
    [desc retain];
\end{lstlisting}

The newly initialized object is put into an autorelease pool.

\begin{lstlisting}{}
    image = nil;

    return [self autorelease];
}
\end{lstlisting}

The methods {\tt description}, {\tt map\_state} and {\tt xclass} return the
appropriate instance variables.

\begin{lstlisting}{}
- (NSString *)description
{
    return desc;
}

- (int)map_state
{
    return attribs.map_state;
}

- (int)xclass
{
    return attribs.class;
}
\end{lstlisting}

A window is on the screen if its onscreen-rectangle is not empty.

\begin{lstlisting}{}
- (BOOL)onScreen
{
    return (NSIsEmptyRect(onscreen) == YES ? NO : YES);
}
\end{lstlisting}

We need to set and retrieve the array of children. Setting the array will
occur at most once, so we don't have to worry about releasing a prior content
of the instance variable.

\begin{lstlisting}{}
- setChildren:(NSMutableArray *)cs
{
    children = cs; [children retain];
    return self;
}

- (NSMutableArray *)children
{
    return children;
}
\end{lstlisting}

{\tt XWinData} objects are ordered according to the description string for
sorting purposes i.e. getting the right order in the browser's columns.

\begin{lstlisting}{}
- (NSComparisonResult)cmpdescriptions:(XWinData *)other
{
    NSString *odesc = [other description];
    return [desc caseInsensitiveCompare:odesc];
}
\end{lstlisting}

The next method is perhaps the most important of the entire application. It
retrieves the window's image from the window server and stores it in an {\tt
NSImage}. We'll be using a bitmap as the image's representation, so we can
write data directly into the bitmap's data plane. (We'll use a non-planar
bitmap with one plane.)

The first step is to translate the screen coordinates of the onscreen
rectangle into window coordinates, so that we can reference the portion of the
image that we wish to read. We translate the coordinates from root window
coordinates to window coordinates and use the clipped bounds rectangle to
obtain the dimensions of the displayed rectangle.

\begin{lstlisting}{}
- computeImage
{
    int tx, ty; 

    if(window==attribs.root){
	tx = 0; ty = 0;
    }
    else{
	Window child_return;
	XTranslateCoordinates(display, attribs.root, window,
			      onscreen.origin.x, onscreen.origin.y,
			      &tx, &ty, &child_return);
    }
    int 
	width = onscreen.size.width, 
	height = onscreen.size.height;
\end{lstlisting}

We are now ready to read the image data and invoke {\tt XGetImage} for this
purpose. We raise an exception if we couldn't read the image.

\begin{lstlisting}{}
    XImage *ximage = 
        XGetImage(display, window, 
		  tx, ty, width, height,
		  -1, ZPixmap);

    if(ximage==NULL){
	[NSException 
	    raise:NSGenericException
	    format:@"couldn't get image: 0x%x, %@", 
	    (unsigned)window, desc];
    }
\end{lstlisting}

We must convert the data in the {\tt XImage} into a bitmap image
representation, which we now declare. It has the correct dimensions and uses
eight bits per sample (red, green, blue). There is no alpha channel. We
retrieve the bitmap's data plane for easy reference. There is only one plane
because the bitmap is not planar.

\begin{lstlisting}{}
    NSBitmapImageRep *rep =
	[[NSBitmapImageRep alloc]
	    initWithBitmapDataPlanes:NULL
	    pixelsWide:width
	    pixelsHigh:height
	    bitsPerSample:8
	    samplesPerPixel:3
	    hasAlpha:NO
	    isPlanar:NO
	    colorSpaceName:NSDeviceRGBColorSpace
	    bytesPerRow:width*3
	    bitsPerPixel:8*3];

    unsigned char *base, *data = [rep bitmapData];
\end{lstlisting}

We briefly digress to explain how we obtain the colors of the pixels of the
image. There is a function {\tt XGetPixel}, which we can use to read the
pixels of the image, which are unsigned long integers. If we are not on a {\tt
TrueColor} visual, then we obtain the color components for this pixel using
the window's color map with a call to {\tt XQueryColor}, otherwise we extract
the bits for each color from the pixel's value.  Once we have these, it is
easy to write them into the bitmap data plane. 

We do {\tt TrueColor} visuals first. The visual contains a bit mask for each
color component. We must determine where in the pixel value the component
starts, and how many bits it takes up. Therefore we declare a ``shift''
variable (offset, i.e. position) and a ``bits'' variable (number of bits) for
each color component.

\begin{lstlisting}{}
    int x, y;

    if(attribs.depth>=8 && attribs.visual->class==TrueColor){
	unsigned long
	    rmask = attribs.visual->red_mask,
	    gmask = attribs.visual->green_mask,
	    bmask = attribs.visual->blue_mask;
	unsigned long
	    rshift, rbits, gshift, gbits, bshift, bbits;
\end{lstlisting}

We apply the following procedure to each mask. First shift the mask to the
right as long as the lowest bit is zero. The number of shifts indicates the
position of the mask in the pixel. Next shift the mask to the right while the
lowest bit is one. The number of these shifts yields the number of bits of the
color component. We use at most eight bits. If there are more than eight bits,
then we increment the position counter to skip over the least significant bits
so that only the eight most significant bits remain.

\begin{lstlisting}{}
	rshift = 0; rbits = 0;
	while(!(rmask & 1)){ 
	    rshift++; 
	    rmask >>= 1; 
	}
	while(rmask & 1){
	    rbits++;
	    rmask >>= 1;
	}
	if(rbits>8){
	    rshift += rbits-8;
	    rbits = 8;
	}

	gshift = 0; gbits = 0;
	while(!(gmask & 1)){ 
	    gshift++; 
	    gmask >>= 1; 
	}
	while(gmask & 1){
	    gbits++;
	    gmask >>= 1;
	}
	if(gbits>8){
	    gshift += gbits-8;
	    gbits = 8;
	}

	bshift = 0; bbits = 0;
	while(!(bmask & 1)){ 
	    bshift++; 
	    bmask >>= 1; 
	}
	while(bmask & 1){
	    bbits++;
	    bmask >>= 1;
	}
	if(bbits>8){
	    bshift += bbits-8;
	    bbits = 8;
	}
\end{lstlisting}

We iterate over the entire image and extract the pixel values for each
position $(x, y).$

\begin{lstlisting}{}
	for(y=0; y<height; y++){
	    for(x=0; x<width; x++){
		unsigned long c;
		XColor color;

		int centry;

		c = XGetPixel(ximage, x, y);
\end{lstlisting}

We write the color components directly into the data plane. There are three
steps for each component. Shift it to the right so that the component's bit
sequence starts at bit zero. Compute a mask that is as long as the number of
bits of the component and consists entirely of ones, then extract the value of
the component by computing the bitwise {\tt and} of the component and the
mask. Finally, if there were less than eight bits to the component, then pad
it with zeros at the right so that it is eight bits long. We output debugging
information once the entire image has been processed.

\begin{lstlisting}{}
		base = data+(y*width+x)*3;

		base[0] = ((c >> rshift) & ((1 << rbits)-1)) 
			      << (8-rbits);
		base[1] = ((c >> gshift) & ((1 << gbits)-1))
			      << (8-gbits);
		base[2] = ((c >> bshift) & ((1 << bbits)-1)) 
			      << (8-bbits);
	    }
	}

	NSLog(@"%@ TrueColor +%d %d, +%d %d, +%d %d", self,
	      rshift, rbits,
	      gshift, gbits,
	      bshift, bbits);
    }
\end{lstlisting}

We now treat the case when we must use {\tt XQueryColor} to obtain the
color. There is only one problem: it is very slow to invoke {\tt XQueryColor}
for every pixel. Hence we cache colors locally. The cache consists of an array
of pixels and an array of colors. The entries at the same index of these two
arrays yield the pixel and the corresponding color. The index for a pixel is
the pixel value modulo the cache size. (This is a kind of hash.) We also have
an array that indicates whether a particular entry has already been written
to. The latter array is initialized so that all entries are marked as
empty. You may want to experiment with different sizes of the cache and
observe how the performance of the application changes.

\begin{lstlisting}{}
    else{
	Colormap cmap = attribs.colormap;
        #define CSIZE 16384
	unsigned long pixels[CSIZE];
	XColor colors[CSIZE];
	BOOL empty[CSIZE];

	int cind;
	for(cind=0; cind<CSIZE; cind++){
	    empty[cind] = YES;
	}
\end{lstlisting}

We may now iterate over the pixels and write into the bitmap's data
plane. Every pixel of the image at some position $(x, y)$ is retrieved.

\begin{lstlisting}{}
	for(y=0; y<height; y++){
	    for(x=0; x<width; x++){
		unsigned long c;
		XColor color;

		int centry;

		c = XGetPixel(ximage, x, y);
\end{lstlisting}

Next we compute where the color would be in the cache if indeed it has been
recorded. Only if we know that the corresponding entry is not empty and its
pixel value matches the current pixel may we use the color from the cache. We
use {\tt XQueryColor} in all other cases and retrieve the color from the
server. The new color is recorded in the cache.

\begin{lstlisting}{}
		centry = c % CSIZE;
	    
		if(empty[centry]==NO && 
		   pixels[centry]==c){
		    color = colors[centry];
		}
		else{
		    empty[centry] = NO;

		    color.pixel = c;
		    XQueryColor(display, cmap, &color);

		    pixels[centry] = c;
		    colors[centry] = color;
		}
\end{lstlisting}

We now have the color components at the location $(x, y)$ and compute the
offset into the data plane. The components are actually sixteen bits, but we
work with eight bits per sample and use only the upper eight bits.

\begin{lstlisting}{}
		base = data+(y*width+x)*3;

		base[0] = color.red   >> 8;
		base[1] = color.green >> 8;
		base[2] = color.blue  >> 8;
	    }
	}

	NSLog(@"%@ color cache", self);
    }
\end{lstlisting}

The bitmap image representation contains the image at the end of the loop. We
are done with the {\tt XImage} and it may be freed. We allocate an image of
the right size and make the bitmap its (only) representation. The image is
retained and there is a method to access the image.

\begin{lstlisting}{}
    XDestroyImage(ximage);

    image = [[NSImage alloc]
		initWithSize:NSMakeSize(width, height)];
    [image addRepresentation:rep];
    [image retain];

    return self;
}


- (NSImage *)image
{
    return image;
}
\end{lstlisting}

The complete functionality of {\tt XWinData} has now been implemented. The
method {\tt dealloc} releases the array of children and the image should they
have been allocated.

\begin{lstlisting}{}
- (void)dealloc
{
    if(image!=nil){
	[image release];
    }
    if(children!=nil){
	[children release];
    }
    [desc release];
    
    [super dealloc];
}

@end
\end{lstlisting}

\subsubsection{{\tt ImageView}}

The class {\tt ImageView} is quite simple. Like {\tt NSImageView}, it stores
an image for display. The initializer is the same as in an {\tt NSView}. The
image may be recorded and retrieved, and the method {\tt drawRect} does the
actual drawing as is standard with views.

\begin{lstlisting}{}
@interface ImageView : NSView
{
    NSImage *image;
}

- (id)initWithFrame:(NSRect)frameRect;

- (NSImage *)image;
- setImage:(NSImage *)anImage;

- (void)drawRect:(NSRect)aRect;

@end
\end{lstlisting}

The initializer clears the sole instance variable and invokes the initializer
for {\tt NSView.}

\begin{lstlisting}{}
@implementation ImageView

- (id)initWithFrame:(NSRect)frameRect
{
    image = nil;
    return [super initWithFrame:frameRect];
}
\end{lstlisting}

Getting and setting the image is easy, but the appropriate {\tt
retain/release} messages must be sent.

\begin{lstlisting}{}
- (NSImage *)image
{
    return image;
}

- setImage:(NSImage *)anImage
{
    if(image!=nil){
        [image release];
    }
    image = anImage; 
    if(image!=nil){
        [image retain];
    }
    [self setNeedsDisplay:YES];
}
\end{lstlisting}

The actual draw code clips to the rectangle that is being asked for and
commands the first representation to draw itself (a bitmap in our case),

\begin{lstlisting}{}
- (void)drawRect:(NSRect)aRect
{
    // idea for this method by A. Malmberg
    if(image!=nil){
        NSRectClip(aRect);
        [[[image representations] 
             objectAtIndex:0] draw];
    }
}

@end
\end{lstlisting}

\subsubsection{{\tt Controller} and {\tt main}}

The controller object stores the browser and the image view, which is inside a
scrollview that does not need to be stored. It also stores the root node of
the window tree.

\begin{lstlisting}{}
@interface Controller : NSObject
{
    NSBrowser *browser;
    ImageView *imgview;
    XWinData *rootData;
}
\end{lstlisting}

There is an initializer that invokes the method {\tt collectVisibles...} to
collect visible on-screen windows before the application object is created
and the application launched. This is so that the application's windows do not
obscure parts of the screen, whose contents we want to grab.

\begin{lstlisting}{}
- init;

- (XWinData *)collectVisiblesOnDisplay:(Display *)display
				window:(Window)window;
\end{lstlisting}

The controller acts as a passive delegate of the browser and implements the
appropriate methods. The key idea is to store the nodes, which are {\tt
XWinData} objects, as ``represented objects'' in the browser's cell. This is
the hook that connects the browser to the window tree. There is a method that
responds to the browser's cells being selected. It must display the
corresponding image.

\begin{lstlisting}{}
- (int)browser:(NSBrowser *)sender numberOfRowsInColumn:(int)column;
- (void)browser:(NSBrowser *)sender willDisplayCell:(id)cell 
	  atRow:(int)row column:(int)column;
- (void)selectItem:(id)sender;
\end{lstlisting}

The controller is also the application's delegate and assembles the main
window after the application has been launched.

\begin{lstlisting}{}
- (void)applicationDidFinishLaunching:(NSNotification *)notif;
\end{lstlisting}

We need a method that will save the currently selected image to a file when
the button in the menu is pressed. That button should only be clickable after
a window has been selected in the browser, hence the method to validate menu
items.

\begin{lstlisting}{}
- (void)saveAs:(id)sender;

- (BOOL)validateMenuItem:(id <NSMenuItem>)menuItem;

@end
\end{lstlisting}

The initializer is straightforward: obtain the display and raise an exception
if there is a problem. Next obtain the screen and its root window. Then build
the window tree. 

\begin{lstlisting}{}
@implementation Controller

- init
{
    Display *display = XOpenDisplay(NULL);
    if(display==NULL){
        [NSException raise:NSGenericException
                     format:@"couldn't connect to display"];
    }

    int screen = DefaultScreen(display);
    Window rootWindow = RootWindow(display, screen);
    rootData =
	[self collectVisiblesOnDisplay:display 
	      window:rootWindow];

    return self;
}
\end{lstlisting}

Building the window tree is actually fairly simple. We rely on the procedure
{\tt XQueryTree} to walk the tree of X windows and build our tree of {\tt
XWinData} objects. The method {\tt collectVisibles...} returns the node that
it has created. We start at the root and recursively process children. The
first step is to ask for the children of the current window.

\begin{lstlisting}{}
- (XWinData *)collectVisiblesOnDisplay:(Display *)display
				window:(Window)window
{
    Window root_return;
    Window parent_return;
    Window *children_return;
    unsigned int nchildren_return;

    XQueryTree(display, window, 
	       &root_return, &parent_return, 
	       &children_return, &nchildren_return);
\end{lstlisting}

The next step is to create the current node. We need the attributes of the
window that the initializer of {\tt XWinData} retrieves.

\begin{lstlisting}{}
    XWinData *current =
	[[XWinData alloc] 
	    initWithDisplay:display
	    window:window
	    parent:parent_return];
\end{lstlisting}

What follows is very important. We identify the windows that go into our tree
(not all X windows do). The window must be viewable, it must not be an
input-only window, and some part of it must be on screen. If all these
conditions are fulfilled, then we record the success in the flag {\tt process}
(which tells us whether we should recurse later on) and we compute the image
of the window. A window that does not fulfill these condition causes the
corresponding {\tt XWinData} object to be released (recall that we put it into
an autorelease pool, so it will be deallocated at some point in the run loop).

\begin{lstlisting}{}
    BOOL process = NO;
    if([current map_state]==IsViewable &&
       [current xclass]==InputOutput &&
       [current onScreen]==YES){
	process = YES;
	[current computeImage];
    }
    else{
	[current release];
    }
\end{lstlisting}

The actual recursion follows. There is work to do if the current window was
processed and has children. We allocate an array for these children and
declare an index that we'll use to iterate over the array {\tt
children\_return} that we obtained from {\tt XQueryTree.}

\begin{lstlisting}{}
    if(children_return!=NULL){
	if(process==YES){
	    NSMutableArray *children =
		[NSMutableArray arrayWithCapacity:1];
	    int ind;
\end{lstlisting}

The loop iterates over the children and recursively creates a node for
each. This node is recorded in the array object if it is not empty.

\begin{lstlisting}{}
	    for(ind=0; ind<nchildren_return; ind++){
		XWinData *child = 
		    [self collectVisiblesOnDisplay:display
			  window:children_return[ind]];
		if(child!=nil){
		    [children addObject:child];
		}
	    }
\end{lstlisting}

We sort the children by their names after all have been obtained. The sorted
array is recorded in the instance variable {\tt children} of the current
node.

\begin{lstlisting}{}
	    [children sortUsingSelector:@selector(cmpdescriptions:)];
	    [current setChildren:children];
	}
\end{lstlisting}

We must free the list of X windows if indeed there was one. The method returns
the newly created node if the window fulfilled our three conditions and {\tt
nil} otherwise.

\begin{lstlisting}{}
	 
	XFree(children_return);
    }

    return (process==YES ? current : nil);
}
\end{lstlisting}

The controller is the passive delegate of the browser, and the required
methods are straightforward. First, we must be able to tell the browser how
many rows there are in a given column. This is easy. There is one row that
contains the root window in column zero. Otherwise there is a column to the
left of the column being loaded, one of its cells is selected, and the new
column should display the children of the object represented by the selected
cell; hence we ask the selected cell for the represented object and return the
number of children of the object.

\begin{lstlisting}{}
- (int)browser:(NSBrowser *)sender numberOfRowsInColumn:(int)column
{
    if(!column){
	return 1;
    }

    return 
	[[[[sender selectedCell] representedObject] 
	     children] count];
}
\end{lstlisting}

Second, we must initialize cells that are about to be displayed. A cell should
be initialized with data from the represented object, which is the root window
in column zero. For non-zero columns the object is the child at position {\tt
row} in the array of children of the selected cell, which is located in the
previous column.

\begin{lstlisting}{}
- (void)browser:(NSBrowser *)sender willDisplayCell:(id)cell 
	  atRow:(int)row column:(int)column
{
    XWinData *represented;

    if(!column){
	represented = rootData;
    }
    else{
	represented =
	    [[[[sender selectedCell] representedObject]
		 children] objectAtIndex:row];
    }
\end{lstlisting}

We read the properties of the represented object and record them in the
cell. We must know if there are any children; this determines whether the cell
is a leaf or not. The string value of the cell is the description string of
the object, and the object itself must be recorded in the cell.

\begin{lstlisting}{}
    int ccount = [[represented children] count];

    [cell setStringValue:[represented description]];
    [cell setRepresentedObject:represented];
    [cell setLeaf:(ccount>0 ? NO : YES)];
}
\end{lstlisting}

What happens when the user selects a cell in the browser? The method {\tt
selectItem} is the action that is invoked in this case. It retrieves the
represented object and the image that it contains.

\begin{lstlisting}{}
- (void)selectItem:(id)sender
{
    XWinData *choice = 
	[[(NSBrowser *)sender selectedCell]
	    representedObject];
    NSImage *img = [choice image];
\end{lstlisting}

We place the image in the image view (which is inside a scrollview) and
resize the image view to be the same size as the image. This completes our
interface with the browser.

\begin{lstlisting}{}
    [imgview setImage:img];

    NSSize size = [img size];
    NSRect iframe = NSMakeRect(0, 0, size.width, size.height);
    [imgview setFrame:iframe];
}
\end{lstlisting}

The remaining code is concerned with the assembly of the main window and the
response to save-as-requests. The controller builds the main window after the
application has finished launching. Recall that the window consists of an
upper part (the browser) and a lower part (the scrollview with the
image). Both parts have the same width and different heights. These are given
by constants.

\begin{lstlisting}{}
#define WIDTH_OF_WINDOW       400
#define HEIGHT_OF_BROWSER     150
#define HEIGHT_OF_SCROLLVIEW  350
\end{lstlisting}

First compute the content rectangle to hold both the upper and the lower
part. Then allocate and initialize a window for this size of content view, and
set its minimum size and its title.

\begin{lstlisting}{}
- (void)applicationDidFinishLaunching:(NSNotification *)notif
{
    [[NSApplication sharedApplication] updateWindows];

    NSRect wframe =
	NSMakeRect(0, 0, 
		   WIDTH_OF_WINDOW,
		   HEIGHT_OF_BROWSER+HEIGHT_OF_SCROLLVIEW);
    NSWindow *window =
	[[NSWindow alloc]
            initWithContentRect:wframe
            styleMask:NSTitledWindowMask | NSResizableWindowMask
            backing:NSBackingStoreBuffered
            defer:NO];
    [window setMinSize:wframe.size];
    [window setTitle:@"Grab"];
\end{lstlisting}

The upper part is next. Compute the frame rectangle of the browser and
allocate and initialize it.

\begin{lstlisting}{}
    NSRect brect =
	NSMakeRect(0, HEIGHT_OF_SCROLLVIEW,
		   WIDTH_OF_WINDOW, HEIGHT_OF_BROWSER);
    browser = [[NSBrowser alloc] initWithFrame:brect];
\end{lstlisting}

The browser should be width-sizable but its height should not change. It is
not titled and does not allow multiple selections. There is a minimum value
for the width of the individual columns.

\begin{lstlisting}{}
    [browser 
	setAutoresizingMask:
	    NSViewWidthSizable | NSViewMinYMargin];
    [browser setTitled:NO];
    [browser setAllowsMultipleSelection:NO];
    [browser setMinColumnWidth:WIDTH_OF_WINDOW/3];
\end{lstlisting}

Finally we connect the browser to the controller, which is its delegate and
implements a passive delegate's methods, as we have seen; the browser's target
is the controller and its action the method {\tt selectItem.} We must place it
in the view hierarchy.

\begin{lstlisting}{}
    [browser setDelegate:self];
    [browser setTarget:self];
    [browser setAction:@selector(selectItem:)];

    [[window contentView] addSubview:browser];
\end{lstlisting}

The lower part contains the scrollview. We compute the frame rectangle and
allocate and initialize it.

\begin{lstlisting}{}
    NSRect srect =
	NSMakeRect(0, 0,
		   WIDTH_OF_WINDOW, HEIGHT_OF_SCROLLVIEW);
    NSScrollView *scrollview =
	[[NSScrollView alloc] initWithFrame:srect];
\end{lstlisting}

The scrollview should be width-sizable and height-sizable and it should have
vertical and horizontal scrollers.

\begin{lstlisting}{}
    [scrollview
	setAutoresizingMask:
	    NSViewWidthSizable | NSViewHeightSizable];
    
    [scrollview setHasHorizontalScroller:YES];
    [scrollview setHasVerticalScroller:YES];
\end{lstlisting}

The document of the scrollview is an image view of the fast variety that we
discussed above. It is initally empty and we choose a frame that will not
cause the scrollview to draw scrollers. We set the image view to be the
document view of the scrollview.

\begin{lstlisting}{}
    NSRect irect = 
	NSMakeRect(0, 0, 
		   HEIGHT_OF_SCROLLVIEW/2,
		   HEIGHT_OF_SCROLLVIEW/2);
    imgview = 
	[[ImageView alloc] initWithFrame:irect];
    [scrollview setDocumentView:imgview];
\end{lstlisting}

The window is fully assembled once we place the scrollview in the view
hierarchy and order the window (now centered) to the front.

\begin{lstlisting}{}
    [[window contentView] addSubview:scrollview];

    [window center];
    [window makeKeyAndOrderFront:self];
}
\end{lstlisting}

The penultimate method of the controller lets the user save images to
files. The structure is very simple: obtain the selected cell and its image,
try to write the bitmap image representation to a TIFF file and signal an
error if you fail. We declare {\tt errno} to have access to the systems error
messages. We ask for the savepanel and restrict it to TIFF file names. Then we
run the panel.

\begin{lstlisting}{}
extern int errno;

-(void)saveAs:(id)sender
{
    NSSavePanel *savePanel;
    int result;

    savePanel = [NSSavePanel savePanel];
    [savePanel setRequiredFileType:@"tiff"];
    result = [savePanel runModal];
\end{lstlisting}

We proceed if the user clicked the OK button and ask the panel for the file
name, the browser for the currently selected cell, the cell for the object it
represents, the object for the image, and the image for its bitmap
representation.

\begin{lstlisting}{}
    if(result == NSOKButton){
	NSString *fname;
        fname = [savePanel filename];

	XWinData *choice = 
	    [[browser selectedCell] representedObject];
	NSImage *img = [choice image];
	NSBitmapImageRep *rep =
	    [[img representations] objectAtIndex:0];
\end{lstlisting}

We read the bitmap's TIFF representation into an {\tt NSData} object and try
to write this object to a file with the name that the user selected.

\begin{lstlisting}{}
	NSData *data =
	    [rep TIFFRepresentationUsingCompression:
		     NSTIFFCompressionPackBits
		 factor:0.5];
	BOOL result=[data writeToFile:fname atomically:NO];
\end{lstlisting}

The remaining code in this method tries to construct a meaningful error
message if we couldn't write the file. The message contains the name of the
file and the error string for the current value of {\tt errno}, if the latter
was set. The message is displayed with an alert panel.

\begin{lstlisting}{}
        if(result==NO){
            NSString *msg = 
		[NSString stringWithFormat:@"Couldn't write %@", 
			  fname];

	    if(errno){
		char *estr = strerror(errno);
		msg = [msg stringByAppendingFormat:@": %s", estr];
	    }

            NSRunAlertPanel(@"Alert", msg, @"Ok", nil, nil);
        }
    }
\end{lstlisting}

We reset the cursor to an arrow should it still be the ibeam cursor (this used
to happen sometimes, not sure if it still does).

\begin{lstlisting}{}
    [[NSCursor arrowCursor] set];
}
\end{lstlisting}

The last method of the controller keeps the save-as menu item, whose tag is
one, disabled if no window has yet been selected in the browser.

\begin{lstlisting}{}
- (BOOL)validateMenuItem:(id <NSMenuItem>)menuItem
{
    int row = [browser selectedRowInColumn:0];
    if([menuItem tag]==1 && row==-1){
	return NO;
    }

    return YES;
}

@end
\end{lstlisting}

This very nearly concludes the discussion of our screen grab application. The
routine {\tt main} creates the autorelease pool, the controller and the
application. The controller is created before the application so that the
screen data do not include the tool's windows, as explained earlier.

\begin{lstlisting}{}
int main(int argc, char** argv, char **env)
{
    NSAutoreleasePool *pool = [NSAutoreleasePool new];
           
    Controller *con = [Controller new];

    NSApplication *app;
    app = [NSApplication sharedApplication];
\end{lstlisting}

The application's menu only contains two entries; one that quits the
application and another that lets the user save images.

\begin{lstlisting}{}
    NSMenu *menu = [NSMenu new];
    [[menu addItemWithTitle: @"Save As"
          action:@selector(saveAs:)
	   keyEquivalent:@""]
	setTag:1];
    [menu addItemWithTitle: @"Quit"
          action:@selector(terminate:)
          keyEquivalent:@"q"];
    [NSApp setMainMenu:menu];
\end{lstlisting}

It remains to connect the controller to the application and run it.

\begin{lstlisting}{}
    [app setDelegate:con];
    [app run];    

    [pool release];
    exit(0);
}
\end{lstlisting}

This recipe was written with the online Xlib manual kindly provided by
Christophe Tronche. Excercise: adapt this recipe to take window borders into
account.