Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
MyPDFView.m
// ====================================================================================================================== |
// MyPDFView.m |
// ====================================================================================================================== |
#import "MyPDFView.h" |
#import "MyWindowController.h" |
static NSRect RectPlusScale (NSRect aRect, float scale); |
@implementation MyPDFView |
// ============================================================================================================ MyPDFView |
// ------------------------------------------------------------------------------------------------------------- drawPage |
- (void) drawPage: (PDFPage *) pdfPage |
{ |
// Let PDFView do most of the hard work. |
[super drawPage: pdfPage]; |
if ([(MyWindowController *)[[self window] windowController] linkMode] == kLinkEditMode) |
{ |
NSArray *allAnnotations; |
allAnnotations = [pdfPage annotations]; |
if (allAnnotations) |
{ |
unsigned int count; |
unsigned int i; |
BOOL foundActive = NO; |
[self transformContextForPage: pdfPage]; |
count = [allAnnotations count]; |
for (i = 0; i < count; i++) |
{ |
PDFAnnotation *annotation; |
annotation = [allAnnotations objectAtIndex: i]; |
if ([[annotation type] isEqualToString: @"Link"]) |
{ |
if (annotation == _activeAnnotation) |
{ |
foundActive = YES; |
} |
else |
{ |
NSRect bounds; |
NSBezierPath *path; |
bounds = [annotation bounds]; |
path = [NSBezierPath bezierPathWithRect: bounds]; |
[path setLineJoinStyle: NSRoundLineJoinStyle]; |
[[NSColor colorWithDeviceWhite: 0.0 alpha: 0.1] set]; |
[path fill]; |
[[NSColor grayColor] set]; |
[path stroke]; |
} |
} |
} |
// Draw active annotation last so it is not "painted" over. |
if (foundActive) |
{ |
NSRect bounds; |
NSBezierPath *path; |
bounds = [_activeAnnotation bounds]; |
path = [NSBezierPath bezierPathWithRect: bounds]; |
[path setLineJoinStyle: NSRoundLineJoinStyle]; |
[[NSColor colorWithDeviceRed: 1.0 green: 0.0 blue: 0.0 alpha: 0.1] set]; |
[path fill]; |
[[NSColor redColor] set]; |
[path stroke]; |
// Draw resize handle. |
NSRectFill(NSIntegralRect([self resizeThumbForRect: bounds rotation: [pdfPage rotation]])); |
} |
} |
} |
} |
// ---------------------------------------------------------------------------------------------- transformContextForPage |
- (void) transformContextForPage: (PDFPage *) page |
{ |
NSAffineTransform *transform; |
NSRect boxRect; |
boxRect = [page boundsForBox: [self displayBox]]; |
transform = [NSAffineTransform transform]; |
[transform translateXBy: -boxRect.origin.x yBy: -boxRect.origin.y]; |
[transform concat]; |
} |
#pragma mark -------- event overrides |
// ------------------------------------------------------------------------------------------- setCursorForAreaOfInterest |
- (void) setCursorForAreaOfInterest: (PDFAreaOfInterest) area |
{ |
NSPoint viewMouse; |
BOOL overDocument; |
// Get mouse in document view coordinates. |
viewMouse = [[self documentView] convertPoint: [[NSApp currentEvent] locationInWindow] fromView: NULL]; |
overDocument = [[self documentView] mouse: viewMouse inRect: [[self documentView] visibleRect]]; |
if (overDocument == NO) |
{ |
[[NSCursor arrowCursor] set]; |
return; |
} |
// Handle link-edit mode. |
if ([(MyWindowController *)[[self window] windowController] linkMode] == kLinkEditMode) |
[[NSCursor arrowCursor] set]; |
else |
[super setCursorForAreaOfInterest: area]; |
} |
// ------------------------------------------------------------------------------------------------------------ mouseDown |
- (void) mouseDown: (NSEvent *) theEvent |
{ |
// Defer to super for locked PDF. |
if ([[self document] isLocked]) |
{ |
[super mouseDown: theEvent]; |
return; |
} |
// Handle link-edit mode. |
if ([(MyWindowController *)[[self window] windowController] linkMode] == kLinkEditMode) |
{ |
PDFAnnotation *newActiveAnnotation = NULL; |
PDFAnnotation *wasActiveAnnotation; |
NSArray *annotations; |
int numAnnotations, i; |
NSPoint pagePoint; |
BOOL newActive; |
// Mouse in display view coordinates. |
_mouseDownLoc = [self convertPoint: [theEvent locationInWindow] fromView: NULL]; |
// Page we're on. |
_activePage = [self pageForPoint: _mouseDownLoc nearest: YES]; |
// Get mouse in "page space". |
pagePoint = [self convertPoint: _mouseDownLoc toPage: _activePage]; |
// Hit test for annotation. |
annotations = [_activePage annotations]; |
numAnnotations = [annotations count]; |
for (i = 0; i < numAnnotations; i++) |
{ |
NSRect annotationBounds; |
// Hit test annotation. |
annotationBounds = [[annotations objectAtIndex: i] bounds]; |
if (NSPointInRect(pagePoint, annotationBounds)) |
{ |
PDFAnnotation *annotationHit; |
// A link annotation? |
annotationHit = [annotations objectAtIndex: i]; |
if ([[annotationHit type] isEqualToString: @"Link"]) |
{ |
// We count this one. |
newActiveAnnotation = annotationHit; |
// Remember click point relative to annotation origin. |
_clickDelta.x = pagePoint.x - annotationBounds.origin.x; |
_clickDelta.y = pagePoint.y - annotationBounds.origin.y; |
break; |
} |
} |
} |
// Flag indicating if _activeAnnotation will change. |
newActive = (_activeAnnotation != newActiveAnnotation); |
// Deselect old annotation when appropriate. |
if ((_activeAnnotation != NULL) && (newActive)) |
{ |
[self setNeedsDisplayInRect: RectPlusScale([self convertRect: [_activeAnnotation bounds] |
fromPage: [_activeAnnotation page]], [self scaleFactor])]; |
} |
// Assign. |
wasActiveAnnotation = _activeAnnotation; |
_activeAnnotation = (PDFAnnotationLink *)newActiveAnnotation; |
if (newActive) |
{ |
// Notification (MyWindowController listens for this). |
if ((wasActiveAnnotation != NULL) && (_activeAnnotation == NULL)) |
{ |
[[NSNotificationCenter defaultCenter] postNotificationName: @"newActiveAnnotation" object: self |
userInfo: [NSDictionary dictionaryWithObjectsAndKeys: |
wasActiveAnnotation, @"wasActiveAnnotation", NULL]]; |
} |
else if ((_activeAnnotation != NULL) && (wasActiveAnnotation == NULL)) |
{ |
[[NSNotificationCenter defaultCenter] postNotificationName: @"newActiveAnnotation" object: self |
userInfo: [NSDictionary dictionaryWithObjectsAndKeys: |
_activeAnnotation, @"activeAnnotation", NULL]]; |
} |
else |
{ |
[[NSNotificationCenter defaultCenter] postNotificationName: @"newActiveAnnotation" object: self |
userInfo: [NSDictionary dictionaryWithObjectsAndKeys: _activeAnnotation, @"activeAnnotation", |
wasActiveAnnotation, @"wasActiveAnnotation", NULL]]; |
} |
} |
if (_activeAnnotation == NULL) |
{ |
[super mouseDown: theEvent]; |
} |
else |
{ |
// Old (current) annotation location. |
_wasBounds = [_activeAnnotation bounds]; |
// Force redisplay. |
[self setNeedsDisplayInRect: RectPlusScale([self convertRect: [_activeAnnotation bounds] |
fromPage: _activePage], [self scaleFactor])]; |
_mouseDownInAnnotation = YES; |
// Hit-test for resize box. |
_resizing = NSPointInRect(pagePoint, [self resizeThumbForRect: _wasBounds rotation: [_activePage rotation]]); |
} |
} |
else |
{ |
[super mouseDown: theEvent]; |
} |
} |
// --------------------------------------------------------------------------------------------------------- mouseDragged |
- (void) mouseDragged: (NSEvent *) theEvent |
{ |
// Defer to super for locked PDF. |
if ([[self document] isLocked]) |
{ |
[super mouseDown: theEvent]; |
return; |
} |
_dragging = YES; |
// Handle link-edit mode. |
if (_mouseDownInAnnotation) |
{ |
NSRect newBounds; |
NSRect currentBounds; |
NSRect dirtyRect; |
NSPoint mouseLoc; |
NSPoint endPt; |
// Where is annotation now? |
currentBounds = [_activeAnnotation bounds]; |
// Mouse in display view coordinates. |
mouseLoc = [self convertPoint: [theEvent locationInWindow] fromView: NULL]; |
// Convert end point to page space. |
endPt = [self convertPoint: mouseLoc toPage: _activePage]; |
if (_resizing) |
{ |
NSPoint startPoint; |
// Convert start point to page space. |
startPoint = [self convertPoint: _mouseDownLoc toPage: _activePage]; |
// Resize the annotation. |
switch ([_activePage rotation]) |
{ |
case 0: |
newBounds.origin.x = _wasBounds.origin.x; |
newBounds.origin.y = _wasBounds.origin.y + (endPt.y - startPoint.y); |
newBounds.size.width = _wasBounds.size.width + (endPt.x - startPoint.x); |
newBounds.size.height = _wasBounds.size.height - (endPt.y - startPoint.y); |
break; |
case 90: |
newBounds.origin.x = _wasBounds.origin.x; |
newBounds.origin.y = _wasBounds.origin.y; |
newBounds.size.width = _wasBounds.size.width + (endPt.x - startPoint.x); |
newBounds.size.height = _wasBounds.size.height + (endPt.y - startPoint.y); |
break; |
case 180: |
newBounds.origin.x = _wasBounds.origin.x + (endPt.x - startPoint.x); |
newBounds.origin.y = _wasBounds.origin.y; |
newBounds.size.width = _wasBounds.size.width - (endPt.x - startPoint.x); |
newBounds.size.height = _wasBounds.size.height + (endPt.y - startPoint.y); |
break; |
case 270: |
newBounds.origin.x = _wasBounds.origin.x + (endPt.x - startPoint.x); |
newBounds.origin.y = _wasBounds.origin.y + (endPt.y - startPoint.y); |
newBounds.size.width = _wasBounds.size.width - (endPt.x - startPoint.x); |
newBounds.size.height = _wasBounds.size.height - (endPt.y - startPoint.y); |
break; |
} |
// Keep integer. |
newBounds = NSIntegralRect(newBounds); |
} |
else |
{ |
// Move annotation. |
// Hit test, is mouse still within page bounds? |
if (NSPointInRect([self convertPoint: mouseLoc toPage: _activePage], |
[_activePage boundsForBox: [self displayBox]])) |
{ |
// Calculate new bounds for annotation. |
newBounds = currentBounds; |
newBounds.origin.x = roundf(endPt.x - _clickDelta.x); |
newBounds.origin.y = roundf(endPt.y - _clickDelta.y); |
} |
else |
{ |
// Snap back to initial location. |
newBounds = _wasBounds; |
} |
} |
// Change annotation's location. |
[_activeAnnotation setBounds: newBounds]; |
// Force redraw. |
dirtyRect = NSUnionRect(currentBounds, newBounds); |
[self setNeedsDisplayInRect: |
RectPlusScale([self convertRect: dirtyRect fromPage: _activePage], [self scaleFactor])]; |
} |
else |
{ |
[super mouseDragged: theEvent]; |
} |
} |
// -------------------------------------------------------------------------------------------------------------- mouseUp |
- (void) mouseUp: (NSEvent *) theEvent |
{ |
// Defer to super for locked PDF. |
if ([[self document] isLocked]) |
{ |
[super mouseDown: theEvent]; |
return; |
} |
_dragging = NO; |
// Handle link-edit mode. |
if (_mouseDownInAnnotation) |
{ |
_mouseDownInAnnotation = NO; |
} |
else |
{ |
[super mouseUp: theEvent]; |
} |
} |
// -------------------------------------------------------------------------------------------------------------- keyDown |
- (void) keyDown: (NSEvent *) theEvent |
{ |
unichar oneChar; |
unsigned int theModifiers; |
BOOL noModifier; |
// Get the character from the keyDown event. |
oneChar = [[theEvent charactersIgnoringModifiers] characterAtIndex: 0]; |
theModifiers = [theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask; |
noModifier = ((theModifiers & (NSShiftKeyMask | NSControlKeyMask | NSAlternateKeyMask)) == 0); |
// Delete? |
if ((oneChar == NSDeleteCharacter) || (oneChar == NSDeleteFunctionKey)) |
[self delete: self]; |
else |
[super keyDown: theEvent]; |
} |
// --------------------------------------------------------------------------------------------------------------- delete |
- (void) delete: (id) sender |
{ |
if (_activeAnnotation != NULL) |
{ |
PDFAnnotationLink *wasAnnotation; |
wasAnnotation = _activeAnnotation; |
[self setActiveAnnotation: NULL]; |
[[wasAnnotation page] removeAnnotation: wasAnnotation]; |
// Set edited flag. |
[[self window] setDocumentEdited: YES]; |
} |
} |
#pragma mark -------- menu actions |
// -------------------------------------------------------------------------------------------------------- printDocument |
- (void) printDocument: (id) sender |
{ |
// Let PDFView handle the printing. |
[super printWithInfo: [NSPrintInfo sharedPrintInfo] autoRotate: YES]; |
return; |
} |
// ------------------------------------------------------------------------------------------------------- saveDocumentAs |
- (void) copy: (id) sender |
{ |
// Put PDF and TIFF data on the Pasteboard if no text selected. |
if ([self currentSelection] == NULL) |
{ |
NSData *pageData; |
NSImage *image; |
// Get PDF data for single (current) page. |
pageData = [[self currentPage] dataRepresentation]; |
// Create NSImage from PDF data. |
image = [[[NSImage alloc] initWithData: pageData] autorelease]; |
// Types to pasteboard. |
[[NSPasteboard generalPasteboard] declareTypes: [NSArray arrayWithObjects: NSPDFPboardType, NSTIFFPboardType, |
NULL] owner: NULL]; |
// Assign data. |
[[NSPasteboard generalPasteboard] setData: pageData forType: NSPDFPboardType]; |
[[NSPasteboard generalPasteboard] setData: [image TIFFRepresentationUsingCompression: NSTIFFCompressionLZW |
factor: 0 ] forType: NSTIFFPboardType]; |
} |
else |
{ |
// Default behavior (PDFView will handle the text case for free). |
[super copy: sender]; |
} |
} |
#pragma mark -------- accessors |
// ----------------------------------------------------------------------------------------------------- activeAnnotation |
- (PDFAnnotationLink *) activeAnnotation |
{ |
return _activeAnnotation; |
} |
// -------------------------------------------------------------------------------------------------- setActiveAnnotation |
- (void) setActiveAnnotation: (PDFAnnotationLink *) newLink; |
{ |
BOOL linkChange; |
// Change? |
linkChange = newLink != _activeAnnotation; |
// Will need to redraw old active anotation. |
if (_activeAnnotation != NULL) |
{ |
[self setNeedsDisplayInRect: RectPlusScale([self convertRect: [_activeAnnotation bounds] fromPage: |
[_activeAnnotation page]], [self scaleFactor])]; |
} |
// Assign. |
if (newLink) |
{ |
_activeAnnotation = newLink; |
_activePage = [newLink page]; |
// Force redisplay. |
[self setNeedsDisplayInRect: RectPlusScale([self convertRect: [_activeAnnotation bounds] fromPage: _activePage], |
[self scaleFactor])]; |
} |
else |
{ |
_activeAnnotation = NULL; |
_activePage = NULL; |
} |
if (linkChange) |
{ |
// Notification (MyWindowController listens for this). |
[[NSNotificationCenter defaultCenter] postNotificationName: @"newActiveAnnotation" object: self |
userInfo: NULL]; |
} |
} |
// --------------------------------------------------------------------------------------------------- defaultNewLinkSize |
- (NSSize) defaultNewLinkSize |
{ |
return NSMakeSize(180.0, 16.0); |
} |
// --------------------------------------------------------------------------------------------------- resizeThumbForRect |
- (NSRect) resizeThumbForRect: (NSRect) rect rotation: (int) rotation |
{ |
NSRect thumb; |
// Start with rect. |
thumb = rect; |
// Use rotation to determine thumb origin. |
switch (rotation) |
{ |
case 0: |
thumb.origin.x += rect.size.width - 8.0; |
break; |
case 90: |
thumb.origin.x += rect.size.width - 8.0; |
thumb.origin.y += rect.size.height - 8.0; |
break; |
case 180: |
thumb.origin.y += rect.size.height - 8.0; |
break; |
} |
thumb.size.width = 8.0; |
thumb.size.height = 8.0; |
return thumb; |
} |
@end |
// -------------------------------------------------------------------------------------------------------- RectPlusScale |
static NSRect RectPlusScale (NSRect aRect, float scale) |
{ |
float maxX; |
float maxY; |
NSPoint origin; |
// Determine edges. |
maxX = ceilf(aRect.origin.x + aRect.size.width) + scale; |
maxY = ceilf(aRect.origin.y + aRect.size.height) + scale; |
origin.x = floorf(aRect.origin.x) - scale; |
origin.y = floorf(aRect.origin.y) - scale; |
return NSMakeRect(origin.x, origin.y, maxX - origin.x, maxY - origin.y); |
} |
Copyright © 2005 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2005-08-10