Chapter 19. Zooming and Saving Graphics Files

This chapter shows how to do a few interesting things with NSViews. In the first part, we’ll show how to put a zoom pop-up menu in an NSScrollView. In the second part, we’ll show how to generate Encapsulated PostScript (EPS), PDF, or TIFF files from an NSView; how to save a graph into an EPS, PDF, or TIFF file; and how to add controls to a Save panel (dialog).

Adding a Zoom Button to GraphPaper

In the previous chapter, we arranged for the GraphView object to rescale its coordinate system when its containing window was resized. Although the scaled coordinate system is appropriate for our graphing application and is a good way to show how to catch resizing events, stretching an application’s window isn’t the right way for a user to get a magnified view of the window’s contents. Consider the Aqua interface standard: a window is supposed to be just that — a window into a page of a virtual document. The document itself shouldn’t get bigger or smaller when the window is resized; rather, a bigger or smaller window should let the user see more or less of a document.

Mac OS X applications should enable the user to see more detail using a zoom button — not the green zoom button in a window’s title bar, but a little pop-up menu button, typically located at the bottom of an NSScrollView, that allows you to change the magnification of the NSScrollView. Microsoft Word and Stone Design’s Create applications both have such a button (see Figure 19-1).

Zoom pop-up menu button in Stone Design’s Create

Figure 19-1. Zoom pop-up menu button in Stone Design’s Create

The window on the left in Figure 19-1 is set to a zoom factor of 100%. Pressing the zoom button reveals a pop-up menu of different magnification settings, which in turn lets you change the size of the text that is displayed. In the right window, we changed the setting to 200%.

It’s easy to add a zoom button to any NSScrollView, but you have to know a little bit about how the NSScrollView works first.

The NSScrollView Class Revisited

Each NSScrollView object has another object “inside” it, called its docView , which is the actual NSView being displayed. In addition to the docView, each NSScrollView has the following three NSView objects to help it perform its scrolling tasks:

  • An NSScroller to control horizontal scrolling

  • An NSScroller to control vertical scrolling

  • An NSClipView that displays the part of the docView that is visible

We can change the magnification of the NSView displayed in the NSScrollView simply by sending a scale: message to the NSClipView. The NSClipView will scale the NSView that it contains when it is drawn. Zooming happens without the knowledge or cooperation of the docView.

Whenever an NSScrollView changes size (for example, when it is resized), and whenever the NSView that it contains changes size, the NSScrollView sends the tile message to itself to alert its subviews to change their sizes. By subclassing the NSScrollView class and overriding the tile method, we can place additional objects (such as rulers and zoom buttons) over or next to the scrollers that the NSScrollView displays.

In this section, we will subclass the NSScrollView class to make a new class called ZoomScrollView. This class will have outlets for its docView — the TrackingGraphView that we created in the previous chapter — as well as for the zoom pop-up menu. We’ll control the ZoomScrollView instance from the Controller object that we created in Chapter 17.

Changes to MainMenu.nib

  1. Open your GraphPaper project in Project Builder and the MainMenu.nib file in Interface Builder.

  2. Resize the TrackingGraphView in the GraphPaper window so that it’s smaller, and put it somewhere in the window that is out of the way.

    Don’t worry about the size or position of the TrackingGraphView — it will automatically be resized and placed in the proper position when GraphPaper runs.

  3. Subclass the NSScrollView class and rename the new subclass “ZoomScrollView”.

  4. Add the subView and zoomButton outlets and the changeZoom: action method to the new ZoomScrollView class.

  5. Drag a CustomView icon from IB’s Cocoa-Containers palette and drop it in the GraphPaper window. Change its class to ZoomScrollView.

  6. Resize the on-screen ZoomScrollView instance to be as large as the previous TrackingGraphView instance. Note that the TrackingGraphView instance shows through the ZoomScrollView a bit.

  7. Choose IB’s Layout Send To Back menu command to put the ZoomScrollView instance “behind” the TrackingGraphView instance, as shown in Figure 19-2.

    The only reason to send the ZoomScrollView to the back is so we can easily see both NSViews at the same time; it won’t make a difference in the way GraphPaper runs.

    ZoomScrollView in GraphPaper window

    Figure 19-2. ZoomScrollView in GraphPaper window

  8. Drag a pop-up menu button from IB’s Cocoa-Other palette and place it below the ZoomScrollView, to the left of the x: field (see Figure 19-3). Do not place it on the ZoomScrollView.

  9. Double-click the pop-up menu button.

