Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial import of LinkBack 1.0 alpha from <http://linkbackproject.org…

  • Loading branch information...
commit 544c9ac65304f28eeef71ad4842d789ee0fecda9 0 parents
Timothy J. Wood tjw authored
Showing with 15,775 additions and 0 deletions.
  1. +14 −0 .gitignore
  2. BIN  Documentation/Linkback Overview.pdf
  3. BIN  LiveSketch/Arrow.tiff
  4. BIN  LiveSketch/Circle.tiff
  5. BIN  LiveSketch/Cross.tiff
  6. BIN  LiveSketch/Curve.tiff
  7. +43 −0 LiveSketch/DocumentModel.subproj/SKTCircle.h
  8. +61 −0 LiveSketch/DocumentModel.subproj/SKTCircle.m
  9. +89 −0 LiveSketch/DocumentModel.subproj/SKTDrawDocument.h
  10. +980 −0 LiveSketch/DocumentModel.subproj/SKTDrawDocument.m
  11. +155 −0 LiveSketch/DocumentModel.subproj/SKTGraphic.h
  12. +715 −0 LiveSketch/DocumentModel.subproj/SKTGraphic.m
  13. +57 −0 LiveSketch/DocumentModel.subproj/SKTImage.h
  14. +248 −0 LiveSketch/DocumentModel.subproj/SKTImage.m
  15. +49 −0 LiveSketch/DocumentModel.subproj/SKTLine.h
  16. +160 −0 LiveSketch/DocumentModel.subproj/SKTLine.m
  17. +42 −0 LiveSketch/DocumentModel.subproj/SKTRectangle.h
  18. +61 −0 LiveSketch/DocumentModel.subproj/SKTRectangle.m
  19. +47 −0 LiveSketch/DocumentModel.subproj/SKTRenderingView.h
  20. +86 −0 LiveSketch/DocumentModel.subproj/SKTRenderingView.m
  21. +48 −0 LiveSketch/DocumentModel.subproj/SKTTextArea.h
  22. +377 −0 LiveSketch/DocumentModel.subproj/SKTTextArea.m
  23. BIN  LiveSketch/Draw2App.icns
  24. BIN  LiveSketch/Draw2App.tiff
  25. BIN  LiveSketch/Draw2File.icns
  26. BIN  LiveSketch/Draw2File.tiff
  27. +6 −0 LiveSketch/English.lproj/Credits.rtf
  28. +69 −0 LiveSketch/English.lproj/Draw2.nib/classes.nib
  29. +19 −0 LiveSketch/English.lproj/Draw2.nib/info.nib
  30. BIN  LiveSketch/English.lproj/Draw2.nib/objects.nib
  31. +13 −0 LiveSketch/English.lproj/DrawWindow.nib/classes.nib
  32. +16 −0 LiveSketch/English.lproj/DrawWindow.nib/info.nib
  33. BIN  LiveSketch/English.lproj/DrawWindow.nib/objects.nib
  34. +30 −0 LiveSketch/English.lproj/GridPanel.nib/classes.nib
  35. +16 −0 LiveSketch/English.lproj/GridPanel.nib/info.nib
  36. BIN  LiveSketch/English.lproj/GridPanel.nib/objects.nib
  37. BIN  LiveSketch/English.lproj/InfoPlist.strings
  38. +34 −0 LiveSketch/English.lproj/Inspector.nib/classes.nib
  39. +16 −0 LiveSketch/English.lproj/Inspector.nib/info.nib
  40. BIN  LiveSketch/English.lproj/Inspector.nib/objects.nib
  41. +93 −0 LiveSketch/English.lproj/Sketch.scriptTerminology
  42. +13 −0 LiveSketch/English.lproj/ToolPalette.nib/classes.nib
  43. +16 −0 LiveSketch/English.lproj/ToolPalette.nib/info.nib
  44. BIN  LiveSketch/English.lproj/ToolPalette.nib/objects.nib
  45. BIN  LiveSketch/Line.tiff
  46. BIN  LiveSketch/Pencil.tiff
  47. BIN  LiveSketch/Polygon.tiff
  48. +131 −0 LiveSketch/ReadMe.rtf
  49. BIN  LiveSketch/Rectangle.tiff
  50. +50 −0 LiveSketch/SKTDrawAppDelegate.h
  51. +108 −0 LiveSketch/SKTDrawAppDelegate.m
  52. +53 −0 LiveSketch/SKTDrawWindowController.h
  53. +119 −0 LiveSketch/SKTDrawWindowController.m
  54. +47 −0 LiveSketch/SKTFoundationExtras.h
  55. +58 −0 LiveSketch/SKTFoundationExtras.m
  56. +168 −0 LiveSketch/SKTGraphicView.h
  57. +1,300 −0 LiveSketch/SKTGraphicView.m
  58. +70 −0 LiveSketch/SKTGridPanelController.h
  59. +181 −0 LiveSketch/SKTGridPanelController.m
  60. +46 −0 LiveSketch/SKTGridView.h
  61. +57 −0 LiveSketch/SKTGridView.m
  62. +69 −0 LiveSketch/SKTInspectorController.h
  63. +291 −0 LiveSketch/SKTInspectorController.m
  64. +53 −0 LiveSketch/SKTToolPaletteController.h
  65. +117 −0 LiveSketch/SKTToolPaletteController.m
  66. +42 −0 LiveSketch/SKT_Prefix.h
  67. BIN  LiveSketch/Scribble.tiff
  68. +1,523 −0 LiveSketch/Sketch.pbxproj/project.pbxproj
  69. +120 −0 LiveSketch/Sketch.scriptSuite
  70. +42 −0 LiveSketch/Sketch_main.m
  71. BIN  LiveSketch/TextGraphic.tiff
  72. +22 −0 LiveSketch/ToDo.txt
  73. +100 −0 Read Me.rtf
  74. BIN  Source/English.lproj/InfoPlist.strings
  75. +24 −0 Source/Info.plist
  76. +133 −0 Source/LinkBack.h
  77. +321 −0 Source/LinkBack.m
  78. +394 −0 Source/LinkBack.xcode/project.pbxproj
  79. +66 −0 Source/LinkBackServer.h
  80. +147 −0 Source/LinkBackServer.m
  81. +7 −0 Source/LinkBack_Prefix.pch
  82. +3 −0  Source/main.c
  83. +16 −0 Source/version.plist
  84. +19 −0 TextEdit+LinkBack/Controller.h
  85. +280 −0 TextEdit+LinkBack/Controller.m
  86. +228 −0 TextEdit+LinkBack/Document.h
  87. +1,977 −0 TextEdit+LinkBack/Document.m
  88. +405 −0 TextEdit+LinkBack/DocumentReadWrite.m
  89. +13 −0 TextEdit+LinkBack/DocumentWindow.nib/classes.nib
  90. +16 −0 TextEdit+LinkBack/DocumentWindow.nib/info.nib
  91. BIN  TextEdit+LinkBack/DocumentWindow.nib/keyedobjects.nib
  92. BIN  TextEdit+LinkBack/Edit.icns
  93. +5 −0 TextEdit+LinkBack/Edit_main.m
  94. +57 −0 TextEdit+LinkBack/EncodingManager.h
  95. +356 −0 TextEdit+LinkBack/EncodingManager.m
  96. +11 −0 TextEdit+LinkBack/English.lproj/Credits.rtf
  97. +104 −0 TextEdit+LinkBack/English.lproj/Edit.nib/classes.nib
  98. +21 −0 TextEdit+LinkBack/English.lproj/Edit.nib/info.nib
  99. BIN  TextEdit+LinkBack/English.lproj/Edit.nib/keyedobjects.nib
  100. +13 −0 TextEdit+LinkBack/English.lproj/EncodingAccessory.nib/classes.nib
  101. +16 −0 TextEdit+LinkBack/English.lproj/EncodingAccessory.nib/info.nib
  102. BIN  TextEdit+LinkBack/English.lproj/EncodingAccessory.nib/objects.nib
  103. BIN  TextEdit+LinkBack/English.lproj/InfoPlist.strings
  104. BIN  TextEdit+LinkBack/English.lproj/LaunchTime.strings
  105. BIN  TextEdit+LinkBack/English.lproj/Localizable.strings
  106. +40 −0 TextEdit+LinkBack/English.lproj/Preferences.nib/classes.nib
  107. +16 −0 TextEdit+LinkBack/English.lproj/Preferences.nib/info.nib
  108. BIN  TextEdit+LinkBack/English.lproj/Preferences.nib/keyedobjects.nib
  109. +13 −0 TextEdit+LinkBack/English.lproj/RichTextDocumentFormatAccessory.nib/classes.nib
  110. +21 −0 TextEdit+LinkBack/English.lproj/RichTextDocumentFormatAccessory.nib/info.nib
  111. BIN  TextEdit+LinkBack/English.lproj/RichTextDocumentFormatAccessory.nib/keyedobjects.nib
  112. +19 −0 TextEdit+LinkBack/English.lproj/SelectEncodingsPanel.nib/classes.nib
  113. +16 −0 TextEdit+LinkBack/English.lproj/SelectEncodingsPanel.nib/info.nib
  114. BIN  TextEdit+LinkBack/English.lproj/SelectEncodingsPanel.nib/keyedobjects.nib
  115. BIN  TextEdit+LinkBack/English.lproj/ServicesMenu.strings
  116. BIN  TextEdit+LinkBack/English.lproj/ZoomValues.strings
  117. +23 −0 TextEdit+LinkBack/LinkBackTextAttachment.h
  118. +44 −0 TextEdit+LinkBack/LinkBackTextAttachment.m
  119. +15 −0 TextEdit+LinkBack/LinkBackTextView.h
  120. +66 −0 TextEdit+LinkBack/LinkBackTextView.m
  121. +23 −0 TextEdit+LinkBack/MultiplePageView.h
  122. +201 −0 TextEdit+LinkBack/MultiplePageView.m
  123. +74 −0 TextEdit+LinkBack/Preferences.h
  124. +348 −0 TextEdit+LinkBack/Preferences.m
  125. +245 −0 TextEdit+LinkBack/README.rtf
  126. +14 −0 TextEdit+LinkBack/ScalingScrollView.h
  127. +199 −0 TextEdit+LinkBack/ScalingScrollView.m
  128. +1,302 −0 TextEdit+LinkBack/TextEdit.pbproj/project.pbxproj
  129. +73 −0 TextEdit+LinkBack/TextEdit.scatterload
  130. +28 −0 TextEdit+LinkBack/TextEdit.scriptSuite
  131. +23 −0 TextEdit+LinkBack/TextEdit.scriptTerminology
  132. BIN  TextEdit+LinkBack/html.icns
  133. BIN  TextEdit+LinkBack/rtf.icns
  134. BIN  TextEdit+LinkBack/rtfd.icns
  135. BIN  TextEdit+LinkBack/txt.icns