When you double-click the pop-up menu button in IB, you’ll see the three menu cells that the associated pop-up menu initially contains. The on-screen pop-up menu is controlled by an instance of the NSPopUpButton class. The NSPopUpButton class, a subclass of the NSButton class, creates an NSMenu object to handle its menu-like functionality.

You can add a new item to an open pop-up menu in IB by dragging the Item menu cell from the Cocoa-Menus palette and dropping it in the pop-up menu. You can’t add submenus to a pop-up menu, however, because that would violate the Aqua interface guidelines.

You can give an individual target and action to each item within the pop-up menu. Alternatively, you can simply give an action to the button on top, which is called the cover (a type of NSPopUpButton). After a selection is made, the NSPopUpButton automatically changes the title of the button by sending it the setTitle: message.

  1. One by one, drag three more menu items from the Cocoa-Menus palette and drop them in the pop-up menu. Make the width of the pop-up menu smaller.

  2. Name the menu cells 100%, 125%, 150%, 175%, 200%, and 300%, and give them the tags 100, 125, 150, 175, 200, and 300, respectively (use the NSMenuCell Attributes Info dialog). When you’re done, the pop-up menu should look like the one shown here.

    ZoomScrollView in GraphPaper window

  3. Connect the NSPopUpButton’s cover to the ZoomScrollView so that it sends the changeZoom: action message (see Figure 19-3).

changeZoom: action connection from PopUpButton to ZoomScrollView in IB

Figure 19-3. changeZoom: action connection from PopUpButton to ZoomScrollView in IB

  1. Click the 100% item and close the pop-up menu by clicking somewhere else in the window. This ensures that the initial condition will be with the pop-up menu at 100%.

  2. Connect the ZoomScrollView’s zoomButton outlet to the pop-up menu button. Note that this connection is in the opposite direction of the previous one.

  3. Connect the ZoomScrollView’s subView outlet to the TrackingGraphView, as shown in Figure 19-4.

subView outlet connection from ZoomScrollView to TrackingGraphView

Figure 19-4. subView outlet connection from ZoomScrollView to TrackingGraphView

  1. Create the class files for ZoomScrollView and insert them into the GraphPaper project.

Changes to ZoomScrollView

  1. Insert the lines shown here in bold into ZoomScrollView.h :

    #import <Cocoa/Cocoa.h>
    
    @interface ZoomScrollView : NSScrollView
    {
        IBOutlet id subView;
        IBOutlet id zoomButton;    double scaleFactor;
    }
    - (IBAction)changeZoom:(id)sender;- (id)initWithFrame:(NSRect)theFrame;
                            - (void)awakeFromNib;
                            - (void)setScaleFactor:(float)aFloat;
                            - (void)tile;
    @end

We’ll use the scaleFactor instance variable to store the current scale factor of the ZoomScrollView. For example, a user’s zoom choice of 100% will yield a scaleFactor of 1.0, a choice of 150% will yield a scaleFactor of 1.5, and so on. When the user changes the zoom percentage using the pop-up menu, the NSPopUpButton object will send the changeZoom: message to the ZoomScrollView object, which in turn will send the setScaleFactor: message to itself. ZoomScrollView’s setScaleFactor: method will then compare the new zoom factor with the current one and calculate the proper arguments to the scale: message. If the new magnification is the same as the old, the ZoomScrollView won’t do anything.

  1. Insert the initWithFrame: method into ZoomScrollView.m:

                            - (id)initWithFrame:(NSRect)theFrame
                            {
                                [super initWithFrame:theFrame];
                                [self setBackgroundColor:[NSColor whiteColor]];
                                scaleFactor = 1.0;
                                return self;
                            }

The initWithFrame: method is the designated initializer for the NSView class. ZoomScrollView’s initWithFrame: method sends the initWithFrame: message to its superclass and then sets the background of the NSScrollView to be white. Finally, it sets the current scale factor to be 1.0, which corresponds to the 100% menu item in the pop-up menu.

TrackingGraphView’s awakeFromNib method installs the TrackingGraphView instance as the docView inside the NSScrollView.

  1. Insert the following awakeFromNib method into ZoomScrollView.m:

                            -(void) awakeFromNib
                            {
                                [self setHasHorizontalScroller:YES];
                                [self setHasVerticalScroller:YES];
                                [self setBorderType:NSLineBorder];
                            
        // Set up the zoom button
                                [[zoomButton cell] setBordered:NO];
                                [[zoomButton cell] setBezeled:YES];
                                [[zoomButton cell] setFont:[NSFont labelFontOfSize:10.0]];
                                [self addSubview:zoomButton];
                            
        // The next two lines install the subView (TrackingGraphView)
                                // and set its size to be the same as the NSScrollView
                                [self setDocumentView:subView];
                                [subView setFrame:[[self contentView] frame]];
                            }