14 .gitignore
@@ -0,0 +1,14 @@
+# xcode noise
+build
+*.pbxuser
+*.mode1v3
+*.mode2v3
+*.perspectivev3
+
+# old skool
+.svn
+CVS
+
+# osx noise
+.DS_Store
+profile
BIN  Documentation/Linkback Overview.pdf
Binary file not shown
BIN  LiveSketch/Arrow.tiff
Binary file not shown
BIN  LiveSketch/Circle.tiff
Binary file not shown
BIN  LiveSketch/Cross.tiff
Binary file not shown
BIN  LiveSketch/Curve.tiff
Binary file not shown
43 LiveSketch/DocumentModel.subproj/SKTCircle.h
@@ -0,0 +1,43 @@
+// SKTCircle.h
+// Sketch Example
+//
+
+#import <AppKit/AppKit.h>
+#import "SKTGraphic.h"
+
+@interface SKTCircle : SKTGraphic {}
+
+@end
+
+/*
+ IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. ("Apple") in
+ consideration of your agreement to the following terms, and your use, installation,
+ modification or redistribution of this Apple software constitutes acceptance of these
+ terms. If you do not agree with these terms, please do not use, install, modify or
+ redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and subject to these
+ terms, Apple grants you a personal, non-exclusive license, under Apple's copyrights in
+ this original Apple software (the "Apple Software"), to use, reproduce, modify and
+ redistribute the Apple Software, with or without modifications, in source and/or binary
+ forms; provided that if you redistribute the Apple Software in its entirety and without
+ modifications, you must retain this notice and the following text and disclaimers in all
+ such redistributions of the Apple Software. Neither the name, trademarks, service marks
+ or logos of Apple Computer, Inc. may be used to endorse or promote products derived from
+ the Apple Software without specific prior written permission from Apple. Except as expressly
+ stated in this notice, no other rights or licenses, express or implied, are granted by Apple
+ herein, including but not limited to any patent rights that may be infringed by your
+ derivative works or by other works in which the Apple Software may be incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES,
+ EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS
+ USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
+ REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND
+ WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
+ OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
61 LiveSketch/DocumentModel.subproj/SKTCircle.m
@@ -0,0 +1,61 @@
+// SKTCircle.m
+// Sketch Example
+//
+
+#import "SKTCircle.h"
+
+@implementation SKTCircle
+
+- (NSBezierPath *)bezierPath {
+ NSBezierPath *path = [NSBezierPath bezierPathWithOvalInRect:[self bounds]];
+
+ [path setLineWidth:[self strokeLineWidth]];
+
+ return path;
+}
+
+- (void)makeNaturalSize {
+ NSRect bounds = [self bounds];
+ if (bounds.size.width < bounds.size.height) {
+ bounds.size.height = bounds.size.width;
+ [self setBounds:bounds];
+ } else if (bounds.size.width > bounds.size.height) {
+ bounds.size.width = bounds.size.height;
+ [self setBounds:bounds];
+ }
+}
+
+@end
+
+/*
+ IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. ("Apple") in
+ consideration of your agreement to the following terms, and your use, installation,
+ modification or redistribution of this Apple software constitutes acceptance of these
+ terms. If you do not agree with these terms, please do not use, install, modify or
+ redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and subject to these
+ terms, Apple grants you a personal, non-exclusive license, under Apple's copyrights in
+ this original Apple software (the "Apple Software"), to use, reproduce, modify and
+ redistribute the Apple Software, with or without modifications, in source and/or binary
+ forms; provided that if you redistribute the Apple Software in its entirety and without
+ modifications, you must retain this notice and the following text and disclaimers in all
+ such redistributions of the Apple Software. Neither the name, trademarks, service marks
+ or logos of Apple Computer, Inc. may be used to endorse or promote products derived from
+ the Apple Software without specific prior written permission from Apple. Except as expressly
+ stated in this notice, no other rights or licenses, express or implied, are granted by Apple
+ herein, including but not limited to any patent rights that may be infringed by your
+ derivative works or by other works in which the Apple Software may be incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES,
+ EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS
+ USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
+ REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND
+ WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
+ OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
89 LiveSketch/DocumentModel.subproj/SKTDrawDocument.h
@@ -0,0 +1,89 @@
+// SKTDrawDocument.h
+// Sketch Example
+//
+
+#import <AppKit/AppKit.h>
+
+@class SKTGraphic;
+
+@class LinkBack ;
+
+@interface SKTDrawDocument : NSDocument {
+ @private
+ NSMutableArray *_graphics;
+ LinkBack* link ;
+}
+
+- (id)init;
+
+// support for live link
+- (void)closeLinkIfNeeded;
+- (id)initWithLinkBack:(LinkBack*)aLink ; // initing with this causes saving to pass edits through link. link will also be closed when document is closed.
+
+- (void)makeWindowControllers;
+
+- (NSDictionary *)drawDocumentDictionaryForGraphics:(NSArray *)graphics;
+- (NSData *)drawDocumentDataForGraphics:(NSArray *)graphics;
+- (NSDictionary *)drawDocumentDictionaryFromData:(NSData *)data;
+- (NSArray *)graphicsFromDrawDocumentDictionary:(NSDictionary *)doc;
+
+- (NSRect)boundsForGraphics:(NSArray *)graphics;
+- (NSRect)drawingBoundsForGraphics:(NSArray *)graphics;
+- (NSData *)TIFFRepresentationForGraphics:(NSArray *)graphics;
+- (NSData *)PDFRepresentationForGraphics:(NSArray *)graphics;
+
+- (NSData *)dataRepresentationOfType:(NSString *)type;
+- (BOOL)loadDataRepresentation:(NSData *)data ofType:(NSString *)type;
+
+- (void)printShowingPrintPanel:(BOOL)flag;
+
+- (NSArray *)graphics;
+- (void)setGraphics:(NSArray *)graphics;
+
+- (void)invalidateGraphic:(SKTGraphic *)graphic;
+
+- (void)insertGraphic:(SKTGraphic *)graphic atIndex:(unsigned)index;
+- (void)removeGraphicAtIndex:(unsigned)index;
+- (void)removeGraphic:(SKTGraphic *)graphic;
+- (void)moveGraphic:(SKTGraphic *)graphic toIndex:(unsigned)newIndex;
+
+- (NSSize)documentSize;
+ // Returns usable document size based on print info paper size and margins.
+
+@end
+
+extern NSString *SKTDrawDocumentType;
+
+
+/*
+ IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. ("Apple") in
+ consideration of your agreement to the following terms, and your use, installation,
+ modification or redistribution of this Apple software constitutes acceptance of these
+ terms. If you do not agree with these terms, please do not use, install, modify or
+ redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and subject to these
+ terms, Apple grants you a personal, non-exclusive license, under Apple's copyrights in
+ this original Apple software (the "Apple Software"), to use, reproduce, modify and
+ redistribute the Apple Software, with or without modifications, in source and/or binary
+ forms; provided that if you redistribute the Apple Software in its entirety and without
+ modifications, you must retain this notice and the following text and disclaimers in all
+ such redistributions of the Apple Software. Neither the name, trademarks, service marks
+ or logos of Apple Computer, Inc. may be used to endorse or promote products derived from
+ the Apple Software without specific prior written permission from Apple. Except as expressly
+ stated in this notice, no other rights or licenses, express or implied, are granted by Apple
+ herein, including but not limited to any patent rights that may be infringed by your
+ derivative works or by other works in which the Apple Software may be incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES,
+ EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS
+ USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
+ REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND
+ WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
+ OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
980 LiveSketch/DocumentModel.subproj/SKTDrawDocument.m
@@ -0,0 +1,980 @@
+// SKTDrawDocument.m
+// Sketch Example
+//
+
+#import "SKTDrawDocument.h"
+#import "SKTDrawWindowController.h"
+#import "SKTGraphic.h"
+#import "SKTRenderingView.h"
+#import "SKTRectangle.h"
+#import "SKTCircle.h"
+#import "SKTLine.h"
+#import "SKTTextArea.h"
+#import "SKTImage.h"
+#import <LinkBack/LinkBack.h>
+
+NSString *SKTDrawDocumentType = @"Apple Sketch Graphic Format";
+
+@implementation SKTDrawDocument
+
+// ...........................................................................
+// LinkBack Support
+// LIVELINK EDITS
+
+- (void)closeLinkIfNeeded
+{
+ if (link) {
+ [link setRepresentedObject: nil] ;
+ [link closeLink] ;
+ [link release] ;
+ link = nil ;
+ }
+}
+
+- (id)initWithLinkBack:(LinkBack*)aLink
+{
+ if (self = [self init]) {
+ link = [aLink retain] ;
+ [link setRepresentedObject: self] ;
+
+ // get graphics from link
+ id linkBackData = [[link pasteboard] propertyListForType: LinkBackPboardType] ;
+ id graphics = LinkBackGetAppData(linkBackData) ;
+ graphics = [self drawDocumentDictionaryFromData: graphics] ;
+ graphics = [self graphicsFromDrawDocumentDictionary: graphics] ;
+ [self setGraphics: graphics] ;
+ // fix up undo
+ [[self undoManager] removeAllActions] ;
+ [self updateChangeCount: NSChangeCleared] ;
+ }
+
+ return self ;
+}
+
+- (NSString*)displayName
+{
+ if (link) {
+ NSString* sourceName = [link sourceName] ;
+ NSString* ret = [NSString stringWithFormat: @"Graphics from %@", sourceName] ;
+ return ret ;
+ } else return [super displayName] ;
+}
+
+- (void)close
+{
+ [self closeLinkIfNeeded] ;
+ [super close] ;
+}
+
+- (void)saveDocument:(id)sender
+{
+ // if this document is a live link doc, return the updated document contents to the client. Otherwise, save like normal.
+ // This code is from the copy: method in SKGraphicsView. In a properly refactored version, this code could be shared.
+ if (link) {
+ NSArray *sel = [self graphics];
+ if ([sel count] > 0) {
+ NSPasteboard *pboard = [link pasteboard];
+ id dta ;
+
+ [pboard declareTypes:[NSArray arrayWithObjects:SKTDrawDocumentType, NSTIFFPboardType, NSPDFPboardType, nil] owner:nil];
+
+ dta = [self drawDocumentDataForGraphics: sel] ;
+ [pboard setData: dta forType:SKTDrawDocumentType];
+ [pboard setData:[self TIFFRepresentationForGraphics: sel] forType:NSTIFFPboardType];
+ [pboard setData:[self PDFRepresentationForGraphics: sel] forType:NSPDFPboardType];
+
+ // save the pboard data for LinkBack.
+ [pboard setPropertyList: MakeLinkBackData(@"sketch", dta) forType: LinkBackPboardType] ;
+
+ [link sendEdit] ;
+
+ // fix up undo
+ [[self undoManager] removeAllActions] ;
+ [self updateChangeCount: NSChangeCleared] ;
+
+ } else NSBeep() ;
+
+ } else [super saveDocument: sender] ;
+}
+
+// ...........................................................................
+// Other Sketch functions
+//
+
+- (id)init {
+ self = [super init];
+ if (self) {
+ _graphics = [[NSMutableArray allocWithZone:[self zone]] init];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [self closeLinkIfNeeded] ;
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+ [_graphics release];
+
+ [super dealloc];
+}
+
+- (void)makeWindowControllers {
+ SKTDrawWindowController *myController = [[SKTDrawWindowController allocWithZone:[self zone]] init];
+ [self addWindowController:myController];
+ [myController release];
+}
+
+static NSString *SKTGraphicsListKey = @"GraphicsList";
+static NSString *SKTDrawDocumentVersionKey = @"DrawDocumentVersion";
+static int SKTCurrentDrawDocumentVersion = 1;
+static NSString *SKTPrintInfoKey = @"PrintInfo";
+
+
+- (NSDictionary *)drawDocumentDictionaryForGraphics:(NSArray *)graphics {
+ NSMutableDictionary *doc = [NSMutableDictionary dictionary];
+ unsigned i, c = [graphics count];
+ NSMutableArray *graphicDicts = [NSMutableArray arrayWithCapacity:c];
+
+ for (i=0; i<c; i++) {
+ [graphicDicts addObject:[[graphics objectAtIndex:i] propertyListRepresentation]];
+ }
+ [doc setObject:graphicDicts forKey:SKTGraphicsListKey];
+ [doc setObject:[NSString stringWithFormat:@"%d", SKTCurrentDrawDocumentVersion] forKey:SKTDrawDocumentVersionKey];
+ [doc setObject:[NSArchiver archivedDataWithRootObject:[self printInfo]] forKey:SKTPrintInfoKey];
+
+ return doc;
+}
+
+- (NSData *)drawDocumentDataForGraphics:(NSArray *)graphics {
+ NSDictionary *doc = [self drawDocumentDictionaryForGraphics:graphics];
+ NSString *string = [doc description];
+ return [string dataUsingEncoding:NSASCIIStringEncoding];
+}
+
+- (NSDictionary *)drawDocumentDictionaryFromData:(NSData *)data {
+ NSString *string = [[NSString allocWithZone:[self zone]] initWithData:data encoding:NSASCIIStringEncoding];
+ NSDictionary *doc = [string propertyList];
+
+ [string release];
+
+ return doc;
+}
+
+- (NSArray *)graphicsFromDrawDocumentDictionary:(NSDictionary *)doc {
+ NSArray *graphicDicts = [doc objectForKey:SKTGraphicsListKey];
+ unsigned i, c = [graphicDicts count];
+ NSMutableArray *graphics = [NSMutableArray arrayWithCapacity:c];
+
+ for (i=0; i<c; i++) {
+ [graphics addObject:[SKTGraphic graphicWithPropertyListRepresentation:[graphicDicts objectAtIndex:i]]];
+ }
+
+ return graphics;
+}
+
+- (NSRect)boundsForGraphics:(NSArray *)graphics {
+ NSRect rect = NSZeroRect;
+ unsigned i, c = [graphics count];
+ for (i=0; i<c; i++) {
+ if (i==0) {
+ rect = [[graphics objectAtIndex:i] bounds];
+ } else {
+ rect = NSUnionRect(rect, [[graphics objectAtIndex:i] bounds]);
+ }
+ }
+ return rect;
+}
+
+- (NSRect)drawingBoundsForGraphics:(NSArray *)graphics {
+ NSRect rect = NSZeroRect;
+ unsigned i, c = [graphics count];
+ for (i=0; i<c; i++) {
+ if (i==0) {
+ rect = [[graphics objectAtIndex:i] drawingBounds];
+ } else {
+ rect = NSUnionRect(rect, [[graphics objectAtIndex:i] drawingBounds]);
+ }
+ }
+ return rect;
+}
+
+- (NSData *)TIFFRepresentationForGraphics:(NSArray *)graphics {
+ NSRect bounds = [self drawingBoundsForGraphics:graphics];
+ NSImage *image;
+ NSData *tiffData;
+ unsigned i = [graphics count];
+ NSAffineTransform *transform;
+ SKTGraphic *curGraphic;
+ NSGraphicsContext *currentContext;
+
+ if (NSIsEmptyRect(bounds)) {
+ return nil;
+ }
+ image = [[NSImage allocWithZone:[self zone]] initWithSize:bounds.size];
+ [image setFlipped:YES];
+ [image lockFocus];
+ // Get the context AFTER we lock focus
+ currentContext = [NSGraphicsContext currentContext];
+ transform = [NSAffineTransform transform];
+ [transform translateXBy:-bounds.origin.x yBy:-bounds.origin.y];
+ [transform concat];
+
+ while (i-- > 0) {
+ // The only reason a graphic knows what view it is drawing in is so that it can draw differently when being created or edited or selected. A nil view means to draw in the standard way.
+ curGraphic = [graphics objectAtIndex:i];
+ [currentContext saveGraphicsState];
+ [NSBezierPath clipRect:[curGraphic drawingBounds]];
+ [curGraphic drawInView:nil isSelected:NO];
+ [currentContext restoreGraphicsState];
+ }
+ [image unlockFocus];
+ tiffData = [image TIFFRepresentation];
+ [image release];
+ return tiffData;
+}
+
+- (NSData *)PDFRepresentationForGraphics:(NSArray *)graphics {
+ NSRect bounds = [self drawingBoundsForGraphics:graphics];
+ SKTRenderingView *view = [[SKTRenderingView allocWithZone:[self zone]] initWithFrame:NSMakeRect(0.0, 0.0, NSMaxX(bounds), NSMaxY(bounds)) graphics:graphics];
+ NSWindow *window = [[NSWindow allocWithZone:[self zone]] initWithContentRect:NSMakeRect(0.0, 0.0, NSMaxX(bounds), NSMaxY(bounds)) styleMask:NSBorderlessWindowMask backing:NSBackingStoreNonretained defer:NO];
+ NSPrintInfo *printInfo = [self printInfo];
+ NSMutableData *pdfData = [[NSMutableData allocWithZone:[self zone]] init];
+ NSPrintOperation *printOp;
+
+ [[window contentView] addSubview:view];
+ [view release];
+ printOp = [NSPrintOperation PDFOperationWithView:view insideRect:bounds toData:pdfData printInfo:printInfo];
+ [printOp setShowPanels:NO];
+
+ if ([printOp runOperation]) {
+ [pdfData autorelease];
+ } else {
+ [pdfData release];
+ pdfData = nil;
+ }
+ [window release];
+
+ return pdfData;
+}
+
+- (NSData *)dataRepresentationOfType:(NSString *)type {
+ if ([type isEqualToString:SKTDrawDocumentType]) {
+ return [self drawDocumentDataForGraphics:[self graphics]];
+ } else if ([type isEqualToString:NSTIFFPboardType]) {
+ return [self TIFFRepresentationForGraphics:[self graphics]];
+ } else if ([type isEqualToString:NSPDFPboardType]) {
+ return [self PDFRepresentationForGraphics:[self graphics]];
+ } else {
+ return nil;
+ }
+}
+
+- (BOOL)loadDataRepresentation:(NSData *)data ofType:(NSString *)type {
+ if ([type isEqualToString:SKTDrawDocumentType]) {
+ NSDictionary *doc = [self drawDocumentDictionaryFromData:data];
+ [self setGraphics:[self graphicsFromDrawDocumentDictionary:doc]];
+
+ data = [doc objectForKey:SKTPrintInfoKey];
+ if (data) {
+ NSPrintInfo *printInfo = [NSUnarchiver unarchiveObjectWithData:data];
+ if (printInfo) {
+ [self setPrintInfo:printInfo];
+ }
+ }
+
+ [[self undoManager] removeAllActions];
+
+ return YES;
+ } else {
+ return NO;
+ }
+}
+
+- (void)updateChangeCount:(NSDocumentChangeType)change {
+ // This clears the undo stack whenever we load or save.
+ [super updateChangeCount:change];
+ if (change == NSChangeCleared) {
+ [[self undoManager] removeAllActions];
+ }
+}
+
+- (NSWindow *)appropriateWindowForDocModalOperations {
+ NSArray *wcs = [self windowControllers];
+ unsigned i, c = [wcs count];
+ NSWindow *docWindow = nil;
+
+ for (i=0; i<c; i++) {
+ docWindow = [[wcs objectAtIndex:i] window];
+ if (docWindow) {
+ break;
+ }
+ }
+ return docWindow;
+}
+
+- (NSSize)documentSize {
+ NSPrintInfo *printInfo = [self printInfo];
+ NSSize paperSize = [printInfo paperSize];
+ paperSize.width -= ([printInfo leftMargin] + [printInfo rightMargin]);
+ paperSize.height -= ([printInfo topMargin] + [printInfo bottomMargin]);
+ return paperSize;
+}
+
+- (void)printShowingPrintPanel:(BOOL)flag {
+ NSSize paperSize = [self documentSize];
+ SKTRenderingView *view = [[SKTRenderingView allocWithZone:[self zone]] initWithFrame:NSMakeRect(0.0, 0.0, paperSize.width, paperSize.height) graphics:[self graphics]];
+ NSWindow *window = [[NSWindow allocWithZone:[self zone]] initWithContentRect:NSMakeRect(0.0, 0.0, paperSize.width, paperSize.height) styleMask:NSBorderlessWindowMask backing:NSBackingStoreNonretained defer:NO];
+ NSPrintInfo *printInfo = [self printInfo];
+ NSPrintOperation *printOp;
+ NSWindow *docWindow = [self appropriateWindowForDocModalOperations];;
+
+ [[window contentView] addSubview:view];
+ [view release];
+ printOp = [NSPrintOperation printOperationWithView:view printInfo:printInfo];
+ [printOp setShowPanels:flag];
+ [printOp setCanSpawnSeparateThread:YES];
+
+ if (docWindow) {
+ (void)[printOp runOperationModalForWindow:docWindow delegate:nil didRunSelector:NULL contextInfo:NULL];
+ } else {
+ (void)[printOp runOperation];
+ }
+
+ [window release];
+}
+
+- (void)setPrintInfo:(NSPrintInfo *)printInfo {
+ [[[self undoManager] prepareWithInvocationTarget:self] setPrintInfo:[self printInfo]];
+ [super setPrintInfo:printInfo];
+ [[self undoManager] setActionName:NSLocalizedStringFromTable(@"Change Print Info", @"UndoStrings", @"Action name for changing print info.")];
+ [[self windowControllers] makeObjectsPerformSelector:@selector(setUpGraphicView)];
+}
+
+- (NSArray *)graphics {
+ return _graphics;
+}
+
+- (void)setGraphics:(NSArray *)graphics {
+ unsigned i = [_graphics count];
+ while (i-- > 0) {
+ [self removeGraphicAtIndex:i];
+ }
+ i = [graphics count];
+ while (i-- > 0) {
+ [self insertGraphic:[graphics objectAtIndex:i] atIndex:0];
+ }
+}
+
+- (void)invalidateGraphic:(SKTGraphic *)graphic {
+ NSArray *windowControllers = [self windowControllers];
+
+ [windowControllers makeObjectsPerformSelector:@selector(invalidateGraphic:) withObject:graphic];
+}
+
+- (void)insertGraphic:(SKTGraphic *)graphic atIndex:(unsigned)index {
+ [[[self undoManager] prepareWithInvocationTarget:self] removeGraphicAtIndex:index];
+ [_graphics insertObject:graphic atIndex:index];
+ [graphic setDocument:self];
+ [self invalidateGraphic:graphic];
+}
+
+- (void)removeGraphicAtIndex:(unsigned)index {
+ id graphic = [[_graphics objectAtIndex:index] retain];
+ [_graphics removeObjectAtIndex:index];
+ [self invalidateGraphic:graphic];
+ [[[self undoManager] prepareWithInvocationTarget:self] insertGraphic:graphic atIndex:index];
+ [graphic release];
+}
+
+- (void)removeGraphic:(SKTGraphic *)graphic {
+ unsigned index = [_graphics indexOfObjectIdenticalTo:graphic];
+ if (index != NSNotFound) {
+ [self removeGraphicAtIndex:index];
+ }
+}
+
+- (void)moveGraphic:(SKTGraphic *)graphic toIndex:(unsigned)newIndex {
+ unsigned curIndex = [_graphics indexOfObjectIdenticalTo:graphic];
+ if (curIndex != newIndex) {
+ [[[self undoManager] prepareWithInvocationTarget:self] moveGraphic:graphic toIndex:((curIndex > newIndex) ? curIndex+1 : curIndex)];
+ if (curIndex < newIndex) {
+ newIndex--;
+ }
+ [graphic retain];
+ [_graphics removeObjectAtIndex:curIndex];
+ [_graphics insertObject:graphic atIndex:newIndex];
+ [graphic release];
+ [self invalidateGraphic:graphic];
+ }
+}
+
+@end
+
+@implementation SKTDrawDocument (SKTScriptingExtras)
+
+// These are methods that we probably wouldn't bother with if we weren't scriptable.
+
+// graphics and setGraphics: are already implemented above.
+
+- (void)addInGraphics:(SKTGraphic *)graphic {
+ [self insertGraphic:graphic atIndex:[[self graphics] count]];
+}
+
+- (void)insertInGraphics:(SKTGraphic *)graphic atIndex:(unsigned)index {
+ [self insertGraphic:graphic atIndex:index];
+}
+
+- (void)removeFromGraphicsAtIndex:(unsigned)index {
+ [self removeGraphicAtIndex:index];
+}
+
+- (void)replaceInGraphics:(SKTGraphic *)graphic atIndex:(unsigned)index {
+ [self removeGraphicAtIndex:index];
+ [self insertGraphic:graphic atIndex:index];
+}
+
+- (NSArray *)graphicsWithClass:(Class)theClass {
+ NSArray *graphics = [self graphics];
+ NSMutableArray *result = [NSMutableArray array];
+ unsigned i, c = [graphics count];
+ id curGraphic;
+
+ for (i=0; i<c; i++) {
+ curGraphic = [graphics objectAtIndex:i];
+ if ([curGraphic isKindOfClass:theClass]) {
+ [result addObject:curGraphic];
+ }
+ }
+ return result;
+}
+
+- (NSArray *)rectangles {
+ return [self graphicsWithClass:[SKTRectangle class]];
+}
+
+- (NSArray *)circles {
+ return [self graphicsWithClass:[SKTCircle class]];
+}
+
+- (NSArray *)lines {
+ return [self graphicsWithClass:[SKTLine class]];
+}
+
+- (NSArray *)textAreas {
+ return [self graphicsWithClass:[SKTTextArea class]];
+}
+
+- (NSArray *)images {
+ return [self graphicsWithClass:[SKTImage class]];
+}
+
+- (void)setRectangles:(NSArray *)rects {
+ // We won't allow wholesale setting of these subset keys.
+ [NSException raise:NSOperationNotSupportedForKeyException format:@"Setting 'rectangles' key is not supported."];
+}
+
+- (void)addInRectangles:(SKTGraphic *)graphic {
+ [self addInGraphics:graphic];
+}
+
+- (void)insertInRectangles:(SKTGraphic *)graphic atIndex:(unsigned)index {
+ // MF:!!! This is not going to be ideal. If we are being asked to, say, "make a new rectangle at after rectangle 2", we will be after rectangle 2, but we may be after some other stuff as well since we will be asked to insertInRectangles:atIndex:3...
+ NSArray *rects = [self rectangles];
+ if (index == [rects count]) {
+ [self addInGraphics:graphic];
+ } else {
+ NSArray *graphics = [self graphics];
+ int newIndex = [graphics indexOfObjectIdenticalTo:[rects objectAtIndex:index]];
+ if (newIndex != NSNotFound) {
+ [self insertGraphic:graphic atIndex:newIndex];
+ } else {
+ // Shouldn't happen.
+ [NSException raise:NSRangeException format:@"Could not find the given rectangle in the graphics."];
+ }
+ }
+}
+
+- (void)removeFromRectanglesAtIndex:(unsigned)index {
+ NSArray *rects = [self rectangles];
+ NSArray *graphics = [self graphics];
+ int newIndex = [graphics indexOfObjectIdenticalTo:[rects objectAtIndex:index]];
+ if (newIndex != NSNotFound) {
+ [self removeGraphicAtIndex:newIndex];
+ } else {
+ // Shouldn't happen.
+ [NSException raise:NSRangeException format:@"Could not find the given rectangle in the graphics."];
+ }
+}
+
+- (void)replaceInRectangles:(SKTGraphic *)graphic atIndex:(unsigned)index {
+ NSArray *rects = [self rectangles];
+ NSArray *graphics = [self graphics];
+ int newIndex = [graphics indexOfObjectIdenticalTo:[rects objectAtIndex:index]];
+ if (newIndex != NSNotFound) {
+ [self removeGraphicAtIndex:newIndex];
+ [self insertGraphic:graphic atIndex:newIndex];
+ } else {
+ // Shouldn't happen.
+ [NSException raise:NSRangeException format:@"Could not find the given rectangle in the graphics."];
+ }
+}
+
+- (void)setCircles:(NSArray *)circles {
+ // We won't allow wholesale setting of these subset keys.
+ [NSException raise:NSOperationNotSupportedForKeyException format:@"Setting 'circles' key is not supported."];
+}
+
+- (void)addInCircles:(SKTGraphic *)graphic {
+ [self addInGraphics:graphic];
+}
+
+- (void)insertInCircles:(SKTGraphic *)graphic atIndex:(unsigned)index {
+ // MF:!!! This is not going to be ideal. If we are being asked to, say, "make a new rectangle at after rectangle 2", we will be after rectangle 2, but we may be after some other stuff as well since we will be asked to insertInCircles:atIndex:3...
+ NSArray *circles = [self circles];
+ if (index == [circles count]) {
+ [self addInGraphics:graphic];
+ } else {
+ NSArray *graphics = [self graphics];
+ int newIndex = [graphics indexOfObjectIdenticalTo:[circles objectAtIndex:index]];
+ if (newIndex != NSNotFound) {
+ [self insertGraphic:graphic atIndex:newIndex];
+ } else {
+ // Shouldn't happen.
+ [NSException raise:NSRangeException format:@"Could not find the given circle in the graphics."];
+ }
+ }
+}
+
+- (void)removeFromCirclesAtIndex:(unsigned)index {
+ NSArray *circles = [self circles];
+ NSArray *graphics = [self graphics];
+ int newIndex = [graphics indexOfObjectIdenticalTo:[circles objectAtIndex:index]];
+ if (newIndex != NSNotFound) {
+ [self removeGraphicAtIndex:newIndex];
+ } else {
+ // Shouldn't happen.
+ [NSException raise:NSRangeException format:@"Could not find the given circle in the graphics."];
+ }
+}
+
+- (void)replaceInCircles:(SKTGraphic *)graphic atIndex:(unsigned)index {
+ NSArray *circles = [self circles];
+ NSArray *graphics = [self graphics];
+ int newIndex = [graphics indexOfObjectIdenticalTo:[circles objectAtIndex:index]];
+ if (newIndex != NSNotFound) {
+ [self removeGraphicAtIndex:newIndex];
+ [self insertGraphic:graphic atIndex:newIndex];
+ } else {
+ // Shouldn't happen.
+ [NSException raise:NSRangeException format:@"Could not find the given circle in the graphics."];
+ }
+}
+
+- (void)setLines:(NSArray *)lines {
+ // We won't allow wholesale setting of these subset keys.
+ [NSException raise:NSOperationNotSupportedForKeyException format:@"Setting 'lines' key is not supported."];
+}
+
+- (void)addInLines:(SKTGraphic *)graphic {
+ [self addInGraphics:graphic];
+}
+
+- (void)insertInLines:(SKTGraphic *)graphic atIndex:(unsigned)index {
+ // MF:!!! This is not going to be ideal. If we are being asked to, say, "make a new rectangle at after rectangle 2", we will be after rectangle 2, but we may be after some other stuff as well since we will be asked to insertInLines:atIndex:3...
+ NSArray *lines = [self lines];
+ if (index == [lines count]) {
+ [self addInGraphics:graphic];
+ } else {
+ NSArray *graphics = [self graphics];
+ int newIndex = [graphics indexOfObjectIdenticalTo:[lines objectAtIndex:index]];
+ if (newIndex != NSNotFound) {
+ [self insertGraphic:graphic atIndex:newIndex];
+ } else {
+ // Shouldn't happen.
+ [NSException raise:NSRangeException format:@"Could not find the given line in the graphics."];
+ }
+ }
+}
+
+- (void)removeFromLinesAtIndex:(unsigned)index {
+ NSArray *lines = [self lines];
+ NSArray *graphics = [self graphics];
+ int newIndex = [graphics indexOfObjectIdenticalTo:[lines objectAtIndex:index]];
+ if (newIndex != NSNotFound) {
+ [self removeGraphicAtIndex:newIndex];
+ } else {
+ // Shouldn't happen.
+ [NSException raise:NSRangeException format:@"Could not find the given line in the graphics."];
+ }
+}
+
+- (void)replaceInLines:(SKTGraphic *)graphic atIndex:(unsigned)index {
+ NSArray *lines = [self lines];
+ NSArray *graphics = [self graphics];
+ int newIndex = [graphics indexOfObjectIdenticalTo:[lines objectAtIndex:index]];
+ if (newIndex != NSNotFound) {
+ [self removeGraphicAtIndex:newIndex];
+ [self insertGraphic:graphic atIndex:newIndex];
+ } else {
+ // Shouldn't happen.
+ [NSException raise:NSRangeException format:@"Could not find the given line in the graphics."];
+ }
+}
+
+- (void)setTextAreas:(NSArray *)textAreas {
+ // We won't allow wholesale setting of these subset keys.
+ [NSException raise:NSOperationNotSupportedForKeyException format:@"Setting 'textAreas' key is not supported."];
+}
+
+- (void)addInTextAreas:(SKTGraphic *)graphic {
+ [self addInGraphics:graphic];
+}
+
+- (void)insertInTextAreas:(SKTGraphic *)graphic atIndex:(unsigned)index {
+ // MF:!!! This is not going to be ideal. If we are being asked to, say, "make a new rectangle at after rectangle 2", we will be after rectangle 2, but we may be after some other stuff as well since we will be asked to insertInTextAreas:atIndex:3...
+ NSArray *textAreas = [self textAreas];
+ if (index == [textAreas count]) {
+ [self addInGraphics:graphic];
+ } else {
+ NSArray *graphics = [self graphics];
+ int newIndex = [graphics indexOfObjectIdenticalTo:[textAreas objectAtIndex:index]];
+ if (newIndex != NSNotFound) {
+ [self insertGraphic:graphic atIndex:newIndex];
+ } else {
+ // Shouldn't happen.
+ [NSException raise:NSRangeException format:@"Could not find the given text area in the graphics."];
+ }
+ }
+}
+
+- (void)removeFromTextAreasAtIndex:(unsigned)index {
+ NSArray *textAreas = [self textAreas];
+ NSArray *graphics = [self graphics];
+ int newIndex = [graphics indexOfObjectIdenticalTo:[textAreas objectAtIndex:index]];
+ if (newIndex != NSNotFound) {
+ [self removeGraphicAtIndex:newIndex];
+ } else {
+ // Shouldn't happen.
+ [NSException raise:NSRangeException format:@"Could not find the given text area in the graphics."];
+ }
+}
+
+- (void)replaceInTextAreas:(SKTGraphic *)graphic atIndex:(unsigned)index {
+ NSArray *textAreas = [self textAreas];
+ NSArray *graphics = [self graphics];
+ int newIndex = [graphics indexOfObjectIdenticalTo:[textAreas objectAtIndex:index]];
+ if (newIndex != NSNotFound) {
+ [self removeGraphicAtIndex:newIndex];
+ [self insertGraphic:graphic atIndex:newIndex];
+ } else {
+ // Shouldn't happen.
+ [NSException raise:NSRangeException format:@"Could not find the given text area in the graphics."];
+ }
+}
+
+- (void)setImages:(NSArray *)images {
+ // We won't allow wholesale setting of these subset keys.
+ [NSException raise:NSOperationNotSupportedForKeyException format:@"Setting 'images' key is not supported."];
+}
+
+- (void)addInImages:(SKTGraphic *)graphic {
+ [self addInGraphics:graphic];
+}
+
+- (void)insertInImages:(SKTGraphic *)graphic atIndex:(unsigned)index {
+ // MF:!!! This is not going to be ideal. If we are being asked to, say, "make a new rectangle at after rectangle 2", we will be after rectangle 2, but we may be after some other stuff as well since we will be asked to insertInImages:atIndex:3...
+ NSArray *images = [self images];
+ if (index == [images count]) {
+ [self addInGraphics:graphic];
+ } else {
+ NSArray *graphics = [self graphics];
+ int newIndex = [graphics indexOfObjectIdenticalTo:[images objectAtIndex:index]];
+ if (newIndex != NSNotFound) {
+ [self insertGraphic:graphic atIndex:newIndex];
+ } else {
+ // Shouldn't happen.
+ [NSException raise:NSRangeException format:@"Could not find the given image in the graphics."];
+ }
+ }
+}
+
+- (void)removeFromImagesAtIndex:(unsigned)index {
+ NSArray *images = [self images];
+ NSArray *graphics = [self graphics];
+ int newIndex = [graphics indexOfObjectIdenticalTo:[images objectAtIndex:index]];
+ if (newIndex != NSNotFound) {
+ [self removeGraphicAtIndex:newIndex];
+ } else {
+ // Shouldn't happen.
+ [NSException raise:NSRangeException format:@"Could not find the given image in the graphics."];
+ }
+}
+
+- (void)replaceInImages:(SKTGraphic *)graphic atIndex:(unsigned)index {
+ NSArray *images = [self images];
+ NSArray *graphics = [self graphics];
+ int newIndex = [graphics indexOfObjectIdenticalTo:[images objectAtIndex:index]];
+ if (newIndex != NSNotFound) {
+ [self removeGraphicAtIndex:newIndex];
+ [self insertGraphic:graphic atIndex:newIndex];
+ } else {
+ // Shouldn't happen.
+ [NSException raise:NSRangeException format:@"Could not find the given image in the graphics."];
+ }
+}
+
+// The following "indicesOf..." methods are in support of scripting. They allow more flexible range and relative specifiers to be used with the different graphic keys of a SKTDrawDocument.
+// The scripting engine does not know about the fact that the "rectangles" key is really just a subset of the "graphics" key, so script code like "rectangles from circle 1 to line 4" don't make sense to it. But Sketch does know and can answer such questions itself, with a little work.
+- (NSArray *)indicesOfObjectsByEvaluatingRangeSpecifier:(NSRangeSpecifier *)rangeSpec {
+ NSString *key = [rangeSpec key];
+
+ if ([key isEqual:@"graphics"] || [key isEqual:@"rectangles"] || [key isEqual:@"circles"] || [key isEqual:@"lines"] || [key isEqual:@"textAreas"] || [key isEqual:@"images"]) {
+ // This is one of the keys we might want to deal with.
+ NSScriptObjectSpecifier *startSpec = [rangeSpec startSpecifier];
+ NSScriptObjectSpecifier *endSpec = [rangeSpec endSpecifier];
+ NSString *startKey = [startSpec key];
+ NSString *endKey = [endSpec key];
+ NSArray *graphics = [self graphics];
+
+ if ((startSpec == nil) && (endSpec == nil)) {
+ // We need to have at least one of these...
+ return nil;
+ }
+ if ([graphics count] == 0) {
+ // If there are no graphics, there can be no match. Just return now.
+ return [NSArray array];
+ }
+
+ if ((!startSpec || [startKey isEqual:@"graphics"] || [startKey isEqual:@"rectangles"] || [startKey isEqual:@"circles"] || [startKey isEqual:@"lines"] || [startKey isEqual:@"textAreas"] || [startKey isEqual:@"images"]) && (!endSpec || [endKey isEqual:@"graphics"] || [endKey isEqual:@"rectangles"] || [endKey isEqual:@"circles"] || [endKey isEqual:@"lines"] || [endKey isEqual:@"textAreas"] || [endKey isEqual:@"images"])) {
+ int startIndex;
+ int endIndex;
+
+ // The start and end keys are also ones we want to handle.
+
+ // The strategy here is going to be to find the index of the start and stop object in the full graphics array, regardless of what its key is. Then we can find what we're looking for in that range of the graphics key (weeding out objects we don't want, if necessary).
+
+ // First find the index of the first start object in the graphics array
+ if (startSpec) {
+ id startObject = [startSpec objectsByEvaluatingSpecifier];
+ if ([startObject isKindOfClass:[NSArray class]]) {
+ if ([startObject count] == 0) {
+ startObject = nil;
+ } else {
+ startObject = [startObject objectAtIndex:0];
+ }
+ }
+ if (!startObject) {
+ // Oops. We could not find the start object.
+ return nil;
+ }
+ startIndex = [graphics indexOfObjectIdenticalTo:startObject];
+ if (startIndex == NSNotFound) {
+ // Oops. We couldn't find the start object in the graphics array. This should not happen.
+ return nil;
+ }
+ } else {
+ startIndex = 0;
+ }
+
+ // Now find the index of the last end object in the graphics array
+ if (endSpec) {
+ id endObject = [endSpec objectsByEvaluatingSpecifier];
+ if ([endObject isKindOfClass:[NSArray class]]) {
+ unsigned endObjectsCount = [endObject count];
+ if (endObjectsCount == 0) {
+ endObject = nil;
+ } else {
+ endObject = [endObject objectAtIndex:(endObjectsCount-1)];
+ }
+ }
+ if (!endObject) {
+ // Oops. We could not find the end object.
+ return nil;
+ }
+ endIndex = [graphics indexOfObjectIdenticalTo:endObject];
+ if (endIndex == NSNotFound) {
+ // Oops. We couldn't find the end object in the graphics array. This should not happen.
+ return nil;
+ }
+ } else {
+ endIndex = [graphics count] - 1;
+ }
+
+ if (endIndex < startIndex) {
+ // Accept backwards ranges gracefully
+ int temp = endIndex;
+ endIndex = startIndex;
+ startIndex = temp;
+ }
+
+ {
+ // Now startIndex and endIndex specify the end points of the range we want within the graphics array.
+ // We will traverse the range and pick the objects we want.
+ // We do this by getting each object and seeing if it actually appears in the real key that we are trying to evaluate in.
+ NSMutableArray *result = [NSMutableArray array];
+ BOOL keyIsGraphics = [key isEqual:@"graphics"];
+ NSArray *rangeKeyObjects = (keyIsGraphics ? nil : [self valueForKey:key]);
+ id curObj;
+ unsigned curKeyIndex, i;
+
+ for (i=startIndex; i<=endIndex; i++) {
+ if (keyIsGraphics) {
+ [result addObject:[NSNumber numberWithInt:i]];
+ } else {
+ curObj = [graphics objectAtIndex:i];
+ curKeyIndex = [rangeKeyObjects indexOfObjectIdenticalTo:curObj];
+ if (curKeyIndex != NSNotFound) {
+ [result addObject:[NSNumber numberWithInt:curKeyIndex]];
+ }
+ }
+ }
+ return result;
+ }
+ }
+ }
+ return nil;
+}
+
+- (NSArray *)indicesOfObjectsByEvaluatingRelativeSpecifier:(NSRelativeSpecifier *)relSpec {
+ NSString *key = [relSpec key];
+
+ if ([key isEqual:@"graphics"] || [key isEqual:@"rectangles"] || [key isEqual:@"circles"] || [key isEqual:@"lines"] || [key isEqual:@"textAreas"] || [key isEqual:@"images"]) {
+ // This is one of the keys we might want to deal with.
+ NSScriptObjectSpecifier *baseSpec = [relSpec baseSpecifier];
+ NSString *baseKey = [baseSpec key];
+ NSArray *graphics = [self graphics];
+ NSRelativePosition relPos = [relSpec relativePosition];
+
+ if (baseSpec == nil) {
+ // We need to have one of these...
+ return nil;
+ }
+ if ([graphics count] == 0) {
+ // If there are no graphics, there can be no match. Just return now.
+ return [NSArray array];
+ }
+
+ if ([baseKey isEqual:@"graphics"] || [baseKey isEqual:@"rectangles"] || [baseKey isEqual:@"circles"] || [baseKey isEqual:@"lines"] || [baseKey isEqual:@"textAreas"] || [baseKey isEqual:@"images"]) {
+ int baseIndex;
+
+ // The base key is also one we want to handle.
+
+ // The strategy here is going to be to find the index of the base object in the full graphics array, regardless of what its key is. Then we can find what we're looking for before or after it.
+
+ // First find the index of the first or last base object in the graphics array
+ // Base specifiers are to be evaluated within the same container as the relative specifier they are the base of. That's this document.
+ id baseObject = [baseSpec objectsByEvaluatingWithContainers:self];
+ if ([baseObject isKindOfClass:[NSArray class]]) {
+ int baseCount = [baseObject count];
+ if (baseCount == 0) {
+ baseObject = nil;
+ } else {
+ if (relPos == NSRelativeBefore) {
+ baseObject = [baseObject objectAtIndex:0];
+ } else {
+ baseObject = [baseObject objectAtIndex:(baseCount-1)];
+ }
+ }
+ }
+ if (!baseObject) {
+ // Oops. We could not find the base object.
+ return nil;
+ }
+
+ baseIndex = [graphics indexOfObjectIdenticalTo:baseObject];
+ if (baseIndex == NSNotFound) {
+ // Oops. We couldn't find the base object in the graphics array. This should not happen.
+ return nil;
+ }
+
+ {
+ // Now baseIndex specifies the base object for the relative spec in the graphics array.
+ // We will start either right before or right after and look for an object that matches the type we want.
+ // We do this by getting each object and seeing if it actually appears in the real key that we are trying to evaluate in.
+ NSMutableArray *result = [NSMutableArray array];
+ BOOL keyIsGraphics = [key isEqual:@"graphics"];
+ NSArray *relKeyObjects = (keyIsGraphics ? nil : [self valueForKey:key]);
+ id curObj;
+ unsigned curKeyIndex, graphicCount = [graphics count];
+
+ if (relPos == NSRelativeBefore) {
+ baseIndex--;
+ } else {
+ baseIndex++;
+ }
+ while ((baseIndex >= 0) && (baseIndex < graphicCount)) {
+ if (keyIsGraphics) {
+ [result addObject:[NSNumber numberWithInt:baseIndex]];
+ break;
+ } else {
+ curObj = [graphics objectAtIndex:baseIndex];
+ curKeyIndex = [relKeyObjects indexOfObjectIdenticalTo:curObj];
+ if (curKeyIndex != NSNotFound) {
+ [result addObject:[NSNumber numberWithInt:curKeyIndex]];
+ break;
+ }
+ }
+ if (relPos == NSRelativeBefore) {
+ baseIndex--;
+ } else {
+ baseIndex++;
+ }
+ }
+
+ return result;
+ }
+ }
+ }
+ return nil;
+}
+
+- (NSArray *)indicesOfObjectsByEvaluatingObjectSpecifier:(NSScriptObjectSpecifier *)specifier {
+ // We want to handle some range and relative specifiers ourselves in order to support such things as "graphics from circle 3 to circle 5" or "circles from graphic 1 to graphic 10" or "circle before rectangle 3".
+ // Returning nil from this method will cause the specifier to try to evaluate itself using its default evaluation strategy.
+
+ if ([specifier isKindOfClass:[NSRangeSpecifier class]]) {
+ return [self indicesOfObjectsByEvaluatingRangeSpecifier:(NSRangeSpecifier *)specifier];
+ } else if ([specifier isKindOfClass:[NSRelativeSpecifier class]]) {
+ return [self indicesOfObjectsByEvaluatingRelativeSpecifier:(NSRelativeSpecifier *)specifier];
+ }
+
+
+ // If we didn't handle it, return nil so that the default object specifier evaluation will do it.
+ return nil;
+}
+
+
+@end
+
+
+/*
+ IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. ("Apple") in
+ consideration of your agreement to the following terms, and your use, installation,
+ modification or redistribution of this Apple software constitutes acceptance of these
+ terms. If you do not agree with these terms, please do not use, install, modify or
+ redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and subject to these
+ terms, Apple grants you a personal, non-exclusive license, under Apple's copyrights in
+ this original Apple software (the "Apple Software"), to use, reproduce, modify and
+ redistribute the Apple Software, with or without modifications, in source and/or binary
+ forms; provided that if you redistribute the Apple Software in its entirety and without
+ modifications, you must retain this notice and the following text and disclaimers in all
+ such redistributions of the Apple Software. Neither the name, trademarks, service marks
+ or logos of Apple Computer, Inc. may be used to endorse or promote products derived from
+ the Apple Software without specific prior written permission from Apple. Except as expressly
+ stated in this notice, no other rights or licenses, express or implied, are granted by Apple
+ herein, including but not limited to any patent rights that may be infringed by your
+ derivative works or by other works in which the Apple Software may be incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES,
+ EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS
+ USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
+ REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND
+ WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
+ OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
155 LiveSketch/DocumentModel.subproj/SKTGraphic.h
@@ -0,0 +1,155 @@
+// SKTGraphic.h
+// Sketch Example
+//
+
+#import <AppKit/AppKit.h>
+
+@class SKTGraphicView;
+@class SKTDrawDocument;
+
+enum {
+ NoKnob = 0,
+ UpperLeftKnob,
+ UpperMiddleKnob,
+ UpperRightKnob,
+ MiddleLeftKnob,
+ MiddleRightKnob,
+ LowerLeftKnob,
+ LowerMiddleKnob,
+ LowerRightKnob,
+};
+
+enum {
+ NoKnobsMask = 0,
+ UpperLeftKnobMask = 1 << UpperLeftKnob,
+ UpperMiddleKnobMask = 1 << UpperMiddleKnob,
+ UpperRightKnobMask = 1 << UpperRightKnob,
+ MiddleLeftKnobMask = 1 << MiddleLeftKnob,
+ MiddleRightKnobMask = 1 << MiddleRightKnob,
+ LowerLeftKnobMask = 1 << LowerLeftKnob,
+ LowerMiddleKnobMask = 1 << LowerMiddleKnob,
+ LowerRightKnobMask = 1 << LowerRightKnob,
+ AllKnobsMask = 0xffffffff,
+};
+
+extern NSString *SKTGraphicDidChangeNotification;
+
+@interface SKTGraphic : NSObject <NSCopying> {
+ @private
+ SKTDrawDocument *_document;
+ NSRect _bounds;
+ NSRect _origBounds;
+ float _lineWidth;
+ NSColor *_fillColor;
+ NSColor *_strokeColor;
+ struct __gFlags {
+ unsigned int drawsFill:1;
+ unsigned int drawsStroke:1;
+ unsigned int manipulatingBounds:1;
+ unsigned int _pad:29;
+ } _gFlags;
+}
+
+- (id)init;
+
+// ========================= Document accessors and conveniences =========================
+- (void)setDocument:(SKTDrawDocument *)document;
+- (SKTDrawDocument *)document;
+- (NSUndoManager *)undoManager;
+
+// =================================== Primitives ===================================
+- (void)didChange;
+ // This sends the did change notification. All change primitives should call it.
+
+- (void)setBounds:(NSRect)bounds;
+- (NSRect)bounds;
+- (void)setDrawsFill:(BOOL)flag;
+- (BOOL)drawsFill;
+- (void)setFillColor:(NSColor *)fillColor;
+- (NSColor *)fillColor;
+- (void)setDrawsStroke:(BOOL)flag;
+- (BOOL)drawsStroke;
+- (void)setStrokeColor:(NSColor *)strokeColor;
+- (NSColor *)strokeColor;
+- (void)setStrokeLineWidth:(float)width;
+- (float)strokeLineWidth;
+
+// =================================== Extended mutation ===================================
+- (void)startBoundsManipulation;
+- (void)stopBoundsManipulation;
+- (void)moveBy:(NSPoint)vector;
+- (void)flipHorizontally;
+- (void)flipVertically;
+- (int)resizeByMovingKnob:(int)knob toPoint:(NSPoint)point;
+- (void)makeNaturalSize;
+
+// =================================== Subclass capabilities ===================================
+- (BOOL)canDrawStroke;
+- (BOOL)canDrawFill;
+- (BOOL)hasNaturalSize;
+
+// =================================== Persistence ===================================
+- (NSMutableDictionary *)propertyListRepresentation;
++ (id)graphicWithPropertyListRepresentation:(NSDictionary *)dict;
+- (void)loadPropertyListRepresentation:(NSDictionary *)dict;
+
+@end
+
+@interface SKTGraphic (SKTDrawing)
+
+- (NSRect)drawingBounds;
+- (NSBezierPath *)bezierPath;
+- (void)drawInView:(SKTGraphicView *)view isSelected:(BOOL)flag;
+- (unsigned)knobMask;
+- (int)knobUnderPoint:(NSPoint)point;
+- (void)drawHandleAtPoint:(NSPoint)point inView:(SKTGraphicView *)view;
+- (void)drawHandlesInView:(SKTGraphicView *)view;
+
+@end
+
+@interface SKTGraphic (SKTEventHandling)
+
++ (NSCursor *)creationCursor;
+
+- (BOOL)createWithEvent:(NSEvent *)theEvent inView:(SKTGraphicView *)view;
+
+- (BOOL)isEditable;
+- (void)startEditingWithEvent:(NSEvent *)event inView:(SKTGraphicView *)view;
+- (void)endEditingInView:(SKTGraphicView *)view;
+
+- (BOOL)hitTest:(NSPoint)point isSelected:(BOOL)isSelected;
+
+@end
+
+/*
+ IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. ("Apple") in
+ consideration of your agreement to the following terms, and your use, installation,
+ modification or redistribution of this Apple software constitutes acceptance of these
+ terms. If you do not agree with these terms, please do not use, install, modify or
+ redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and subject to these
+ terms, Apple grants you a personal, non-exclusive license, under Apple's copyrights in
+ this original Apple software (the "Apple Software"), to use, reproduce, modify and
+ redistribute the Apple Software, with or without modifications, in source and/or binary
+ forms; provided that if you redistribute the Apple Software in its entirety and without
+ modifications, you must retain this notice and the following text and disclaimers in all
+ such redistributions of the Apple Software. Neither the name, trademarks, service marks
+ or logos of Apple Computer, Inc. may be used to endorse or promote products derived from
+ the Apple Software without specific prior written permission from Apple. Except as expressly
+ stated in this notice, no other rights or licenses, express or implied, are granted by Apple
+ herein, including but not limited to any patent rights that may be infringed by your
+ derivative works or by other works in which the Apple Software may be incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES,
+ EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS
+ USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
+ REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND
+ WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
+ OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
715 LiveSketch/DocumentModel.subproj/SKTGraphic.m
@@ -0,0 +1,715 @@
+// SKTGraphic.m
+// Sketch Example
+//
+
+#import "SKTGraphic.h"
+#import "SKTGraphicView.h"
+#import "SKTDrawDocument.h"
+#import "SKTFoundationExtras.h"
+
+NSString *SKTGraphicDidChangeNotification = @"SKTGraphicDidChange";
+
+@implementation SKTGraphic
+
+// =================================== Initialization ===================================
+- (id)init {
+ self = [super init];
+ if (self) {
+ _document = nil;
+ [self setBounds:NSMakeRect(0.0, 0.0, 1.0, 1.0)];
+ [self setFillColor:[NSColor whiteColor]];
+ [self setDrawsFill:NO];
+ [self setStrokeColor:[NSColor blackColor]];
+ [self setDrawsStroke:YES];
+ [self setStrokeLineWidth:1.0];
+ _origBounds = NSZeroRect;
+ _gFlags.manipulatingBounds = NO;
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [_fillColor release];
+ [_strokeColor release];
+ [super dealloc];
+}
+
+- (id)copyWithZone:(NSZone *)zone {
+ id newObj = [[[self class] allocWithZone:zone] init];
+
+ // Document is not "copied". The new graphic will need to be inserted into a document.
+ [newObj setBounds:[self bounds]];
+ [newObj setFillColor:[self fillColor]];
+ [newObj setDrawsFill:[self drawsFill]];
+ [newObj setStrokeColor:[self strokeColor]];
+ [newObj setDrawsStroke:[self drawsStroke]];
+ [newObj setStrokeLineWidth:[self strokeLineWidth]];
+
+ return newObj;
+}
+
+// ========================= Document accessors and conveniences =========================
+- (void)setDocument:(SKTDrawDocument *)document {
+ _document = document;
+}
+
+- (SKTDrawDocument *)document {
+ return _document;
+}
+
+- (NSUndoManager *)undoManager {
+ return [[self document] undoManager];
+}
+
+- (NSString *)graphicType {
+ return NSStringFromClass([self class]);
+}
+
+// =================================== Primitives ===================================
+- (void)didChange {
+ [_document invalidateGraphic:self];
+ [[NSNotificationCenter defaultCenter] postNotificationName:SKTGraphicDidChangeNotification object:self];
+}
+
+- (void)setBounds:(NSRect)bounds {
+ if (!NSEqualRects(bounds, _bounds)) {
+ if (!_gFlags.manipulatingBounds) {
+ // Send the notification before and after so that observers who invalidate display in views will wind up invalidating both the original rect and the new one.
+ [self didChange];
+ [[[self undoManager] prepareWithInvocationTarget:self] setBounds:_bounds];
+ }
+ _bounds = bounds;
+ if (!_gFlags.manipulatingBounds) {
+ [self didChange];
+ }
+ }
+}
+
+- (NSRect)bounds {
+ return _bounds;
+}
+
+- (void)setDrawsFill:(BOOL)flag {
+ if (_gFlags.drawsFill != flag) {
+ [[[self undoManager] prepareWithInvocationTarget:self] setDrawsFill:_gFlags.drawsFill];
+ _gFlags.drawsFill = (flag ? YES : NO);
+ [self didChange];
+ }
+}
+
+- (BOOL)drawsFill {
+ return _gFlags.drawsFill;
+}
+
+- (void)setFillColor:(NSColor *)fillColor {
+ if (_fillColor != fillColor) {
+ [[[self undoManager] prepareWithInvocationTarget:self] setFillColor:_fillColor];
+ [_fillColor autorelease];
+ _fillColor = [fillColor retain];
+ [self didChange];
+ }
+ if (_fillColor) {
+ [self setDrawsFill:YES];
+ } else {
+ [self setDrawsFill:NO];
+ }
+}
+
+- (NSColor *)fillColor {
+ return _fillColor;
+}
+
+- (void)setDrawsStroke:(BOOL)flag {
+ if (_gFlags.drawsStroke != flag) {
+ [[[self undoManager] prepareWithInvocationTarget:self] setDrawsStroke:_gFlags.drawsStroke];
+ _gFlags.drawsStroke = (flag ? YES : NO);
+ [self didChange];
+ }
+}
+
+- (BOOL)drawsStroke {
+ return _gFlags.drawsStroke;
+}
+
+- (void)setStrokeColor:(NSColor *)strokeColor {
+ if (_strokeColor != strokeColor) {
+ [[[self undoManager] prepareWithInvocationTarget:self] setStrokeColor:_strokeColor];
+ [_strokeColor autorelease];
+ _strokeColor = [strokeColor retain];
+ [self didChange];
+ }
+ if (_strokeColor) {
+ [self setDrawsStroke:YES];
+ } else {
+ [self setDrawsStroke:NO];
+ }
+}
+
+- (NSColor *)strokeColor {
+ return _strokeColor;
+}
+
+- (void)setStrokeLineWidth:(float)width {
+ if (_lineWidth != width) {
+ [[[self undoManager] prepareWithInvocationTarget:self] setStrokeLineWidth:_lineWidth];
+ if (width >= 0.0) {
+ [self setDrawsStroke:YES];
+ _lineWidth = width;
+ } else {
+ [self setDrawsStroke:NO];
+ _lineWidth = 0.0;
+ }
+ [self didChange];
+ }
+}
+
+- (float)strokeLineWidth {
+ return _lineWidth;
+}
+
+// =================================== Extended mutation ===================================
+- (void)startBoundsManipulation {
+ // Save the original bounds.
+ _gFlags.manipulatingBounds = YES;
+ _origBounds = _bounds;
+}
+
+- (void)stopBoundsManipulation {
+ if (_gFlags.manipulatingBounds) {
+ // Restore the original bounds, the set the new bounds.
+ if (!NSEqualRects(_origBounds, _bounds)) {
+ NSRect temp;
+
+ _gFlags.manipulatingBounds = NO;
+ temp = _bounds;
+ _bounds = _origBounds;
+ [self setBounds:temp];
+ } else {
+ _gFlags.manipulatingBounds = NO;
+ }
+ }
+}
+
+- (void)moveBy:(NSPoint)vector {
+ [self setBounds:NSOffsetRect([self bounds], vector.x, vector.y)];
+}
+
+- (void)flipHorizontally {
+ // Some subclasses need to know.
+ return;
+}
+
+- (void)flipVertically {
+ // Some subclasses need to know.
+ return;
+}
+
++ (int)flipKnob:(int)knob horizontal:(BOOL)horizFlag {
+ static BOOL initedFlips = NO;
+ static int horizFlips[9];
+ static int vertFlips[9];
+
+ if (!initedFlips) {
+ horizFlips[UpperLeftKnob] = UpperRightKnob;
+ horizFlips[UpperMiddleKnob] = UpperMiddleKnob;
+ horizFlips[UpperRightKnob] = UpperLeftKnob;
+ horizFlips[MiddleLeftKnob] = MiddleRightKnob;
+ horizFlips[MiddleRightKnob] = MiddleLeftKnob;
+ horizFlips[LowerLeftKnob] = LowerRightKnob;
+ horizFlips[LowerMiddleKnob] = LowerMiddleKnob;
+ horizFlips[LowerRightKnob] = LowerLeftKnob;
+
+ vertFlips[UpperLeftKnob] = LowerLeftKnob;
+ vertFlips[UpperMiddleKnob] = LowerMiddleKnob;
+ vertFlips[UpperRightKnob] = LowerRightKnob;
+ vertFlips[MiddleLeftKnob] = MiddleLeftKnob;
+ vertFlips[MiddleRightKnob] = MiddleRightKnob;
+ vertFlips[LowerLeftKnob] = UpperLeftKnob;
+ vertFlips[LowerMiddleKnob] = UpperMiddleKnob;
+ vertFlips[LowerRightKnob] = UpperRightKnob;
+ initedFlips = YES;
+ }
+ if (horizFlag) {
+ return horizFlips[knob];
+ } else {
+ return vertFlips[knob];
+ }
+}
+
+- (int)resizeByMovingKnob:(int)knob toPoint:(NSPoint)point {
+ NSRect bounds = [self bounds];
+
+ if ((knob == UpperLeftKnob) || (knob == MiddleLeftKnob) || (knob == LowerLeftKnob)) {
+ // Adjust left edge
+ bounds.size.width = NSMaxX(bounds) - point.x;
+ bounds.origin.x = point.x;
+ } else if ((knob == UpperRightKnob) || (knob == MiddleRightKnob) || (knob == LowerRightKnob)) {
+ // Adjust left edge
+ bounds.size.width = point.x - bounds.origin.x;
+ }
+ if (bounds.size.width < 0.0) {
+ knob = [SKTGraphic flipKnob:knob horizontal:YES];
+ bounds.size.width = -bounds.size.width;
+ bounds.origin.x -= bounds.size.width;
+ [self flipHorizontally];
+ }
+
+ if ((knob == UpperLeftKnob) || (knob == UpperMiddleKnob) || (knob == UpperRightKnob)) {
+ // Adjust top edge
+ bounds.size.height = NSMaxY(bounds) - point.y;
+ bounds.origin.y = point.y;
+ } else if ((knob == LowerLeftKnob) || (knob == LowerMiddleKnob) || (knob == LowerRightKnob)) {
+ // Adjust bottom edge
+ bounds.size.height = point.y - bounds.origin.y;
+ }
+ if (bounds.size.height < 0.0) {
+ knob = [SKTGraphic flipKnob:knob horizontal:NO];
+ bounds.size.height = -bounds.size.height;
+ bounds.origin.y -= bounds.size.height;
+ [self flipVertically];
+ }
+ [self setBounds:bounds];
+ return knob;
+}
+
+- (void)makeNaturalSize {
+ // Do nothing by default
+}
+
+// =================================== Subclass capabilities ===================================
+
+// Some subclasses will not ever have a stroke or fill or a natural size. Overriding these methods in such subclasses allows the Inspector and Menu items to better reflect allowable actions.
+
+- (BOOL)canDrawStroke {
+ return YES;
+}
+
+- (BOOL)canDrawFill {
+ return YES;
+}
+
+- (BOOL)hasNaturalSize {
+ return YES;
+}
+
+// =================================== Persistence ===================================
+NSString *SKTClassKey = @"Class";
+NSString *SKTBoundsKey = @"Bounds";
+NSString *SKTDrawsFillKey = @"DrawsFill";
+NSString *SKTFillColorKey = @"FillColor";
+NSString *SKTDrawsStrokeKey = @"DrawsStroke";
+NSString *SKTStrokeColorKey = @"StrokeColor";
+NSString *SKTStrokeLineWidthKey = @"StrokeLineWidth";
+
+- (NSMutableDictionary *)propertyListRepresentation {
+ NSMutableDictionary *dict = [NSMutableDictionary dictionary];
+ NSString *className = NSStringFromClass([self class]);
+ NSRange sktRange = [className rangeOfString:@"SKT" options:NSAnchoredSearch];
+
+ // Strip SKT prefix to preserve document capatibility with old versions of Sketch.
+ if (sktRange.location != NSNotFound) {
+ className = [className substringFromIndex:NSMaxRange(sktRange)];
+ }
+ [dict setObject:className forKey:SKTClassKey];
+ [dict setObject:NSStringFromRect([self bounds]) forKey:SKTBoundsKey];
+ [dict setObject:([self drawsFill] ? @"YES" : @"NO") forKey:SKTDrawsFillKey];
+ if ([self fillColor]) {
+ [dict setObject:[NSArchiver archivedDataWithRootObject:[self fillColor]] forKey:SKTFillColorKey];
+ }
+ [dict setObject:([self drawsStroke] ? @"YES" : @"NO") forKey:SKTDrawsStrokeKey];
+ if ([self strokeColor]) {
+ [dict setObject:[NSArchiver archivedDataWithRootObject:[self strokeColor]] forKey:SKTStrokeColorKey];
+ }
+ [dict setObject:[NSString stringWithFormat:@"%.2f", [self strokeLineWidth]] forKey:SKTStrokeLineWidthKey];
+
+ return dict;
+}
+
++ (id)graphicWithPropertyListRepresentation:(NSDictionary *)dict {
+ Class theClass = NSClassFromString([dict objectForKey:SKTClassKey]);
+ id theGraphic = nil;
+
+ // Prepend SKT to the class name if we did not find it literally. When we write the classname key we strip the prefix. We try it first without the prefix because for a short time Sketch did not strip the prefix so there could be documents that do not need it prepended.
+ if (!theClass) {
+ theClass = NSClassFromString([@"SKT" stringByAppendingString:[dict objectForKey:SKTClassKey]]);
+ }
+ if (theClass) {
+ theGraphic = [[[theClass allocWithZone:NULL] init] autorelease];
+ if (theGraphic) {
+ [theGraphic loadPropertyListRepresentation:dict];
+ }
+ }
+ return theGraphic;
+}
+
+- (void)loadPropertyListRepresentation:(NSDictionary *)dict {
+ id obj;
+
+ obj = [dict objectForKey:SKTBoundsKey];
+ if (obj) {
+ [self setBounds:NSRectFromString(obj)];
+ }
+ obj = [dict objectForKey:SKTFillColorKey];
+ if (obj) {
+ [self setFillColor:[NSUnarchiver unarchiveObjectWithData:obj]];
+ }
+ obj = [dict objectForKey:SKTDrawsFillKey];
+ if (obj) {
+ [self setDrawsFill:[obj isEqualToString:@"YES"]];
+ }
+ obj = [dict objectForKey:SKTStrokeColorKey];
+ if (obj) {
+ [self setStrokeColor:[NSUnarchiver unarchiveObjectWithData:obj]];
+ }
+ obj = [dict objectForKey:SKTStrokeLineWidthKey];
+ if (obj) {
+ [self setStrokeLineWidth:[obj floatValue]];
+ }
+ obj = [dict objectForKey:SKTDrawsStrokeKey];
+ if (obj) {
+ [self setDrawsStroke:[obj isEqualToString:@"YES"]];
+ }
+ return;
+}
+
+// =================================== Drawing ===================================
+- (NSRect)drawingBounds {
+ float inset = -SKT_HALF_HANDLE_WIDTH;
+ if ([self drawsStroke]) {
+ float halfLineWidth = ([self strokeLineWidth] / 2.0) + 1.0;
+ if (-halfLineWidth < inset) {
+ inset = -halfLineWidth;
+ }
+ }
+ inset += -1.0;
+ return NSInsetRect([self bounds], inset, inset);
+}
+
+- (NSBezierPath *)bezierPath {
+ // Subclasses that just have a simple path override this to return it. The basic drawInView:isSelected: implementation below will stroke and fill this path. Subclasses that need more complex drawing will just override drawInView:isSelected:.
+ return nil;
+}
+
+- (void)drawInView:(SKTGraphicView *)view isSelected:(BOOL)flag {
+ NSBezierPath *path = [self bezierPath];
+ if (path) {
+ if ([self drawsFill]) {
+ [[self fillColor] set];
+ [path fill];
+ }
+ if ([self drawsStroke]) {
+ [[self strokeColor] set];
+ [path stroke];
+ }
+ }
+ if (flag) {
+ [self drawHandlesInView:view];
+ }
+}
+
+- (unsigned)knobMask {
+ return AllKnobsMask;
+}
+
+- (int)knobUnderPoint:(NSPoint)point {
+ NSRect bounds = [self bounds];
+ unsigned knobMask = [self knobMask];
+ NSRect handleRect;
+
+ handleRect.size.width = SKT_HANDLE_WIDTH;
+ handleRect.size.height = SKT_HANDLE_WIDTH;
+
+ if (knobMask & UpperLeftKnobMask) {
+ handleRect.origin.x = NSMinX(bounds) - SKT_HALF_HANDLE_WIDTH;
+ handleRect.origin.y = NSMinY(bounds) - SKT_HALF_HANDLE_WIDTH;
+ if (NSPointInRect(point, handleRect)) {
+ return UpperLeftKnob;
+ }
+ }
+ if (knobMask & UpperMiddleKnobMask) {
+ handleRect.origin.x = NSMidX(bounds) - SKT_HALF_HANDLE_WIDTH;
+ handleRect.origin.y = NSMinY(bounds) - SKT_HALF_HANDLE_WIDTH;
+ if (NSPointInRect(point, handleRect)) {
+ return UpperMiddleKnob;
+ }
+ }
+ if (knobMask & UpperRightKnobMask) {
+ handleRect.origin.x = NSMaxX(bounds) - SKT_HALF_HANDLE_WIDTH;
+ handleRect.origin.y = NSMinY(bounds) - SKT_HALF_HANDLE_WIDTH;
+ if (NSPointInRect(point, handleRect)) {
+ return UpperRightKnob;
+ }
+ }
+ if (knobMask & MiddleLeftKnobMask) {
+ handleRect.origin.x = NSMinX(bounds) - SKT_HALF_HANDLE_WIDTH;
+ handleRect.origin.y = NSMidY(bounds) - SKT_HALF_HANDLE_WIDTH;
+ if (NSPointInRect(point, handleRect)) {
+ return MiddleLeftKnob;
+ }
+ }
+ if (knobMask & MiddleRightKnobMask) {
+ handleRect.origin.x = NSMaxX(bounds) - SKT_HALF_HANDLE_WIDTH;
+ handleRect.origin.y = NSMidY(bounds) - SKT_HALF_HANDLE_WIDTH;
+ if (NSPointInRect(point, handleRect)) {
+ return MiddleRightKnob;
+ }
+ }
+ if (knobMask & LowerLeftKnobMask) {
+ handleRect.origin.x = NSMinX(bounds) - SKT_HALF_HANDLE_WIDTH;
+ handleRect.origin.y = NSMaxY(bounds) - SKT_HALF_HANDLE_WIDTH;
+ if (NSPointInRect(point, handleRect)) {
+ return LowerLeftKnob;
+ }
+ }
+ if (knobMask & LowerMiddleKnobMask) {
+ handleRect.origin.x = NSMidX(bounds) - SKT_HALF_HANDLE_WIDTH;
+ handleRect.origin.y = NSMaxY(bounds) - SKT_HALF_HANDLE_WIDTH;
+ if (NSPointInRect(point, handleRect)) {
+ return LowerMiddleKnob;
+ }
+ }
+ if (knobMask & LowerRightKnobMask) {
+ handleRect.origin.x = NSMaxX(bounds) - SKT_HALF_HANDLE_WIDTH;
+ handleRect.origin.y = NSMaxY(bounds) - SKT_HALF_HANDLE_WIDTH;
+ if (NSPointInRect(point, handleRect)) {
+ return LowerRightKnob;
+ }
+ }
+
+ return NoKnob;
+}
+
+- (void)drawHandleAtPoint:(NSPoint)point inView:(SKTGraphicView *)view {
+ NSRect handleRect;
+
+ handleRect.origin.x = point.x - SKT_HALF_HANDLE_WIDTH + 1.0;
+ handleRect.origin.y = point.y - SKT_HALF_HANDLE_WIDTH + 1.0;
+ handleRect.size.width = SKT_HANDLE_WIDTH - 1.0;
+ handleRect.size.height = SKT_HANDLE_WIDTH - 1.0;
+ handleRect = [view centerScanRect:handleRect];
+ [[NSColor controlDarkShadowColor] set];
+ NSRectFill(handleRect);
+ handleRect = NSOffsetRect(handleRect, -1.0, -1.0);
+ [[NSColor knobColor] set];
+ NSRectFill(handleRect);
+}
+
+- (void)drawHandlesInView:(SKTGraphicView *)view {
+ NSRect bounds = [self bounds];
+ unsigned knobMask = [self knobMask];
+
+ if (knobMask & UpperLeftKnobMask) {
+ [self drawHandleAtPoint:NSMakePoint(NSMinX(bounds), NSMinY(bounds)) inView:view];
+ }
+ if (knobMask & UpperMiddleKnobMask) {
+ [self drawHandleAtPoint:NSMakePoint(NSMidX(bounds), NSMinY(bounds)) inView:view];
+ }
+ if (knobMask & UpperRightKnobMask) {
+ [self drawHandleAtPoint:NSMakePoint(NSMaxX(bounds), NSMinY(bounds)) inView:view];
+ }
+
+ if (knobMask & MiddleLeftKnobMask) {
+ [self drawHandleAtPoint:NSMakePoint(NSMinX(bounds), NSMidY(bounds)) inView:view];
+ }
+ if (knobMask & MiddleRightKnobMask) {
+ [self drawHandleAtPoint:NSMakePoint(NSMaxX(bounds), NSMidY(bounds)) inView:view];
+ }
+
+ if (knobMask & LowerLeftKnobMask) {
+ [self drawHandleAtPoint:NSMakePoint(NSMinX(bounds), NSMaxY(bounds)) inView:view];
+ }
+ if (knobMask & LowerMiddleKnobMask) {
+ [self drawHandleAtPoint:NSMakePoint(NSMidX(bounds), NSMaxY(bounds)) inView:view];
+ }
+ if (knobMask & LowerRightKnobMask) {
+ [self drawHandleAtPoint:NSMakePoint(NSMaxX(bounds), NSMaxY(bounds)) inView:view];
+ }
+}
+
+// =================================== Event Handling ===================================
++ (NSCursor *)creationCursor {
+ // By default we use the crosshair cursor
+ static NSCursor *crosshairCursor = nil;
+ if (!crosshairCursor) {
+ NSImage *crosshairImage = [NSImage imageNamed:@"Cross"];
+ NSSize imageSize = [crosshairImage size];
+ crosshairCursor = [[NSCursor allocWithZone:[self zone]] initWithImage:crosshairImage hotSpot:NSMakePoint((imageSize.width / 2.0), (imageSize.height / 2.0))];
+ }
+ return crosshairCursor;
+}
+
+- (BOOL)createWithEvent:(NSEvent *)theEvent inView:(SKTGraphicView *)view {
+ // default implementation tracks until mouseUp: just setting the bounds of the new graphic.
+ NSPoint point = [view convertPoint:[theEvent locationInWindow] fromView:nil];
+ int knob = LowerRightKnob;
+ NSRect bounds;
+ BOOL snapsToGrid = [view snapsToGrid];
+ float spacing = [view gridSpacing];
+ BOOL echoToRulers = [[view enclosingScrollView] rulersVisible];
+
+ [self startBoundsManipulation];
+ if (snapsToGrid) {
+ point.x = floor((point.x / spacing) + 0.5) * spacing;
+ point.y = floor((point.y / spacing) + 0.5) * spacing;
+ }
+ [self setBounds:NSMakeRect(point.x, point.y, 0.0, 0.0)];
+ if (echoToRulers) {
+ [view beginEchoingMoveToRulers:[self bounds]];
+ }
+ while (1) {
+ theEvent = [[view window] nextEventMatchingMask:(NSLeftMouseDraggedMask | NSLeftMouseUpMask)];
+ point = [view convertPoint:[theEvent locationInWindow] fromView:nil];
+ if (snapsToGrid) {
+ point.x = floor((point.x / spacing) + 0.5) * spacing;
+ point.y = floor((point.y / spacing) + 0.5) * spacing;
+ }
+ [view setNeedsDisplayInRect:[self drawingBounds]];
+ knob = [self resizeByMovingKnob:knob toPoint:point];
+ [view setNeedsDisplayInRect:[self drawingBounds]];
+ if (echoToRulers) {
+ [view continueEchoingMoveToRulers:[self bounds]];
+ }
+ if ([theEvent type] == NSLeftMouseUp) {
+ break;
+ }
+ }
+ if (echoToRulers) {
+ [view stopEchoingMoveToRulers];
+ }
+
+ [self stopBoundsManipulation];
+
+ bounds = [self bounds];
+ if ((bounds.size.width > 0.0) || (bounds.size.height > 0.0)) {
+ return YES;
+ } else {
+ return NO;
+ }
+}
+
+- (BOOL)isEditable {
+ return NO;
+}
+
+- (void)startEditingWithEvent:(NSEvent *)event inView:(SKTGraphicView *)view {
+ return;
+}
+
+- (void)endEditingInView:(SKTGraphicView *)view {
+ return;
+}
+
+- (BOOL)hitTest:(NSPoint)point isSelected:(BOOL)isSelected {
+ if (isSelected && ([self knobUnderPoint:point] != NoKnob)) {
+ return YES;
+ } else {
+ NSBezierPath *path = [self bezierPath];
+
+ if (path) {
+ if ([path containsPoint:point]) {
+ return YES;
+ }
+ } else {
+ if (NSPointInRect(point, [self bounds])) {
+ return YES;
+ }
+ }
+ return NO;
+ }
+}
+
+- (NSString *)description {
+ return [[self propertyListRepresentation] description];
+}
+
+@end
+
+@implementation SKTGraphic (SKTScriptingExtras)
+
+// These are methods that we probably wouldn't bother with if we weren't scriptable.
+
+- (NSScriptObjectSpecifier *)objectSpecifier {
+ NSArray *graphics = [[self document] graphics];
+ unsigned index = [graphics indexOfObjectIdenticalTo:self];
+ if (index != NSNotFound) {
+ NSScriptObjectSpecifier *containerRef = [[self document] objectSpecifier];
+ return [[[NSIndexSpecifier allocWithZone:[self zone]] initWithContainerClassDescription:[containerRef keyClassDescription] containerSpecifier:containerRef key:@"graphics" index:index] autorelease];
+ } else {
+ return nil;
+ }
+}
+
+- (float)xPosition {
+ return [self bounds].origin.x;
+}
+
+- (void)setXPosition:(float)newVal {
+ NSRect bounds = [self bounds];
+ bounds.origin.x = newVal;
+ [self setBounds:bounds];
+}
+
+- (float)yPosition {
+ return [self bounds].origin.y;
+}
+
+- (void)setYPosition:(float)newVal {
+ NSRect bounds = [self bounds];
+ bounds.origin.y = newVal;
+ [self setBounds:bounds];
+}
+
+- (float)width {
+ return [self bounds].size.width;
+}
+
+- (void)setWidth:(float)newVal {
+ NSRect bounds = [self bounds];
+ bounds.size.width = newVal;
+ [self setBounds:bounds];
+}
+
+- (float)height {
+ return [self bounds].size.height;
+}
+
+- (void)setHeight:(float)newVal {
+ NSRect bounds = [self bounds];
+ bounds.size.height = newVal;
+ [self setBounds:bounds];
+}
+
+@end
+
+/*
+ IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. ("Apple") in
+ consideration of your agreement to the following terms, and your use, installation,
+ modification or redistribution of this Apple software constitutes acceptance of these
+ terms. If you do not agree with these terms, please do not use, install, modify or
+ redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and subject to these
+ terms, Apple grants you a personal, non-exclusive license, under Apple's copyrights in
+ this original Apple software (the "Apple Software"), to use, reproduce, modify and
+ redistribute the Apple Software, with or without modifications, in source and/or binary
+ forms; provided that if you redistribute the Apple Software in its entirety and without
+ modifications, you must retain this notice and the following text and disclaimers in all
+ such redistributions of the Apple Software. Neither the name, trademarks, service marks
+ or logos of Apple Computer, Inc. may be used to endorse or promote products derived from
+ the Apple Software without specific prior written permission from Apple. Except as expressly
+ stated in this notice, no other rights or licenses, express or implied, are granted by Apple
+ herein, including but not limited to any patent rights that may be infringed by your
+ derivative works or by other works in which the Apple Software may be incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES,
+ EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS
+ USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
+ REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND
+ WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
+ OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
57 LiveSketch/DocumentModel.subproj/SKTImage.h
@@ -0,0 +1,57 @@
+// SKTImage.h
+// Sketch Example
+//
+
+#import "SKTGraphic.h"
+
+@interface SKTImage : SKTGraphic {
+ @private
+ NSImage *_image;
+ NSImage *_cachedImage;
+ BOOL _flippedHorizontally;
+ BOOL _flippedVertically;
+}
+
+- (void)setImage:(NSImage *)image;
+- (NSImage *)image;
+- (NSImage *)transformedImage;
+
+- (void)setFlippedHorizontally:(BOOL)flag;
+- (BOOL)flippedHorizontally;
+- (void)setFlippedVertically:(BOOL)flag;
+- (BOOL)flippedVertically;
+
+@end
+
+/*
+ IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. ("Apple") in
+ consideration of your agreement to the following terms, and your use, installation,
+ modification or redistribution of this Apple software constitutes acceptance of these
+ terms. If you do not agree with these terms, please do not use, install, modify or
+ redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and subject to these
+ terms, Apple grants you a personal, non-exclusive license, under Apple's copyrights in
+ this original Apple software (the "Apple Software"), to use, reproduce, modify and
+ redistribute the Apple Software, with or without modifications, in source and/or binary
+ forms; provided that if you redistribute the Apple Software in its entirety and without
+ modifications, you must retain this notice and the following text and disclaimers in all
+ such redistributions of the Apple Software. Neither the name, trademarks, service marks
+ or logos of Apple Computer, Inc. may be used to endorse or promote products derived from
+ the Apple Software without specific prior written permission from Apple. Except as expressly
+ stated in this notice, no other rights or licenses, express or implied, are granted by Apple
+ herein, including but not limited to any patent rights that may be infringed by your
+ derivative works or by other works in which the Apple Software may be incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES,
+ EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS
+ USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
+ REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND
+ WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
+ OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
248 LiveSketch/DocumentModel.subproj/SKTImage.m
@@ -0,0 +1,248 @@
+// SKTImage.m
+// Sketch Example
+//
+
+#import "SKTImage.h"
+
+@implementation SKTImage
+
+- (id)init {
+ self = [super init];
+ if (self) {
+ _image = nil;
+ _cachedImage = nil;
+ }
+ return self;
+}
+
+- (void)dealloc {
+ if (_image != _cachedImage) {
+ [_cachedImage release];
+ }
+ [_image release];
+ [super dealloc];
+}
+
+- (id)copyWithZone:(NSZone *)zone {
+ id newObj = [super copyWithZone:zone];
+
+ [newObj setImage:[self image]];
+ [newObj setFlippedHorizontally:[self flippedHorizontally]];
+ [newObj setFlippedVertically:[self flippedVertically]];
+
+ return newObj;
+}
+
+- (void)SKT_clearCachedImage {
+ if (_cachedImage != _image) {
+ [_cachedImage release];
+ }
+ _cachedImage = nil;
+}
+
+- (void)setImage:(NSImage *)image {
+ if (image != _image) {
+ [[[self undoManager] prepareWithInvocationTarget:self] setImage:_image];
+ [_image release];
+ _image = [image retain];
+ [self SKT_clearCachedImage];
+ [self didChange];
+ }
+}
+
+- (NSImage *)image {
+ return _image;
+}
+
+- (NSImage *)transformedImage {
+ if (!_cachedImage) {
+ NSRect bounds = [self bounds];
+ NSImage *image = [self image];
+ NSSize imageSize = [image size];
+
+ if (NSEqualSizes(bounds.size, imageSize)) {
+ _cachedImage = _image;
+ } else if (!NSIsEmptyRect(bounds)) {
+ BOOL flippedHorizontally = [self flippedHorizontally];
+ BOOL flippedVertically = [self flippedVertically];
+
+ _cachedImage = [[NSImage allocWithZone:[self zone]] initWithSize:bounds.size];
+ if (!NSIsEmptyRect(bounds)) {
+ // Only draw in the image if it has any content.
+ [_cachedImage lockFocus];
+
+ if (flippedHorizontally || flippedVertically) {
+ // If the image needs flipping, we need to play some games with the transform matrix
+ NSAffineTransform *transform = [NSAffineTransform transform];
+ [transform scaleXBy:([self flippedHorizontally] ? -1.0 : 1.0) yBy:([self flippedVertically] ? -1.0 : 1.0)];
+ [transform translateXBy:([self flippedHorizontally] ? -bounds.size.width : 0.0) yBy:([self flippedVertically] ? -bounds.size.height : 0.0)];
+ [transform concat];
+ }
+
+ [[image bestRepresentationForDevice:nil] drawInRect:NSMakeRect(0.0, 0.0, bounds.size.width, bounds.size.height)];
+ [_cachedImage unlockFocus];
+ }
+ }
+ }
+ return _cachedImage;
+}
+
+- (void)setFlippedHorizontally:(BOOL)flag {
+ if (_flippedHorizontally != flag) {
+ [[[self undoManager] prepareWithInvocationTarget:self] setFlippedHorizontally:_flippedHorizontally];
+ _flippedHorizontally = flag;
+ [self SKT_clearCachedImage];
+ [self didChange];
+ }
+}
+
+- (BOOL)flippedHorizontally {
+ return _flippedHorizontally;
+}
+
+- (void)setFlippedVertically:(BOOL)flag {
+ if (_flippedVertically != flag) {
+ [[[self undoManager] prepareWithInvocationTarget:self] setFlippedVertically:_flippedVertically];
+ _flippedVertically = flag;
+ [self SKT_clearCachedImage];
+ [self didChange];
+ }
+}
+
+- (BOOL)flippedVertically {
+ return _flippedVertically;
+}
+
+- (void)flipHorizontally {
+ [self setFlippedHorizontally:([self flippedHorizontally] ? NO : YES)];
+}
+
+- (void)flipVertically {
+ [self setFlippedVertically:([self flippedVertically] ? NO : YES)];
+}
+
+- (void)setBounds:(NSRect)bounds {
+ if (!NSEqualSizes([self bounds].size, bounds.size)) {
+ [self SKT_clearCachedImage];
+ }
+ [super setBounds:bounds];
+}
+
+- (BOOL)drawsStroke {
+ // Never draw stroke.
+ return NO;
+}
+
+- (BOOL)canDrawStroke {
+ // Never draw stroke.
+ return NO;
+}
+
+- (void)drawInView:(SKTGraphicView *)view isSelected:(BOOL)flag {
+ NSRect bounds = [self bounds];
+ NSImage *image;
+
+ if ([self drawsFill]) {
+ [[self fillColor] set];
+ NSRectFill(bounds);
+ }
+ image = [self transformedImage];
+ if (image) {
+ [image compositeToPoint:NSMakePoint(NSMinX(bounds), NSMaxY(bounds)) operation:NSCompositeSourceOver];
+ }
+ [super drawInView:view isSelected:flag];
+}
+
+- (void)makeNaturalSize {
+ NSRect bounds = [self bounds];
+ NSImage *image = [self image];
+ NSSize requiredSize = (image ? [image size] : NSMakeSize(10.0, 10.0));
+
+ bounds.size = requiredSize;
+ [self setBounds:bounds];
+ [self setFlippedHorizontally:NO];
+ [self setFlippedVertically:NO];
+}
+
+NSString *SKTImageContentsKey = @"Image";
+NSString *SKTFlippedHorizontallyKey = @"FlippedHorizontally";
+NSString *SKTFlippedVerticallyKey = @"FlippedVertically";
+
+- (NSMutableDictionary *)propertyListRepresentation {
+ NSMutableDictionary *dict = [super propertyListRepresentation];
+ [dict setObject:[NSArchiver archivedDataWithRootObject:[self image]] forKey:SKTImageContentsKey];
+ [dict setObject:([self flippedHorizontally] ? @"YES" : @"NO") forKey:SKTFlippedHorizontallyKey];
+ [dict setObject:([self flippedVertically] ? @"YES" : @"NO") forKey:SKTFlippedVerticallyKey];
+ return dict;
+}
+
+- (void)loadPropertyListRepresentation:(NSDictionary *)dict {
+ id obj;
+
+ [super loadPropertyListRepresentation:dict];
+
+ obj = [dict objectForKey:SKTImageContentsKey];
+ if (obj) {
+ [self setImage:[NSUnarchiver unarchiveObjectWithData:obj]];
+ }
+ obj = [dict objectForKey:SKTFlippedHorizontallyKey];
+ if (obj) {
+ [self setFlippedHorizontally:[obj isEqualToString:@"YES"]];
+ }
+ obj = [dict objectForKey:SKTFlippedVerticallyKey];
+ if (obj) {
+ [self setFlippedVertically:[obj isEqualToString:@"YES"]];
+ }
+ _cachedImage = nil;
+}
+
+- (void)setImageFile:(NSString *)filePath {
+ NSImage *newImage;
+ filePath = [filePath stringByStandardizingPath];
+ filePath = [filePath stringByExpandingTildeInPath];
+ newImage = [[NSImage allocWithZone:[self zone]] initWithContentsOfFile:filePath];
+ if (newImage) {
+ [self setImage:newImage];
+ [newImage release];
+ }
+}
+
+- (NSString *)imageFile {
+ // This is really a "write-only" attribute used for setting the image for an SKTImage shape from a script. We don't remember the path so the accessor just returns an empty string.
+ return @"";
+}
+
+@end
+
+/*
+ IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. ("Apple") in
+ consideration of your agreement to the following terms, and your use, installation,
+ modification or redistribution of this Apple software constitutes acceptance of these
+ terms. If you do not agree with these terms, please do not use, install, modify or
+ redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and subject to these
+ terms, Apple grants you a personal, non-exclusive license, under Apple's copyrights in
+ this original Apple software (the "Apple Software"), to use, reproduce, modify and
+ redistribute the Apple Software, with or without modifications, in source and/or binary
+ forms; provided that if you redistribute the Apple Software in its entirety and without
+ modifications, you must retain this notice and the following text and disclaimers in all
+ such redistributions of the Apple Software. Neither the name, trademarks, service marks
+ or logos of Apple Computer, Inc. may be used to endorse or promote products derived from
+ the Apple Software without specific prior written permission from Apple. Except as expressly
+ stated in this notice, no other rights or licenses, express or implied, are granted by Apple
+ herein, including but not limited to any patent rights that may be infringed by your
+ derivative works or by other works in which the Apple Software may be incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES,
+ EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS
+ USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
+ REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND
+ WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
+ OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
49 LiveSketch/DocumentModel.subproj/SKTLine.h
@@ -0,0 +1,49 @@
+// SKTLine.h
+// Sketch Example
+//
+
+#import <AppKit/AppKit.h>
+#import "SKTGraphic.h"
+
+@interface SKTLine : SKTGraphic {