This method tells the ZoomScrollView that both scrollers are required and that its border should be of type NSLineBorder (as opposed to NSNoBorder, NSBezelBorder, or NSGrooveBorder, all of which are defined in NSView.h). Finally, it sizes the TrackingGraphView (the subView) to be the size of the NSScrollView’s contentView, which is the NSClipView.

The next method is the one that actually changes the magnification of the NSClipView object. NSClipViews are used by the NSScrollView class to do the actual displaying of the NSView.

  1. Insert the following setScaleFactor: method into ZoomScrollView.m:

                            - (void)setScaleFactor:(float)aFactor
                            {
                                NSAssert(aFactor!=0,@"Illegal scale factor. Set the tag!");
                            
        if (scaleFactor != aFactor) {
                                    float delta = aFactor/scaleFactor;
                                    scaleFactor = aFactor;
                                    [[self contentView]
                                     scaleUnitSquareToSize:NSMakeSize(delta,delta)];
                                }
                            }

The scaleUnitSquareToSize: method rescales the NSClipView (the contentView), with the arguments being the delta (change) necessary to make the NSClipView have the magnification that the user wants. This method uses the NSAssert( ) macro, which is similar to the ANSI C assert( ) macro, except that it allows you to specify a printf-style string that is printed if the assertion is false. If aFactor==0, the NSAssert( ) macro will raise the exception NSInternalInconsistencyException.

The next method is the action that is invoked when the user clicks the pop-up menu button.

  1. Insert the line shown here in bold into the changeZoom: method in ZoomScrollView.m :

    - (IBAction)changeZoom:(id)sender
    {    [self setScaleFactor:[[sender selectedCell] tag] / 100.0];
    }

Finally, there is our tile method, an override of NSScrollView’s tile method. It is invoked automatically when the ZoomScrollView’s size changes.

  1. Insert the following tile method into ZoomScrollView.m:

                            - (void)tile
                            {
                                NSRect scrollerRect, buttonRect;
                            
        [super tile];
                            
        // Place the pop-up button next to the scroller
                                scrollerRect = [[self horizontalScroller] frame];
                                NSDivideRect(scrollerRect, &buttonRect, &scrollerRect, 50.0,
                                             NSMaxXEdge);
                                [[self horizontalScroller] setFrame: scrollerRect];
                                [zoomButton setFrame: NSInsetRect(buttonRect, 1.0, 1.0)];
                            }

This tile method gets the frame of the horizontal scroller, snips off 50 pixels, and gives that space to the zoom button. The 50-pixel limit was determined by trial and error. Note how handy the Application Kit functions (e.g., NSDivideRect( ) and NSInsetRect( )) are for manipulating rectangles!

Testing the Zoom Button

  1. Build and run GraphPaper, saving all files first.

  2. You should get a magnification button in the lower-right corner of the ZoomScrollView, as shown in the window at the top of Figure 19-5. Enter a function and click the Graph button.

Zooming GraphPaper’s graph from 100% (top) to 175% (bottom)

Figure 19-5. Zooming GraphPaper’s graph from 100% (top) to 175% (bottom)

  1. Press the pop-up menu button, drag to 175%, and release the mouse button. The zoom button’s title should change, as shown in the window at the bottom of Figure 19-5.

  2. Resize the GraphPaper window — oops, we haven’t taken care of Autosizing yet! We’ll show you how to do that in the next section.

  3. Resize the GraphPaper window anyway and drag the scroll knobs. The ZoomScrollView should stretch, making more of the graph visible, rather than changing its scale, as it did previously.

  4. Quit GraphPaper.

Autosizing in GraphPaper

The zoom pop-up menu will make it more likely that a user will want to resize (probably enlarge) the GraphPaper window, but we didn’t take care of that possibility yet. We’ll do that now.

  1. Back in IB, select ZoomScrollView and type Command-3. Make ZoomScrollView’s Autosizing box look like the one on the left in Figure 19-6.

Autosizing for ZoomScrollView and NSForm instances in GraphPaper window

Figure 19-6. Autosizing for ZoomScrollView and NSForm instances in GraphPaper window

  1. Then select the NSForm (xmin, xmax, etc.) and make its Autosizing box look like the one on the right in Figure 19-6.

  2. Build and run GraphPaper, saving the nib file. Note how quickly the building process took this time — PB only had to stuff a new nib file in the .app directory, not much work.

  3. Graph a function and resize GraphPaper. It works better but is still not perfect. See Section 19.6 at the end of this chapter for more.

  4. Quit GraphPaper.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.202.240