summaryrefslogtreecommitdiff
path: root/tikzit/src/osx
diff options
context:
space:
mode:
authorrandomguy3 <randomguy3@7c02a99a-9b00-45e3-bf44-6f3dd7fddb64>2012-01-09 11:00:50 +0000
committerrandomguy3 <randomguy3@7c02a99a-9b00-45e3-bf44-6f3dd7fddb64>2012-01-09 11:00:50 +0000
commita8a8dfb90d6a51ae369c042c95162f45754c7c4b (patch)
tree0e7a5f82febebe7129ebfb015f05b114064c39fd /tikzit/src/osx
parente1cf0babff63e670e0d550b4072c22649a117fa7 (diff)
Move tikzit into "trunk" directory
git-svn-id: https://tikzit.svn.sourceforge.net/svnroot/tikzit/trunk@365 7c02a99a-9b00-45e3-bf44-6f3dd7fddb64
Diffstat (limited to 'tikzit/src/osx')
-rw-r--r--tikzit/src/osx/AppDelegate.h53
-rw-r--r--tikzit/src/osx/AppDelegate.m105
-rw-r--r--tikzit/src/osx/CALayer+DrawLabel.h21
-rw-r--r--tikzit/src/osx/CALayer+DrawLabel.m84
-rw-r--r--tikzit/src/osx/CoreGraphicsRenderContext.h44
-rw-r--r--tikzit/src/osx/CoreGraphicsRenderContext.m234
-rw-r--r--tikzit/src/osx/EdgeControlLayer.h44
-rw-r--r--tikzit/src/osx/EdgeControlLayer.m150
-rw-r--r--tikzit/src/osx/EdgeStyle+Coder.h30
-rw-r--r--tikzit/src/osx/EdgeStyle+Coder.m50
-rw-r--r--tikzit/src/osx/Graph+Coder.h17
-rw-r--r--tikzit/src/osx/Graph+Coder.m25
-rw-r--r--tikzit/src/osx/GraphicsView.h128
-rw-r--r--tikzit/src/osx/GraphicsView.m1142
-rw-r--r--tikzit/src/osx/Grid.h48
-rw-r--r--tikzit/src/osx/Grid.m152
-rw-r--r--tikzit/src/osx/MultiCombo.h18
-rw-r--r--tikzit/src/osx/MultiCombo.m38
-rw-r--r--tikzit/src/osx/MultiField.h18
-rw-r--r--tikzit/src/osx/MultiField.m53
-rw-r--r--tikzit/src/osx/NilToEmptyStringTransformer.h28
-rw-r--r--tikzit/src/osx/NilToEmptyStringTransformer.m53
-rw-r--r--tikzit/src/osx/NodeLayer.h62
-rw-r--r--tikzit/src/osx/NodeLayer.m239
-rw-r--r--tikzit/src/osx/NodeSelectionLayer.h45
-rw-r--r--tikzit/src/osx/NodeSelectionLayer.m93
-rw-r--r--tikzit/src/osx/NodeStyle+Coder.h36
-rw-r--r--tikzit/src/osx/NodeStyle+Coder.m91
-rw-r--r--tikzit/src/osx/PreambleController.h57
-rw-r--r--tikzit/src/osx/PreambleController.m168
-rw-r--r--tikzit/src/osx/Preambles+Coder.h32
-rw-r--r--tikzit/src/osx/Preambles+Coder.m41
-rw-r--r--tikzit/src/osx/PreviewController.h51
-rw-r--r--tikzit/src/osx/PreviewController.m137
-rw-r--r--tikzit/src/osx/PropertyInspectorController.h73
-rw-r--r--tikzit/src/osx/PropertyInspectorController.m272
-rw-r--r--tikzit/src/osx/SelectBoxLayer.h22
-rw-r--r--tikzit/src/osx/SelectBoxLayer.m48
-rw-r--r--tikzit/src/osx/SelectableCollectionViewItem.h33
-rw-r--r--tikzit/src/osx/SelectableCollectionViewItem.m54
-rw-r--r--tikzit/src/osx/SelectableNodeView.h38
-rw-r--r--tikzit/src/osx/SelectableNodeView.m96
-rw-r--r--tikzit/src/osx/StylePaletteController.h80
-rw-r--r--tikzit/src/osx/StylePaletteController.m247
-rw-r--r--tikzit/src/osx/TikzDocument.h37
-rw-r--r--tikzit/src/osx/TikzDocument.m84
-rw-r--r--tikzit/src/osx/TikzParser.h66
-rw-r--r--tikzit/src/osx/TikzParser.m574
-rw-r--r--tikzit/src/osx/TikzSourceController.h66
-rw-r--r--tikzit/src/osx/TikzSourceController.m196
-rw-r--r--tikzit/src/osx/TikzWindowController.h31
-rw-r--r--tikzit/src/osx/TikzWindowController.m66
-rw-r--r--tikzit/src/osx/ToolPaletteController.h42
-rw-r--r--tikzit/src/osx/ToolPaletteController.m58
-rw-r--r--tikzit/src/osx/main.m26
-rw-r--r--tikzit/src/osx/test/main.m56
-rw-r--r--tikzit/src/osx/test/osx.m64
57 files changed, 5916 insertions, 0 deletions
diff --git a/tikzit/src/osx/AppDelegate.h b/tikzit/src/osx/AppDelegate.h
new file mode 100644
index 0000000..349b47b
--- /dev/null
+++ b/tikzit/src/osx/AppDelegate.h
@@ -0,0 +1,53 @@
+//
+// AppDelegate.h
+// TikZiT
+//
+// Copyright 2010 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#import <Cocoa/Cocoa.h>
+#import "StylePaletteController.h"
+#import "ToolPaletteController.h"
+#import "PropertyInspectorController.h"
+#import "PreambleController.h"
+#import "PreviewController.h"
+#import "GraphicsView.h"
+
+@interface AppDelegate : NSObject {
+ NSMapTable *table;
+ StylePaletteController *stylePaletteController;
+ PropertyInspectorController *propertyInspectorController;
+ PreambleController *preambleController;
+ PreviewController *previewController;
+ ToolPaletteController *toolPaletteController;
+ IBOutlet GraphicsView *graphicsView;
+ NSString *tempDir;
+}
+
+@property IBOutlet StylePaletteController *stylePaletteController;
+@property IBOutlet ToolPaletteController *toolPaletteController;
+
+- (void)awakeFromNib;
+- (void)applicationWillTerminate:(NSNotification *)notification;
+- (IBAction)toggleStyleInspector:(id)sender;
+- (IBAction)togglePropertyInspector:(id)sender;
+- (IBAction)togglePreamble:(id)sender;
+- (IBAction)refreshShapes:(id)sender;
+
+@end
diff --git a/tikzit/src/osx/AppDelegate.m b/tikzit/src/osx/AppDelegate.m
new file mode 100644
index 0000000..edfaf19
--- /dev/null
+++ b/tikzit/src/osx/AppDelegate.m
@@ -0,0 +1,105 @@
+//
+// AppDelegate.m
+// TikZiT
+//
+// Copyright 2010 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#import "AppDelegate.h"
+#import "TikzGraphAssembler.h"
+#import "TikzDocument.h"
+#import "Shape.h"
+#import "SupportDir.h"
+
+@implementation AppDelegate
+
+@synthesize stylePaletteController, toolPaletteController;
+
+
+- (void)awakeFromNib {
+ [TikzGraphAssembler setup]; // initialise lex/yacc parser globals
+
+ [SupportDir createUserSupportDir];
+ NSString *supportDir = [SupportDir userSupportDir];
+ //NSLog(stylePlist);
+ stylePaletteController =
+ [[StylePaletteController alloc] initWithWindowNibName:@"StylePalette"
+ supportDir:supportDir];
+
+ propertyInspectorController =
+ [[PropertyInspectorController alloc] initWithWindowNibName:@"PropertyInspector"];
+
+ [propertyInspectorController setStylePaletteController:stylePaletteController];
+
+ NSString *preamblePlist = [supportDir stringByAppendingPathComponent:@"preambles.plist"];
+ preambleController =
+ [[PreambleController alloc] initWithWindowNibName:@"Preamble"
+ plist:preamblePlist
+ styles:[stylePaletteController nodeStyles]];
+
+ char template[] = "/tmp/tikzit_tmp_XXXXXXX";
+ char *dir = mkdtemp(template);
+ tempDir = [NSString stringWithUTF8String:dir];
+
+ NSLog(@"created temp dir: %@", tempDir);
+ NSLog(@"system support dir: %@", [SupportDir systemSupportDir]);
+
+ previewController =
+ [[PreviewController alloc] initWithWindowNibName:@"Preview"
+ preambleController:preambleController
+ tempDir:tempDir];
+
+ // each application has one global preview controller
+ [PreviewController setDefaultPreviewController:previewController];
+}
+
+- (void)applicationWillTerminate:(NSNotification *)notification {
+ NSString *supportDir = [SupportDir userSupportDir];
+ [stylePaletteController saveStyles:supportDir];
+ [preambleController savePreambles:[supportDir stringByAppendingPathComponent:@"preambles.plist"]];
+
+ NSLog(@"wiping temp dir: %@", tempDir);
+ [[NSFileManager defaultManager] removeItemAtPath:tempDir error:NULL];
+}
+
+- (void)toggleController:(NSWindowController*)c {
+ if ([[c window] isVisible]) {
+ [c close];
+ } else {
+ [c showWindow:self];
+ }
+}
+
+- (IBAction)toggleStyleInspector:(id)sender {
+ [self toggleController:stylePaletteController];
+}
+
+- (IBAction)togglePropertyInspector:(id)sender {
+ [self toggleController:propertyInspectorController];
+}
+
+- (IBAction)togglePreamble:(id)sender {
+ [self toggleController:preambleController];
+}
+
+- (IBAction)refreshShapes:(id)sender {
+ [Shape refreshShapeDictionary];
+}
+
+@end
diff --git a/tikzit/src/osx/CALayer+DrawLabel.h b/tikzit/src/osx/CALayer+DrawLabel.h
new file mode 100644
index 0000000..32282d9
--- /dev/null
+++ b/tikzit/src/osx/CALayer+DrawLabel.h
@@ -0,0 +1,21 @@
+//
+// CALayer+DrawLabel.h
+// TikZiT
+//
+// Created by Aleks Kissinger on 09/05/2011.
+// Copyright 2011 Aleks Kissinger. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+#import <QuartzCore/CoreAnimation.h>
+
+@class Transformer;
+
+@interface CALayer(DrawLabel)
+
+- (void)drawLabel:(NSString*)label
+ atPoint:(NSPoint)pt
+ inContext:(CGContextRef)context
+ usingTrans:(Transformer*)t;
+
+@end
diff --git a/tikzit/src/osx/CALayer+DrawLabel.m b/tikzit/src/osx/CALayer+DrawLabel.m
new file mode 100644
index 0000000..4860a3c
--- /dev/null
+++ b/tikzit/src/osx/CALayer+DrawLabel.m
@@ -0,0 +1,84 @@
+//
+// CALayer+DrawLabel.m
+// TikZiT
+//
+// Created by Aleks Kissinger on 09/05/2011.
+// Copyright 2011 Aleks Kissinger. All rights reserved.
+//
+
+#import "CALayer+DrawLabel.h"
+#import "Transformer.h"
+
+@implementation CALayer(DrawLabel)
+
+- (void)drawLabel:(NSString*)label
+ atPoint:(NSPoint)pt
+ inContext:(CGContextRef)context
+ usingTrans:(Transformer*)t {
+
+ CGContextSaveGState(context);
+
+ if ([label length] > 15) {
+ label = [[label substringToIndex:12] stringByAppendingString:@"..."];
+ }
+
+ float fontSize = [t scaleToScreen:0.18f]; // size 9 @ 100%
+ if (fontSize > 18.0f) fontSize = 18.0f;
+
+ // Prepare font
+ CTFontRef font = CTFontCreateWithName(CFSTR("Monaco"), fontSize, NULL);
+
+ // Create an attributed string
+ CFStringRef keys[] = { kCTFontAttributeName };
+ CFTypeRef values[] = { font };
+ CFDictionaryRef attr = CFDictionaryCreate(NULL,
+ (const void **)&keys,
+ (const void **)&values,
+ sizeof(keys) / sizeof(keys[0]),
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ CFAttributedStringRef attrString =
+ CFAttributedStringCreate(NULL, (CFStringRef)label, attr);
+ CFRelease(attr);
+
+ // Draw the string
+ CTLineRef line = CTLineCreateWithAttributedString(attrString);
+ CGContextSetTextMatrix(context, CGAffineTransformIdentity);
+ CGContextSetTextPosition(context, 0, 0);
+
+ CGRect labelBounds = CGRectIntegral(CTLineGetImageBounds(line, context));
+ //int shiftx = round(labelBounds.size.width / 2);
+
+ CGContextSetTextPosition(context,
+ round(pt.x - (labelBounds.size.width/2)),
+ round(pt.y - (0.9*labelBounds.size.height/2)));
+
+ labelBounds = CGRectIntegral(CTLineGetImageBounds(line, context));
+ labelBounds.origin.x -= 2;
+ labelBounds.origin.y -= 2;
+ labelBounds.size.width += 4;
+ labelBounds.size.height += 4;
+
+ CGContextSetShouldAntialias(context, NO);
+
+ CGContextSetRGBFillColor(context, 1.0f, 1.0f, 0.5f, 0.7f);
+ CGContextSetRGBStrokeColor(context, 0.5f, 0.0f, 0.0f, 0.7f);
+
+ CGContextFillRect(context, labelBounds);
+ CGContextStrokeRect(context, labelBounds);
+
+ CGContextSetShouldAntialias(context, YES);
+
+ CGContextSetRGBFillColor(context, 0.3f, 0.3f, 0.3f, 0.7f);
+
+ CTLineDraw(line, context);
+
+ // Clean up
+ CFRelease(line);
+ CFRelease(attrString);
+ CFRelease(font);
+
+ CGContextRestoreGState(context);
+}
+
+@end
diff --git a/tikzit/src/osx/CoreGraphicsRenderContext.h b/tikzit/src/osx/CoreGraphicsRenderContext.h
new file mode 100644
index 0000000..7b00484
--- /dev/null
+++ b/tikzit/src/osx/CoreGraphicsRenderContext.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2011 Alex Merry <alex.merry@kdemail.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#import <Cocoa/Cocoa.h>
+#import "RenderContext.h"
+
+@interface CoreTextLayout: NSObject<TextLayout> {
+ CFAttributedStringRef attrString;
+ CTFontRef font;
+ CTLineRef line;
+ CGContextRef ctx;
+}
+
++ (CoreTextLayout*) layoutForContext:(CGContextRef)c withString:(NSString*)string fontSize:(CGFloat)fontSize;
+- (id) initWithContext:(CGContextRef)cr withString:(NSString*)string fontSize:(CGFloat)fontSize;
+
+@end
+
+@interface CoreGraphicsRenderContext: NSObject<RenderContext> {
+ CGContextRef ctx;
+}
+
++ (CoreGraphicsRenderContext*) contextWithCGContext:(CGContextRef)c;
++ (id) initWithCGContext:(CGContextRef)c;
+
+- (CGContextRef) ctx;
+
+@end
+
+// vim:ft=objc:ts=4:noet:sts=4:sw=4
diff --git a/tikzit/src/osx/CoreGraphicsRenderContext.m b/tikzit/src/osx/CoreGraphicsRenderContext.m
new file mode 100644
index 0000000..1cb0daf
--- /dev/null
+++ b/tikzit/src/osx/CoreGraphicsRenderContext.m
@@ -0,0 +1,234 @@
+/*
+ * Copyright 2011 Alex Merry <alex.merry@kdemail.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#import <Cocoa/Cocoa.h>
+#import "RenderContext.h"
+
+@implementation CoreTextLayout
+
++ (CoreTextLayout*) layoutForContext:(CGContextRef)c withString:(NSString*)string fontSize:(CGFloat)fontSize {
+ return [[[self alloc] initWithContext:c withString:string fontSize:fontSize] autorelease];
+}
+
+- (id) initWithContext:(CGContextRef)cr withString:(NSString*)string fontSize:(CGFloat)fontSize {
+ self = [super init];
+
+ if (self == nil) {
+ return nil;
+ }
+
+ CGContextRetain (cr);
+ ctx = cr;
+ font = CTFontCreateWithName(CFSTR("Monaco"), fontSize, NULL);
+
+ // Create an attributed string
+ CFStringRef keys[] = { kCTFontAttributeName };
+ CFTypeRef values[] = { font };
+ CFDictionaryRef attr = CFDictionaryCreate(NULL,
+ (const void **)&keys,
+ (const void **)&values,
+ sizeof(keys) / sizeof(keys[0]),
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ attrString = CFAttributedStringCreate(NULL, (CFStringRef)lab, attr);
+ CFRelease(attr);
+ line = CTLineCreateWithAttributedString(attrString);
+
+ return self;
+}
+
+- (NSSize) size {
+ CGRect labelBounds = CGRectIntegral(CTLineGetImageBounds(line, ctx));
+ return labelBounds.size;
+}
+
+- (NSString*) text {
+ return CFAttributedStringGetString (attrString);
+}
+
+- (void) showTextAt:(NSPoint)topLeft withColor:(RColor)color {
+ CGContextSaveGState(ctx);
+
+ CGContextSetRGBFillColor(ctx, color.red, color.green, color.blue, color.alpha);
+ CGContextSetRGBStrokeColor(ctx, color.red, color.green, color.blue, color.alpha);
+ CGContextSetShouldAntialias(ctx, YES);
+
+ CGContextSetTextMatrix(ctx, CGAffineTransformIdentity);
+ CGContextSetTextPosition(ctx, 0, 0);
+ CGRect bounds = CGRectIntegral(CTLineGetImageBounds(line, ctx));
+ CGContextSetTextPosition(ctx, topLeft.x - bounds.x, topLeft.y - bounds.y);
+
+ CTLineDraw(line, ctx);
+
+ CGContextRestoreGState(ctx);
+}
+
+- (void) dealloc {
+ CFRelease(line);
+ CFRelease(attrString);
+ CFRelease(font);
+ CGContextRelease (ctx);
+
+ [super dealloc];
+}
+
+@end
+
+@implementation CoreGraphicsRenderContext
+
++ (CoreGraphicsRenderContext*) contextWithCGContext:(CGContextRef)c {
+ return [[[self alloc] initWithCGContext:c] autorelease];
+}
+
++ (id) initWithCGContext:(CGContextRef)c {
+ self = [super init];
+
+ if (self) {
+ ctx = c;
+ CGContextRetain (ctx);
+ }
+
+ return self;
+}
+
+- (void) dealloc {
+ CGContextRelease (ctx);
+
+ [super dealloc];
+}
+
+- (CGContextRef) ctx {
+ return ctx;
+}
+
+- (void) saveState {
+ CGContextSaveGState(ctx);
+}
+
+- (void) restoreState {
+ CGContextRestoreGState(ctx);
+}
+
+- (NSRect) clipBoundingBox {
+ return CGContextGetClipBoundingBox (ctx);
+}
+
+- (BOOL) strokeIncludesPoint:(NSPoint)p {
+ return CGContextPathContainsPoint(ctx, NSPointToCGPoint(p), kCGPathStroke);
+}
+
+- (BOOL) fillIncludesPoint:(NSPoint)p {
+ return CGContextPathContainsPoint(ctx, NSPointToCGPoint(p), kCGPathFill);
+}
+
+- (id<TextLayout>) layoutText:(NSString*)text withSize:(CGFloat)fontSize {
+ return [CoreTextLayout layoutForContext:ctx withString:text fontSize:fontSize];
+}
+
+// this may not affect text rendering
+- (void) setAntialiasMode:(AntialiasMode)mode {
+ CGContextSetShouldAntialias(ctx, mode != AntialiasDisabled);
+}
+
+- (void) setLineWidth:(CGFloat)width {
+ CGContextSetLineWidth(ctx, width);
+}
+
+// setting to 0 will unset the dash
+- (void) setLineDash:(CGFloat)dashLength {
+ if (dashLength <= 0.0f) {
+ CGContextSetLineDash(ctx, 0.0f, NULL, 0);
+ } else {
+ const CGFloat dash[] = {dashLength, dashLength};
+ CGContextSetLineDash(ctx, 0.0f, dash, 2);
+ }
+}
+
+// paths
+- (void) startPath {
+ CGContextBeginPath (ctx);
+}
+
+- (void) closeSubPath {
+ CGContextClosePath (ctx);
+}
+
+- (void) moveTo:(NSPoint)p {
+ CGContextMoveToPoint (ctx, p.x, p.y);
+}
+
+- (void) curveTo:(NSPoint)end withCp1:(NSPoint)cp1 andCp2:(NSPoint)cp2 {
+ CGContextAddCurveToPoint(ctx, cp1.x, cp1.y, cp2.x, cp2.y, end.x, end.y);
+}
+
+- (void) lineTo:(NSPoint)end {
+ CGContextAddLineToPoint(ctx, end.x, end.y);
+}
+
+- (void) rect:(NSRect)rect {
+ CGContextAddRect (ctx, rect);
+}
+
+- (void) circleAt:(NSPoint)c withRadius:(CGFloat)r {
+ CGContextMoveToPoint (ctx, c.x + r, c.y);
+ CGContextAddArc (ctx, c.x, c.y, r, 0.0f, M_PI, 1);
+}
+
+// these methods clear the path
+- (void) strokePathWithColor:(RColor)color {
+ CGContextSetRGBStrokeColor(ctx, color.red, color.green, color.blue, color.alpha);
+ CGContextDrawPath (ctx, kCGPathStroke);
+}
+
+- (void) fillPathWithColor:(RColor)color {
+ CGContextSetRGBFillColor(ctx, color.red, color.green, color.blue, color.alpha);
+ CGContextDrawPath (ctx, kCGPathFill);
+}
+
+- (void) strokePathWithColor:(RColor)scolor
+ andFillWithColor:(RColor)fcolor {
+ CGContextSetRGBFillColor(ctx, color.red, color.green, color.blue, color.alpha);
+ CGContextSetRGBStrokeColor(ctx, color.red, color.green, color.blue, color.alpha);
+ CGContextDrawPath (ctx, kCGPathFillStroke);
+}
+
+- (void) strokePathWithColor:(RColor)scolor
+ andFillWithColor:(RColor)fcolor
+ usingAlpha:(CGFloat)alpha {
+ CGContextSetRGBFillColor(ctx, color.red, color.green, color.blue, color.alpha * alpha);
+ CGContextSetRGBStrokeColor(ctx, color.red, color.green, color.blue, color.alpha * alpha);
+ CGContextDrawPath (ctx, kCGPathFillStroke);
+}
+
+- (void) clipToPath {
+ CGContextClip (ctx);
+}
+
+// paint everywhere within the clip
+- (void) paintWithColor:(RColor)color {
+ CGContextSetRGBFillColor(ctx, color.red, color.green, color.blue, color.alpha);
+ CGRect r = CGContextGetClipBoundingBox (ctx);
+ r.origin.x -= 1;
+ r.origin.y -= 1;
+ r.size.width += 2;
+ r.size.height += 2;
+ CGContextFillRect(context, r);
+}
+
+@end
+
+// vim:ft=objc:ts=4:noet:sts=4:sw=4
diff --git a/tikzit/src/osx/EdgeControlLayer.h b/tikzit/src/osx/EdgeControlLayer.h
new file mode 100644
index 0000000..4cdf8bc
--- /dev/null
+++ b/tikzit/src/osx/EdgeControlLayer.h
@@ -0,0 +1,44 @@
+//
+// EdgeControlLayer.h
+// TikZiT
+//
+// Copyright 2010 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#import <Cocoa/Cocoa.h>
+#import <QuartzCore/CoreAnimation.h>
+#import "Edge.h"
+#import "Transformer.h"
+
+
+@interface EdgeControlLayer : CALayer {
+ Edge *edge;
+ Transformer *transformer;
+ BOOL selected;
+}
+
+- (id)initWithEdge:(Edge*)e andTransformer:(Transformer*)t;
+- (void)highlight;
+- (void)unhighlight;
+- (void)select;
+- (void)deselect;
+
++ (float)handleRadius;
+
+@end
diff --git a/tikzit/src/osx/EdgeControlLayer.m b/tikzit/src/osx/EdgeControlLayer.m
new file mode 100644
index 0000000..377cde4
--- /dev/null
+++ b/tikzit/src/osx/EdgeControlLayer.m
@@ -0,0 +1,150 @@
+//
+// EdgeControlLayer.m
+// TikZiT
+//
+// Copyright 2010 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#import "EdgeControlLayer.h"
+#import "util.h"
+
+
+@implementation EdgeControlLayer
+
+
+- (id)initWithEdge:(Edge*)e andTransformer:(Transformer*)t {
+ [super init];
+ transformer = t;
+ edge = e;
+ self.opacity = 0.0f;
+ return self;
+}
+
+- (void)select {
+ selected = YES;
+ self.opacity = 1.0f;
+}
+
+- (void)deselect {
+ selected = NO;
+ self.opacity = 0.0f;
+}
+
+- (void)highlight {
+ if (!selected) {
+ self.opacity = 0.5f;
+ }
+}
+
+- (void)unhighlight {
+ if (!selected) {
+ self.opacity = 0.0f;
+ }
+}
+
+- (void)drawInContext:(CGContextRef)ctx {
+ CGContextSaveGState(ctx);
+
+ [edge updateControls];
+ CGPoint source = NSPointToCGPoint([transformer toScreen:[[edge source] point]]);
+ CGPoint target = NSPointToCGPoint([transformer toScreen:[[edge target] point]]);
+ CGPoint mid = NSPointToCGPoint([transformer toScreen:[edge mid]]);
+ CGPoint cp1 = NSPointToCGPoint([transformer toScreen:[edge cp1]]);
+ CGPoint cp2 = NSPointToCGPoint([transformer toScreen:[edge cp2]]);
+
+ float dx = (target.x - source.x);
+ float dy = (target.y - source.y);
+
+ // draw a circle at the midpoint
+ CGRect mid_rect = CGRectMake(mid.x-3.0f, mid.y-3.0f, 6.0f, 6.0f);
+ CGContextAddEllipseInRect(ctx, mid_rect);
+ CGContextSetLineWidth(ctx, 1.0f);
+ CGContextSetRGBFillColor(ctx, 1.0f, 1.0f, 1.0f, 0.5f);
+ CGContextSetRGBStrokeColor(ctx, 0.0f, 0.0f, 1.0f, 0.5f);
+ CGContextDrawPath(ctx, kCGPathFillStroke);
+
+
+ CGContextSetShouldAntialias(ctx, YES);
+
+ // compute size of control circles
+ float cdist;
+ if (dx == 0 && dy == 0) cdist = [transformer scaleToScreen:edge.weight];
+ else cdist = sqrt(dx*dx + dy*dy) * edge.weight;
+
+ // if basic bend, draw blue, if inout, draw green
+ if ([edge bendMode] == EdgeBendModeBasic) CGContextSetRGBStrokeColor(ctx, 0, 0, 1, 0.4f);
+ else CGContextSetRGBStrokeColor(ctx, 0, 0.7f, 0, 0.4f);
+
+ // draw source control circle
+ CGRect ellipse1 = CGRectMake(source.x-cdist, source.y-cdist, cdist*2.0f, cdist*2.0f);
+ CGContextAddEllipseInRect(ctx, ellipse1);
+ if (dx!=0 || dy!=0) {
+ CGRect ellipse2 = CGRectMake(target.x-cdist, target.y-cdist, cdist*2.0f, cdist*2.0f);
+ CGContextAddEllipseInRect(ctx, ellipse2);
+ }
+
+ CGContextStrokePath(ctx);
+
+ float handleRad = [EdgeControlLayer handleRadius];
+
+ // handles
+ CGRect ctrl1 = CGRectMake(cp1.x-handleRad, cp1.y-handleRad, 2*handleRad, 2*handleRad);
+ CGRect ctrl2 = CGRectMake(cp2.x-handleRad, cp2.y-handleRad, 2*handleRad, 2*handleRad);
+
+ CGContextSetRGBFillColor(ctx, 1.0f, 1.0f, 1.0f, 0.8f);
+
+ // draw a line from source vertex to first handle
+ if ([edge bendMode] == EdgeBendModeInOut) {
+ if ([edge outAngle] % 45 == 0) CGContextSetRGBStrokeColor(ctx, 1, 0, 1, 0.6f);
+ else CGContextSetRGBStrokeColor(ctx, 0, 0.7f, 0, 0.4f);
+ } else {
+ if ([edge bend] % 45 == 0) CGContextSetRGBStrokeColor(ctx, 1, 0, 1, 0.6f);
+ else CGContextSetRGBStrokeColor(ctx, 0, 0, 1, 0.4f);
+ }
+
+ CGContextMoveToPoint(ctx, source.x, source.y);
+ CGContextAddLineToPoint(ctx, cp1.x, cp1.y);
+ CGContextStrokePath(ctx);
+
+ CGContextAddEllipseInRect(ctx, ctrl1);
+ CGContextDrawPath(ctx, kCGPathFillStroke);
+
+
+ // draw a line from target vertex to second handle
+ if ([edge bendMode] == EdgeBendModeInOut) {
+ if ([edge inAngle] % 45 == 0) CGContextSetRGBStrokeColor(ctx, 1, 0, 1, 0.6f);
+ else CGContextSetRGBStrokeColor(ctx, 0, 0.7f, 0, 0.4f);
+ } else {
+ if ([edge bend] % 45 == 0) CGContextSetRGBStrokeColor(ctx, 1, 0, 1, 0.6f);
+ else CGContextSetRGBStrokeColor(ctx, 0, 0, 1, 0.4f);
+ }
+
+ CGContextMoveToPoint(ctx, target.x, target.y);
+ CGContextAddLineToPoint(ctx, cp2.x, cp2.y);
+ CGContextStrokePath(ctx);
+
+ CGContextAddEllipseInRect(ctx, ctrl2);
+ CGContextDrawPath(ctx, kCGPathFillStroke);
+
+ CGContextRestoreGState(ctx);
+}
+
++ (float)handleRadius { return 4.0f; }
+
+@end
diff --git a/tikzit/src/osx/EdgeStyle+Coder.h b/tikzit/src/osx/EdgeStyle+Coder.h
new file mode 100644
index 0000000..e35c18f
--- /dev/null
+++ b/tikzit/src/osx/EdgeStyle+Coder.h
@@ -0,0 +1,30 @@
+//
+// EdgeStyle+Coder.h
+// TikZiT
+//
+// Copyright 2011 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#import <Cocoa/Cocoa.h>
+#import "EdgeStyle.h"
+
+@interface EdgeStyle (Coder)
+- (id)initWithCoder:(NSCoder*)coder;
+- (void)encodeWithCoder:(NSCoder*)coder;
+@end
diff --git a/tikzit/src/osx/EdgeStyle+Coder.m b/tikzit/src/osx/EdgeStyle+Coder.m
new file mode 100644
index 0000000..208fac0
--- /dev/null
+++ b/tikzit/src/osx/EdgeStyle+Coder.m
@@ -0,0 +1,50 @@
+//
+// EdgeStyle+Coder.m
+// TikZiT
+//
+// Copyright 2011 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#import "EdgeStyle+Coder.h"
+
+@implementation EdgeStyle (Coder)
+
+- (id)initWithCoder:(NSCoder*)coder {
+ [super init];
+
+ name = [coder decodeObjectForKey:@"name"];
+ category = [coder decodeObjectForKey:@"category"];
+ headStyle = [coder decodeIntForKey:@"headStyle"];
+ tailStyle = [coder decodeIntForKey:@"tailStyle"];
+ decorationStyle = [coder decodeIntForKey:@"decorationStyle"];
+ thickness = [coder decodeFloatForKey:@"thickness"];
+
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder*)coder {
+ [coder encodeObject:name forKey:@"name"];
+ [coder encodeObject:category forKey:@"category"];
+ [coder encodeInt:headStyle forKey:@"headStyle"];
+ [coder encodeInt:tailStyle forKey:@"tailStyle"];
+ [coder encodeInt:decorationStyle forKey:@"decorationStyle"];
+ [coder encodeFloat:thickness forKey:@"thickness"];
+}
+
+@end
diff --git a/tikzit/src/osx/Graph+Coder.h b/tikzit/src/osx/Graph+Coder.h
new file mode 100644
index 0000000..1404fc2
--- /dev/null
+++ b/tikzit/src/osx/Graph+Coder.h
@@ -0,0 +1,17 @@
+//
+// Graph+Coder.h
+// TikZiT
+//
+// Created by Aleks Kissinger on 27/04/2010.
+// Copyright 2010 __MyCompanyName__. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "Graph.h"
+
+@interface Graph (Coder)
+
+- (id)initWithCoder:(NSCoder*)coder;
+- (void)encodeWithCoder:(NSCoder*)coder;
+
+@end
diff --git a/tikzit/src/osx/Graph+Coder.m b/tikzit/src/osx/Graph+Coder.m
new file mode 100644
index 0000000..6a3f650
--- /dev/null
+++ b/tikzit/src/osx/Graph+Coder.m
@@ -0,0 +1,25 @@
+//
+// Graph+Coder.m
+// TikZiT
+//
+// Created by Aleks Kissinger on 27/04/2010.
+// Copyright 2010 __MyCompanyName__. All rights reserved.
+//
+
+#import "Graph+Coder.h"
+#import "TikzGraphAssembler.h"
+
+@implementation Graph(Coder)
+
+- (id)initWithCoder:(NSCoder*)coder {
+ NSString *tikz = [coder decodeObject];
+ TikzGraphAssembler *ass = [[TikzGraphAssembler alloc] init];
+ [ass parseTikz:tikz forGraph:self];
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder*)coder {
+ [coder encodeObject:[self tikz]];
+}
+
+@end
diff --git a/tikzit/src/osx/GraphicsView.h b/tikzit/src/osx/GraphicsView.h
new file mode 100644
index 0000000..ddd005f
--- /dev/null
+++ b/tikzit/src/osx/GraphicsView.h
@@ -0,0 +1,128 @@
+//
+// GraphicsView.h
+// TikZiT
+//
+// Copyright 2010 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#import <Cocoa/Cocoa.h>
+#import <QuartzCore/CoreAnimation.h>
+#import "PickSupport.h"
+#import "Grid.h"
+#import "Transformer.h"
+#import "Graph.h"
+#import "NodeStyle.h"
+#import "StylePaletteController.h"
+#import "ToolPaletteController.h"
+#import "SelectBoxLayer.h"
+
+// mouse modes, corresponding to different tools. format: (tool)[sub-mode]Mode
+typedef enum {
+ SelectMode = 0x10,
+ SelectBoxMode = 0x11,
+ SelectMoveMode = 0x12,
+ SelectEdgeBendMode = 0x14,
+
+ NodeMode = 0x20,
+
+ EdgeMode = 0x40,
+ EdgeDragMode = 0x41,
+
+ CropMode = 0x80,
+ CropDragMode = 0x81
+} MouseMode;
+
+@class TikzSourceController;
+
+@interface GraphicsView : NSView {
+ BOOL enabled;
+
+ IBOutlet NSApplication *application;
+ StylePaletteController *stylePaletteController;
+ ToolPaletteController *toolPaletteController;
+
+ BOOL frameMoveMode;
+
+ Graph *graph;
+ NSString *graphTikzOnMouseDown;
+ PickSupport *pickSupport;
+ //NSMapTable *nodeSelectionLayers;
+ NSMapTable *edgeControlLayers;
+ NSMapTable *nodeLayers;
+ NSPoint dragOrigin;
+ NSPoint dragTarget;
+ NSPoint oldTransformerOrigin;
+ NSPoint oldMainOrigin;
+ NSRect oldBounds;
+ //NSRect selectionBox;
+ Transformer *transformer;
+
+ CALayer *mainLayer;
+ CALayer *gridLayer;
+ CALayer *graphLayer;
+ CALayer *hudLayer;
+ SelectBoxLayer *selectionLayer;
+
+ MouseMode mouseMode;
+ Node *leaderNode;
+ Grid *grid;
+
+ Edge *modifyEdge;
+ BOOL firstControlPoint;
+
+ int bboxLeftRight;
+ int bboxBottomTop;
+
+ NSUndoManager *documentUndoManager;
+ NSPoint startPoint;
+
+ TikzSourceController *tikzSourceController;
+}
+
+@property BOOL enabled;
+@property Graph *graph;
+@property IBOutlet TikzSourceController *tikzSourceController;
+@property (readonly) Transformer *transformer;
+@property (readonly) PickSupport *pickSupport;
+
+- (void)setDocumentUndoManager:(NSUndoManager*)um;
+- (void)applyStyleToSelectedNodes:(NodeStyle*)style;
+- (void)applyStyleToSelectedEdges:(EdgeStyle*)style;
+
+- (void)updateMouseMode;
+- (void)refreshLayers;
+
+//- (void)registerUndo:(GraphChange *)change withActionName:(NSString*)name;
+- (void)registerUndo:(NSString*)oldTikz withActionName:(NSString*)name;
+//- (void)undoGraphChange:(GraphChange *)change;
+- (void)undoGraphChange:(NSString*)oldTikz;
+- (void)postGraphChange;
+- (void)postSelectionChange;
+
+- (void)deselectAll:(id)sender;
+- (void)selectAll:(id)sender;
+- (void)cut:(id)sender;
+- (void)copy:(id)sender;
+- (void)paste:(id)sender;
+- (void)delete:(id)sender;
+- (void)flipHorizonal:(id)sender;
+- (void)flipVertical:(id)sender;
+- (void)reverseEdgeDirection:(id)sender;
+
+@end
diff --git a/tikzit/src/osx/GraphicsView.m b/tikzit/src/osx/GraphicsView.m
new file mode 100644
index 0000000..38ed1f0
--- /dev/null
+++ b/tikzit/src/osx/GraphicsView.m
@@ -0,0 +1,1142 @@
+//
+// GraphicsView.m
+// TikZiT
+//
+// Copyright 2010 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#import "GraphicsView.h"
+#import "util.h"
+#import "CALayer+DrawLabel.h"
+
+#import "NodeSelectionLayer.h"
+#import "NodeLayer.h"
+#import "EdgeControlLayer.h"
+#import "AppDelegate.h"
+#import "TikzGraphAssembler.h"
+#import "TikzSourceController.h"
+
+@interface GraphicsView (Private)
+- (void)setupLayers;
+- (void)addNodeLayers:(Node*)n;
+- (void)addEdgeLayers:(Edge*)e;
+- (void)removeNodeLayers:(Node*)n;
+- (void)resetMainOrigin;
+- (void)setMainOrigin:(NSPoint)o;
+@end
+
+static CGColorRef cgGrayColor, cgWhiteColor, cgClearColor = nil;
+
+
+@implementation GraphicsView
+
+@synthesize enabled, transformer, pickSupport, tikzSourceController;
+
+- (void)postGraphChange {
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"GraphChanged"
+ object:self];
+ [self postSelectionChange];
+}
+
+- (void)postSelectionChange {
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"SelectionChanged"
+ object:self];
+}
+
+- (id)initWithFrame:(NSRect)frame {
+ self = [super initWithFrame:frame];
+ if (self) {
+ if (cgClearColor == nil) {
+ cgClearColor = CGColorGetConstantColor(kCGColorClear);
+ cgGrayColor = CGColorCreateGenericGray(0.5f, 0.5f);
+ cgWhiteColor = CGColorCreateGenericRGB(1, 1, 1, 1);
+ }
+
+ transformer = [[Transformer alloc] init];
+ mouseMode = SelectMode;
+ grid = [Grid gridWithSpacing:1.0f
+ subdivisions:4
+ transformer:transformer];
+ [grid setSize:NSSizeFromCGSize([gridLayer bounds].size)];
+ [transformer setScale:PIXELS_PER_UNIT];
+
+ [self setupLayers];
+
+ leaderNode = nil;
+ pickSupport = [[PickSupport alloc] init];
+ frameMoveMode = NO;
+
+ enabled = YES;
+ [self setGraph:[Graph graph]];
+ }
+ return self;
+}
+
+- (void)awakeFromNib {
+ AppDelegate *del = [application delegate];
+ stylePaletteController = [del stylePaletteController];
+ toolPaletteController = [del toolPaletteController];
+ [self refreshLayers];
+ [self postGraphChange];
+}
+
+- (void)setupLayers {
+ mainLayer = [CALayer layer];
+ [mainLayer setBackgroundColor:cgWhiteColor];
+ [mainLayer setFrame:CGRectIntegral(NSRectToCGRect([self bounds]))];
+ [mainLayer setOpacity:1.0f];
+ [self setLayer:mainLayer];
+ [self resetMainOrigin];
+
+ gridLayer = [CALayer layer];
+ [gridLayer setDelegate:grid];
+ [gridLayer setOpacity:0.3f];
+ [mainLayer addSublayer:gridLayer];
+
+ graphLayer = [CALayer layer];
+ [graphLayer setDelegate:self];
+ [mainLayer addSublayer:graphLayer];
+
+ hudLayer = [CALayer layer];
+ [mainLayer addSublayer:hudLayer];
+
+ selectionLayer = [SelectBoxLayer layer];
+ [mainLayer addSublayer:selectionLayer];
+
+ [transformer setOrigin:NSMakePoint(NSMidX([self bounds]),NSMidY([self bounds]))];
+ oldBounds = [self bounds];
+ [self refreshLayers];
+}
+
+// Lion resume feature
+//- (void)encodeRestorableStateWithCoder:(NSCoder*)coder {
+// NSLog(@"got encode request");
+//}
+//- (void)restoreStateWithCoder:(NSCoder*)coder {
+// NSLog(@"got decode request");
+//}
+
+- (void)registerUndo:(NSString*)oldTikz withActionName:(NSString*)nm {
+ [documentUndoManager registerUndoWithTarget:self
+ selector:@selector(undoGraphChange:)
+ object:oldTikz];
+ [documentUndoManager setActionName:nm];
+}
+
+- (void)revertToTikz:(NSString*)tikz {
+ [tikzSourceController setTikz:tikz];
+ [tikzSourceController tryParseTikz];
+ [self refreshLayers];
+ [self postGraphChange];
+}
+
+
+- (void)undoGraphChange:(NSString*)oldTikz {
+ NSString *currentTikz = [graph tikz];
+ [self revertToTikz:oldTikz];
+ [documentUndoManager registerUndoWithTarget:self
+ selector:@selector(undoGraphChange:)
+ object:currentTikz];
+}
+
+- (void)setGraph:(Graph*)gr {
+ graph = gr;
+
+ NSEnumerator *e;
+ CALayer *layer;
+
+ e = [edgeControlLayers objectEnumerator];
+ while (layer = [e nextObject]) [layer removeFromSuperlayer];
+ edgeControlLayers = [NSMapTable mapTableWithStrongToStrongObjects];
+
+
+ e = [nodeLayers objectEnumerator];
+ while (layer = [e nextObject]) [layer removeFromSuperlayer];
+ nodeLayers = [NSMapTable mapTableWithStrongToStrongObjects];
+
+ for (Node *n in [graph nodes]) {
+ [n attachStyleFromTable:[stylePaletteController nodeStyles]];
+ [self addNodeLayers:n];
+ }
+
+ for (Edge *e in [graph edges]) {
+ [e setAttributesFromData];
+ [e attachStyleFromTable:[stylePaletteController edgeStyles]];
+ [self addEdgeLayers:e];
+ }
+}
+
+- (Graph*)graph { return graph; }
+
+- (void)setMainOrigin:(NSPoint)o {
+ o.x = round(o.x);
+ o.y = round(o.y);
+ CGRect rect = [mainLayer frame];
+ rect.origin = NSPointToCGPoint(o);
+ [mainLayer setFrame:rect];
+}
+
+- (void)resetMainOrigin {
+ NSRect bds = [self bounds];
+ bds.origin.x -= bds.size.width;
+ bds.origin.y -= bds.size.height;
+ bds.size.width *= 3;
+ bds.size.height *= 3;
+ [mainLayer setFrame:NSRectToCGRect([self bounds])];
+}
+
+- (void)refreshLayers {
+ [gridLayer setFrame:[mainLayer frame]];
+ [graphLayer setFrame:[mainLayer frame]];
+ [hudLayer setFrame:[mainLayer frame]];
+ [selectionLayer setFrame:[mainLayer frame]];
+
+ if (enabled) {
+ [hudLayer setBackgroundColor:cgClearColor];
+ } else {
+ [hudLayer setBackgroundColor:cgGrayColor];
+ }
+
+ [grid setSize:NSSizeFromCGSize([gridLayer bounds].size)];
+ [gridLayer setNeedsDisplay];
+ [graphLayer setNeedsDisplay];
+ [hudLayer setNeedsDisplay];
+
+ NSEnumerator *e = [edgeControlLayers objectEnumerator];
+ CALayer *layer;
+ while (layer = [e nextObject]) {
+ [layer setFrame:[graphLayer frame]];
+ [layer setNeedsDisplay];
+ }
+}
+
+
+- (void)viewDidEndLiveResize {
+ [super viewDidEndLiveResize];
+ NSPoint o = [transformer origin];
+ o.x += round(([self bounds].size.width - oldBounds.size.width)/2.0f);
+ o.y += round(([self bounds].size.height - oldBounds.size.height)/2.0f);
+ [transformer setOrigin:o];
+ oldBounds = [self bounds];
+ [self refreshLayers];
+}
+
+- (void)applyStyleToSelectedNodes:(NodeStyle*)style {
+ NSString *oldTikz = [graph tikz];
+
+ for (Node *n in [pickSupport selectedNodes]) {
+ [n setStyle:style];
+ [[nodeLayers objectForKey:n] setNeedsDisplay];
+ }
+
+ [self registerUndo:oldTikz withActionName:@"Apply Style to Nodes"];
+ [self refreshLayers];
+ [self postGraphChange];
+}
+
+- (void)applyStyleToSelectedEdges:(EdgeStyle*)style {
+ NSString *oldTikz = [graph tikz];
+
+ for (Edge *e in [pickSupport selectedEdges]) {
+ [e setStyle:style];
+ }
+
+ [self registerUndo:oldTikz withActionName:@"Apply Style to Edges"];
+ [self refreshLayers];
+ [self postGraphChange];
+}
+
+- (void)addNodeLayers:(Node*)n {
+ // add a node to the graph
+ [graph addNode:n];
+
+ NSPoint pt = [transformer toScreen:[n point]];
+
+ // add a node layer
+ NodeLayer *nl = [[NodeLayer alloc] initWithNode:n transformer:transformer];
+ [nl setCenter:pt];
+ [nodeLayers setObject:nl forKey:n];
+ [graphLayer addSublayer:nl];
+ [nl setNeedsDisplay];
+}
+
+- (void)removeNodeLayers:(Node*)n {
+ [[nodeLayers objectForKey:n] removeFromSuperlayer];
+ [nodeLayers removeObjectForKey:n];
+}
+
+- (void)addEdgeLayers:(Edge *)e {
+ [graph addEdge:e];
+ EdgeControlLayer *ecl = [[EdgeControlLayer alloc] initWithEdge:e andTransformer:transformer];
+ [edgeControlLayers setObject:ecl forKey:e];
+ [ecl setFrame:CGRectMake(10, 10, 100, 100)];
+ [hudLayer addSublayer:ecl];
+ [ecl setNeedsDisplay];
+}
+
+- (void)removeEdgeLayers:(Edge*)e {
+ [[edgeControlLayers objectForKey:e] removeFromSuperlayer];
+ [edgeControlLayers removeObjectForKey:e];
+ [self refreshLayers];
+}
+
+- (BOOL)circleWithCenter:(NSPoint)center andRadius:(float)radius containsPoint:(NSPoint)p {
+ float dx = center.x - p.x;
+ float dy = center.y - p.y;
+ return (dx*dx + dy*dy) <= radius*radius;
+}
+
+- (BOOL)node:(Node*)node containsPoint:(NSPoint)p {
+ NodeLayer *nl = [nodeLayers objectForKey:node];
+ return [nl nodeContainsPoint:p];
+}
+
+- (BOOL)edge:(Edge*)edge containsPoint:(NSPoint)p {
+// NSPoint center = [transformer toScreen:edge.mid];
+// float dx = center.x - p.x;
+// float dy = center.y - p.y;
+// float radius = 5.0f; // tolerence for clicks
+// return (dx*dx + dy*dy) <= radius*radius;
+
+ CGContextRef ctx = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
+
+ // Save the graphics state before doing the hit detection.
+ CGContextSaveGState(ctx);
+
+ NSPoint src = [transformer toScreen:[[edge source] point]];
+ NSPoint targ = [transformer toScreen:[[edge target] point]];
+ NSPoint cp1 = [transformer toScreen:[edge cp1]];
+ NSPoint cp2 = [transformer toScreen:[edge cp2]];
+
+ CGContextSetLineWidth(ctx, 8.0f);
+
+ CGContextMoveToPoint(ctx, src.x, src.y);
+ CGContextAddCurveToPoint(ctx, cp1.x, cp1.y, cp2.x, cp2.y, targ.x, targ.y);
+
+ BOOL containsPoint = CGContextPathContainsPoint(ctx, NSPointToCGPoint(p), kCGPathStroke);
+
+ CGContextSetRGBStrokeColor(ctx, 0, 0, 0, 0);
+
+ CGContextStrokePath(ctx);
+ //CGContextFlush(ctx);
+ CGContextRestoreGState(ctx);
+
+ return containsPoint;
+}
+
+- (void)shiftNodes:(NSSet*)set from:(NSPoint)source to:(NSPoint)dest {
+ float dx = dest.x - source.x;
+ float dy = dest.y - source.y;
+
+ for (Node *n in set) {
+ NSPoint p = [transformer toScreen:[n point]];
+ p = [grid snapScreenPoint:NSMakePoint(p.x+dx, p.y+dy)];
+ [n setPoint:[transformer fromScreen:p]];
+ }
+}
+
+
+- (void)mouseDown:(NSEvent*)theEvent {
+ if (!enabled) return;
+
+ [self updateMouseMode];
+
+ dragOrigin = [self convertPoint:[theEvent locationInWindow] fromView:nil];
+ dragTarget = dragOrigin;
+
+ graphTikzOnMouseDown = [graph tikz];
+
+ if ([theEvent modifierFlags] & NSCommandKeyMask) {
+ oldTransformerOrigin = [transformer origin];
+ oldMainOrigin = [self frame].origin;
+ frameMoveMode = YES;
+ return;
+ }
+
+ if (mouseMode == SelectMode) {
+ [selectionLayer setActive:YES];
+ [selectionLayer setSelectBox:NSRectAroundPoints(dragOrigin, dragOrigin)];
+ [selectionLayer setNeedsDisplay];
+
+ modifyEdge = nil;
+ NSPoint cp1, cp2;
+ for (Edge *e in [pickSupport selectedEdges]) {
+ cp1 = [transformer toScreen:[e cp1]];
+ cp2 = [transformer toScreen:[e cp2]];
+ if ([self circleWithCenter:cp1
+ andRadius:[EdgeControlLayer handleRadius]
+ containsPoint:dragOrigin])
+ {
+ mouseMode = SelectEdgeBendMode;
+ modifyEdge = e;
+ firstControlPoint = YES;
+ break;
+ } else if ([self circleWithCenter:cp2
+ andRadius:[EdgeControlLayer handleRadius]
+ containsPoint:dragOrigin])
+ {
+ mouseMode = SelectEdgeBendMode;
+ modifyEdge = e;
+ firstControlPoint = NO;
+ break;
+ }
+ }
+
+ if (modifyEdge == nil) { // skip all the rest if we're modifying an edge
+
+ leaderNode = nil;
+
+ // in first pass, try to find a leader node, under the mouse
+ for (Node* n in [graph nodes]) {
+ if ([self node:n containsPoint:dragOrigin]) {
+ leaderNode = n;
+ [gridLayer setOpacity:1.0f];
+ break;
+ }
+ }
+
+ // if we found one, deselect the others (if appropriate) and go to move mode
+ if (leaderNode != nil) {
+ startPoint = [leaderNode point];
+
+ // if we select a node, we should always deselect all edges:
+ for (Edge *e in [graph edges]) [[edgeControlLayers objectForKey:e] deselect];
+ [pickSupport deselectAllEdges];
+
+ BOOL shouldDeselect =
+ !([theEvent modifierFlags] & NSShiftKeyMask)
+ && ![pickSupport isNodeSelected:leaderNode];
+ for (Node *n in [graph nodes]) {
+ if (n != leaderNode && shouldDeselect) {
+ [pickSupport deselectNode:n];
+ [[[nodeLayers objectForKey:n] selection] deselect];
+ }
+ }
+
+ // ensure the leader node is actually selected
+ if (![pickSupport isNodeSelected:leaderNode]) {
+ [pickSupport selectNode:leaderNode];
+ [[[nodeLayers objectForKey:leaderNode] selection] select];
+ }
+
+
+ // put us in move mode
+ mouseMode = SelectMoveMode;
+ } else {
+ mouseMode = SelectBoxMode;
+
+ // if we didn't select a node, start hunting for an edge to select
+ BOOL shouldDeselect = !([theEvent modifierFlags] & NSShiftKeyMask);
+
+ if (shouldDeselect) {
+ [pickSupport deselectAllEdges];
+ for (Edge *e in graph.edges) [[edgeControlLayers objectForKey:e] deselect];
+ }
+
+ for (Edge* e in [graph edges]) {
+ // find the first node under the pointer, select it, show its controls
+ // and deselect all others if shift isn't down
+ if ([self edge:e containsPoint:dragOrigin]) {
+ for (Node *n in [pickSupport selectedNodes]) [[[nodeLayers objectForKey:n] selection] deselect];
+
+ [pickSupport deselectAllNodes];
+ [pickSupport selectEdge:e];
+ [[edgeControlLayers objectForKey:e] select];
+ break;
+ }
+ } // end for e in [graph edges]
+ } // end if leaderNode == nil
+ } // end if modifyEdge == nil
+
+ } else if (mouseMode == NodeMode) {
+ // do nothing...
+ } else if (mouseMode == EdgeMode) {
+ for (Node *n in [graph nodes]) {
+ if ([self node:n containsPoint:dragOrigin]) {
+ [[[nodeLayers objectForKey:n] selection] highlight];
+ }
+ }
+ mouseMode = EdgeDragMode;
+ } else if (mouseMode == CropMode) {
+ if ([graph hasBoundingBox]) {
+ float fudge = 3;
+
+ NSRect bb = [graph boundingBox];
+ NSPoint bl = [transformer toScreen:bb.origin];
+ NSPoint tr = [transformer
+ toScreen:NSMakePoint(bb.origin.x+bb.size.width,
+ bb.origin.y+bb.size.height)];
+ if (dragOrigin.x > bl.x-fudge && dragOrigin.x < tr.x+fudge &&
+ dragOrigin.y > tr.y-fudge && dragOrigin.y < tr.y+fudge)
+ {
+ bboxBottomTop = 1;
+ } else if (dragOrigin.x > bl.x-fudge && dragOrigin.x < tr.x+fudge &&
+ dragOrigin.y > bl.y-fudge && dragOrigin.y < bl.y+fudge)
+ {
+ bboxBottomTop = -1;
+ } else {
+ bboxBottomTop = 0;
+ }
+
+ if (dragOrigin.y > bl.y-fudge && dragOrigin.y < tr.y+fudge &&
+ dragOrigin.x > tr.x-fudge && dragOrigin.x < tr.x+fudge)
+ {
+ bboxLeftRight = 1;
+ } else if (dragOrigin.y > bl.y-fudge && dragOrigin.y < tr.y+fudge &&
+ dragOrigin.x > bl.x-fudge && dragOrigin.x < bl.x+fudge)
+ {
+ bboxLeftRight = -1;
+ } else {
+ bboxLeftRight = 0;
+ }
+
+ if (bboxBottomTop != 0 || bboxLeftRight != 0) {
+ mouseMode = CropDragMode;
+ }
+ }
+ } else {
+ printf("WARNING: MOUSE DOWN IN INVALID MODE.\n");
+ }
+
+ [self refreshLayers];
+}
+
+- (void)mouseDragged:(NSEvent *)theEvent {
+ if (!enabled) return;
+ dragTarget = [self convertPoint:[theEvent locationInWindow] fromView:nil];
+
+ if (frameMoveMode) {
+ NSPoint newTransOrigin, newMainOrigin;
+ NSPoint diff = NSMakePoint(dragTarget.x - dragOrigin.x, dragTarget.y - dragOrigin.y);
+ newTransOrigin.x = oldTransformerOrigin.x + diff.x;
+ newTransOrigin.y = oldTransformerOrigin.y + diff.y;
+ newMainOrigin.x = oldMainOrigin.x + diff.x;
+ newMainOrigin.y = oldMainOrigin.y + diff.y;
+
+ [CATransaction begin];
+ [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
+ [self setMainOrigin:newMainOrigin];
+ [CATransaction commit];
+
+ [transformer setOrigin:newTransOrigin];
+ return;
+ }
+
+ if (mouseMode == SelectBoxMode) {
+ [selectionLayer setSelectBox:NSRectAroundPoints(dragOrigin, dragTarget)];
+ [selectionLayer setNeedsDisplay];
+
+ for (Node* n in [graph nodes]) {
+ if (NSPointInRect([transformer toScreen:[n point]], [selectionLayer selectBox])) {
+ [[[nodeLayers objectForKey:n] selection] highlight];
+ } else if (!([theEvent modifierFlags] & NSShiftKeyMask)) {
+ [[[nodeLayers objectForKey:n] selection] unhighlight];
+ }
+ }
+ } else if (mouseMode == SelectMoveMode) {
+ if (leaderNode != nil) {
+ [self shiftNodes:[pickSupport selectedNodes]
+ from:[transformer toScreen:[leaderNode point]]
+ to:dragTarget];
+ } else {
+ printf("WARNING: LEADER NODE SHOULD NOT BE NIL.\n");
+ }
+
+ [self refreshLayers];
+ } else if (mouseMode == SelectEdgeBendMode) {
+ NSPoint src = [transformer toScreen:[[modifyEdge source] point]];
+ NSPoint targ = [transformer toScreen:[[modifyEdge target] point]];
+ float dx1 = targ.x - src.x;
+ float dy1 = targ.y - src.y;
+ float dx2, dy2;
+ if (firstControlPoint) {
+ dx2 = dragTarget.x - src.x;
+ dy2 = dragTarget.y - src.y;
+ } else {
+ dx2 = dragTarget.x - targ.x;
+ dy2 = dragTarget.y - targ.y;
+ }
+ float base_dist = sqrt(dx1*dx1 + dy1*dy1);
+ float handle_dist = sqrt(dx2*dx2 + dy2*dy2);
+ float wcourseness = 0.1f;
+
+ if (![modifyEdge isSelfLoop]) {
+ if (base_dist != 0) {
+ [modifyEdge setWeight:roundToNearest(wcourseness, handle_dist/base_dist)];
+ //round(handle_dist / (base_dist*wcourseness)) * wcourseness;
+ } else {
+ [modifyEdge setWeight:
+ roundToNearest(wcourseness, [transformer scaleFromScreen:handle_dist])];
+ }
+ }
+
+
+ float control_angle = good_atan(dx2, dy2);
+
+ int bcourseness = 15;
+
+ if ([modifyEdge bendMode] == EdgeBendModeBasic) {
+ float bnd;
+ float base_angle = good_atan(dx1, dy1);
+ if (firstControlPoint) {
+ bnd = base_angle - control_angle;
+ } else {
+ bnd = control_angle - base_angle + pi;
+ if (bnd > pi) bnd -= 2*pi;
+ }
+
+ [modifyEdge setBend:round(bnd * (180.0f / pi) *
+ (1.0f / (float)bcourseness)) *
+ bcourseness];
+ } else {
+ int bnd = round(control_angle * (180.0f / pi) *
+ (1.0f / (float)bcourseness)) *
+ bcourseness;
+ if (firstControlPoint) {
+ if ([theEvent modifierFlags] & NSAlternateKeyMask) {
+ if ([modifyEdge isSelfLoop]) {
+ [modifyEdge setInAngle:[modifyEdge inAngle] +
+ (bnd - [modifyEdge outAngle])];
+ } else {
+ [modifyEdge setInAngle:[modifyEdge inAngle] -
+ (bnd - [modifyEdge outAngle])];
+ }
+ }
+
+ [modifyEdge setOutAngle:bnd];
+ } else {
+ if (theEvent.modifierFlags & NSAlternateKeyMask) {
+ if ([modifyEdge isSelfLoop]) {
+ [modifyEdge setOutAngle:[modifyEdge outAngle] +
+ (bnd - [modifyEdge inAngle])];
+ } else {
+ [modifyEdge setOutAngle:[modifyEdge outAngle] -
+ (bnd - [modifyEdge inAngle])];
+ }
+ }
+
+ [modifyEdge setInAngle:bnd];
+ }
+ }
+
+ [self refreshLayers];
+ } else if (mouseMode == NodeMode) {
+ // do nothing...
+ } else if (mouseMode == EdgeDragMode) {
+ for (Node *n in [graph nodes]) {
+ if ([self node:n containsPoint:dragOrigin] ||
+ [self node:n containsPoint:dragTarget])
+ {
+ [[[nodeLayers objectForKey:n] selection] highlight];
+ } else {
+ [[[nodeLayers objectForKey:n] selection] unhighlight];
+ }
+ }
+
+ [self refreshLayers];
+ } else if (mouseMode == CropMode || mouseMode == CropDragMode) {
+ NSPoint p1 = [transformer fromScreen:[grid snapScreenPoint:dragOrigin]];
+ NSPoint p2 = [transformer fromScreen:[grid snapScreenPoint:dragTarget]];
+
+ NSRect bbox;
+ if (mouseMode == CropDragMode) {
+ bbox = [graph boundingBox];
+ if (bboxBottomTop == -1) {
+ float dy = p2.y - bbox.origin.y;
+ bbox.origin.y += dy;
+ bbox.size.height -= dy;
+ } else if (bboxBottomTop == 1) {
+ float dy = p2.y - (bbox.origin.y + bbox.size.height);
+ bbox.size.height += dy;
+ }
+
+ if (bboxLeftRight == -1) {
+ float dx = p2.x - bbox.origin.x;
+ bbox.origin.x += dx;
+ bbox.size.width -= dx;
+ } else if (bboxLeftRight == 1) {
+ float dx = p2.x - (bbox.origin.x + bbox.size.width);
+ bbox.size.width += dx;
+ }
+ } else {
+ bbox = NSRectAroundPoints(p1, p2);
+ }
+
+ [graph setBoundingBox:bbox];
+ [self postGraphChange];
+ [self refreshLayers];
+ } else {
+ printf("WARNING: MOUSE DRAGGED IN INVALID MODE.\n");
+ }
+}
+
+- (void)mouseUp:(NSEvent*)theEvent {
+ if (!enabled) return;
+
+ if (frameMoveMode) {
+ [CATransaction begin];
+ [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
+ [self resetMainOrigin];
+ [self refreshLayers];
+ [CATransaction commit];
+ frameMoveMode = NO;
+ return;
+ }
+
+ dragTarget = [self convertPoint:[theEvent locationInWindow] fromView:nil];
+
+ if ((mouseMode & SelectMode) == SelectMode && [theEvent clickCount] == 2) {
+ for (Edge *e in [graph edges]) {
+ if ([self edge:e containsPoint:dragTarget]) {
+ if ([e bendMode] == EdgeBendModeBasic) {
+ [e convertBendToAngles];
+ [e setBendMode:EdgeBendModeInOut];
+ } else {
+ [e convertAnglesToBend];
+ [e setBendMode:EdgeBendModeBasic];
+ }
+
+ [self registerUndo:graphTikzOnMouseDown withActionName:@"Change Edge Mode"];
+ [self postGraphChange];
+ break;
+ }
+ }
+ }
+
+ if (mouseMode == SelectBoxMode) {
+ for (Node* n in [graph nodes]) {
+ if (NSPointInRect([transformer toScreen:[n point]], [selectionLayer selectBox])) {
+ [pickSupport selectNode:n];
+ [[[nodeLayers objectForKey:n] selection] select];
+ } else if (!([theEvent modifierFlags] & NSShiftKeyMask)) {
+ [pickSupport deselectNode:n];
+ [[[nodeLayers objectForKey:n] selection] deselect];
+ }
+ }
+
+ [selectionLayer setActive:NO];
+ [selectionLayer setNeedsDisplay];
+ [self postSelectionChange];
+
+ mouseMode = SelectMode;
+ } else if (mouseMode == SelectMoveMode) {
+ [gridLayer setOpacity:0.3f];
+
+ if (dragTarget.x != dragOrigin.x || dragTarget.y != dragOrigin.y) {
+ [self registerUndo:graphTikzOnMouseDown withActionName:@"Shift Nodes"];
+ }
+
+ leaderNode = nil;
+
+ [self postGraphChange];
+ mouseMode = SelectMode;
+ } else if (mouseMode == SelectEdgeBendMode) {
+ [self registerUndo:graphTikzOnMouseDown withActionName:@"Adjust Edge"];
+ [self postGraphChange];
+ mouseMode = SelectMode;
+ modifyEdge = nil;
+ } else if (mouseMode == NodeMode) {
+ NSPoint coords = [transformer fromScreen:[grid snapScreenPoint:dragTarget]];
+ Node *n = [Node nodeWithPoint:coords];
+ [n setStyle:[stylePaletteController activeNodeStyle]];
+ [graph addNode:n];
+
+ [self registerUndo:graphTikzOnMouseDown withActionName:@"Add Node"];
+
+ [self addNodeLayers:n];
+ [self postGraphChange];
+ } else if (mouseMode == EdgeDragMode) {
+ Node *src = nil;
+ Node *targ = nil;
+ BOOL found = NO; // don't break the loop until everything is unhighlighted
+ for (Node *n in [graph nodes]) {
+ [[[nodeLayers objectForKey:n] selection] unhighlight];
+ if (!found) {
+ if ([self node:n containsPoint:dragOrigin]) src = n;
+ if ([self node:n containsPoint:dragTarget]) targ = n;
+ if (src != nil && targ != nil) {
+ Edge *e = [Edge edgeWithSource:src andTarget:targ];
+ [e setStyle:[stylePaletteController activeEdgeStyle]];
+ [graph addEdge:e];
+ [self registerUndo:graphTikzOnMouseDown withActionName:@"Add Edge"];
+ [self addEdgeLayers:e];
+ found = YES;
+ }
+ }
+ }
+
+ [self postGraphChange];
+ mouseMode = EdgeMode;
+ } else if (mouseMode == CropMode || mouseMode == CropDragMode) {
+ if (dragOrigin.x == dragTarget.x && dragOrigin.y == dragTarget.y) {
+ [graph setBoundingBox:NSMakeRect(0, 0, 0, 0)];
+ [self registerUndo:graphTikzOnMouseDown withActionName:@"Clear Bounding Box"];
+ [self postGraphChange];
+ } else {
+ [self registerUndo:graphTikzOnMouseDown withActionName:@"Change Bounding Box"];
+ }
+
+ mouseMode = CropMode;
+ } else {
+ if (! ([theEvent modifierFlags] & NSCommandKeyMask))
+ printf("WARNING: MOUSE UP IN INVALID MODE.\n");
+ }
+
+ [self refreshLayers];
+}
+
+- (void)drawNode:(Node*)nd onLayer:(CALayer*)layer inContext:(CGContextRef)context {
+ NSPoint pt = [transformer toScreen:[nd point]];
+
+ NodeLayer *nl = [nodeLayers objectForKey:nd];
+ //[nl setStrokeWidth:2.0f];
+ [nl setCenter:pt andAnimateWhen:(mouseMode != SelectMoveMode)];
+}
+
+- (void)drawEdge:(Edge*)e onLayer:(CALayer*)layer inContext:(CGContextRef)context {
+ CGContextSaveGState(context);
+ NSPoint src = [transformer toScreen:[[e source] point]];
+ NSPoint targ = [transformer toScreen:[[e target] point]];
+ NSPoint cp1 = [transformer toScreen:[e cp1]];
+ NSPoint cp2 = [transformer toScreen:[e cp2]];
+
+ // all nodes have the same radius. this will need to be fixed
+ float sradius = 0;//(slayer.ghost) ? 0 : slayer.radius;
+ float tradius = 0;//(tlayer.ghost) ? 0 : tlayer.radius;
+
+ float sdx = cp1.x - src.x;
+ float sdy = cp1.y - src.y;
+ float sdist = sqrt(sdx*sdx + sdy*sdy);
+ float sshortx = (sdist==0) ? 0 : sdx/sdist * sradius;
+ float sshorty = (sdist==0) ? 0 : sdy/sdist * sradius;
+
+ float tdx = cp2.x - targ.x;
+ float tdy = cp2.y - targ.y;
+ float tdist = sqrt(tdx*tdx + tdy*tdy);
+ float tshortx = (tdist==0) ? 0 : tdx/sdist * tradius;
+ float tshorty = (tdist==0) ? 0 : tdy/sdist * tradius;
+
+ CGContextMoveToPoint(context, src.x+sshortx, src.y+sshorty);
+ CGContextAddCurveToPoint(context, cp1.x, cp1.y, cp2.x, cp2.y, targ.x+tshortx, targ.y+tshorty);
+
+ if ([e style] != nil) {
+ NSPoint p1,p2,p3;
+
+ // draw edge decoration
+ switch ([[e style] decorationStyle]) {
+ case ED_None:
+ break;
+ case ED_Tick:
+ p1 = [transformer toScreen:[e leftNormal]];
+ p2 = [transformer toScreen:[e rightNormal]];
+ CGContextMoveToPoint(context, p1.x, p1.y);
+ CGContextAddLineToPoint(context, p2.x, p2.y);
+ break;
+ case ED_Arrow:
+ p1 = [transformer toScreen:[e leftNormal]];
+ p2 = [transformer toScreen:[e midTan]];
+ p3 = [transformer toScreen:[e rightNormal]];
+ CGContextMoveToPoint(context, p1.x, p1.y);
+ CGContextAddLineToPoint(context, p2.x, p2.y);
+ CGContextAddLineToPoint(context, p3.x, p3.y);
+ break;
+ }
+ }
+
+
+ // OLD DRAW TICK CODE:
+ // float dx = targ.x - src.x;
+ // float dy = targ.y - src.y;
+ // float dist = sqrt(dx*dx+dy*dy);
+ // if (dist!=0) {
+ // CGContextMoveToPoint(context,
+ // round(mid.x + 4.0f * dy/dist),
+ // round(mid.y - 4.0f * dx/dist));
+ // CGContextAddLineToPoint(context,
+ // round(mid.x - 4.0f * dy/dist),
+ // round(mid.y + 4.0f * dx/dist));
+ // }
+
+
+ float lineWidth = [transformer scaleToScreen:0.04f];
+
+ CGContextSetLineWidth(context, lineWidth);
+ CGContextSetRGBStrokeColor(context, 0, 0, 0, 1);
+ CGContextStrokePath(context);
+
+ CGContextRestoreGState(context);
+
+ if ([e hasEdgeNode]) {
+ Node *en = [e edgeNode];
+ NSPoint mid = [transformer toScreen:[e mid]];
+ if (![[en label] isEqual:@""]) {
+ [layer drawLabel:[en label]
+ atPoint:mid
+ inContext:context
+ usingTrans:transformer];
+ }
+ }
+
+ EdgeControlLayer *ecl = [edgeControlLayers objectForKey:e];
+ [ecl setNeedsDisplay];
+}
+
+
+// draw the graph layer
+-(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)context {
+ for (Edge* e in [graph edges]) [self drawEdge:e onLayer:layer inContext:context];
+
+ for (Node* n in [graph nodes]) [self drawNode:n onLayer:layer inContext:context];
+
+ if ([graph hasBoundingBox]) {
+ CGRect bbox = NSRectToCGRect(NSIntegralRect(
+ [transformer rectToScreen:[graph boundingBox]]));
+ CGContextSetRGBStrokeColor(context, 1.0f, 0.7f, 0.5f, 1.0f);
+ CGContextSetLineWidth(context, 1.0f);
+ CGContextSetShouldAntialias(context, NO);
+ CGContextStrokeRect(context, bbox);
+ CGContextSetShouldAntialias(context, YES);
+ }
+
+ if (mouseMode == EdgeDragMode) {
+ CGContextMoveToPoint(context, dragOrigin.x, dragOrigin.y);
+ CGContextAddLineToPoint(context, dragTarget.x, dragTarget.y);
+ CGContextSetLineWidth(context, 2);
+ CGContextSetRGBStrokeColor(context, 0, 0, 1, 1);
+ CGContextStrokePath(context);
+ }
+}
+
+// if enabled, suppress the default "bonk" behaviour on key presses
+- (void)keyDown:(NSEvent *)theEvent {
+ if (!enabled) [super keyDown:theEvent];
+}
+
+- (void)delete:(id)sender {
+ BOOL didDelete = NO;
+ NSString *oldTikz = [graph tikz];
+
+ if ([[pickSupport selectedNodes] count] != 0) {
+ GraphChange *change = [graph removeNodes:[pickSupport selectedNodes]];
+ for (Node *n in [change affectedNodes]) [self removeNodeLayers:n];
+ for (Edge *e in [change affectedEdges]) [self removeEdgeLayers:e];
+
+ [self refreshLayers];
+ [self postGraphChange];
+ didDelete = YES;
+ }
+
+ if ([[pickSupport selectedEdges] count] != 0) {
+ [graph removeEdges:[pickSupport selectedEdges]];
+ for (Edge *e in [pickSupport selectedEdges]) [self removeEdgeLayers:e];
+ [self refreshLayers];
+ [self postGraphChange];
+ didDelete = YES;
+ }
+
+ [pickSupport deselectAllNodes];
+ [pickSupport deselectAllEdges];
+
+ if (didDelete) [self registerUndo:oldTikz withActionName:@"Delete Nodes or Edges"];
+}
+
+- (void)keyUp:(NSEvent *)theEvent {
+ if (!enabled) return;
+
+ id sender = self;
+ switch ([theEvent keyCode]) {
+ case 51: // delete
+ [self delete:sender]; // "self" is the sender
+ break;
+ case 1: // S
+ [toolPaletteController setSelectedTool:TikzToolSelect];
+ break;
+ case 45: // N
+ case 9: // V
+ [toolPaletteController setSelectedTool:TikzToolNode];
+ break;
+ case 14: // E
+ [toolPaletteController setSelectedTool:TikzToolEdge];
+ //[self updateMouseMode];
+ break;
+ case 40: // K
+ [toolPaletteController setSelectedTool:TikzToolCrop];
+ break;
+ }
+ [self refreshLayers];
+}
+
+
+- (void)deselectAll:(id)sender {
+ [pickSupport deselectAllNodes];
+ [pickSupport deselectAllEdges];
+
+ for (Node *n in [graph nodes]) {
+ [[[nodeLayers objectForKey:n] selection] deselect];
+ }
+
+ for (Edge *e in [graph edges]) {
+ [[edgeControlLayers objectForKey:e] deselect];
+ }
+
+ [self postSelectionChange];
+}
+
+- (void)selectAll:(id)sender {
+ [pickSupport selectAllNodes:graph.nodes];
+
+ for (Node *n in [graph nodes]) {
+ [[[nodeLayers objectForKey:n] selection] select];
+ }
+
+ [self postSelectionChange];
+}
+
+
+- (void)updateMouseMode {
+ switch (toolPaletteController.selectedTool) {
+ case TikzToolSelect:
+ mouseMode = SelectMode;
+ break;
+ case TikzToolNode:
+ mouseMode = NodeMode;
+ break;
+ case TikzToolEdge:
+ mouseMode = EdgeMode;
+ break;
+ case TikzToolCrop:
+ mouseMode = CropMode;
+ break;
+ }
+}
+
+- (void)setDocumentUndoManager:(NSUndoManager *)um {
+ documentUndoManager = um;
+}
+
+- (void)copy:(id)sender {
+ if ([pickSupport selectedNodes].count != 0) {
+ Graph *clip = [graph copyOfSubgraphWithNodes:[pickSupport selectedNodes]];
+ NSString *tikz = [clip tikz];
+ NSData *data = [tikz dataUsingEncoding:NSUTF8StringEncoding];
+ //NSLog(@"about to copy: %@", tikz);
+ NSPasteboard *cb = [NSPasteboard generalPasteboard];
+ [cb declareTypes:[NSArray arrayWithObject:@"tikzpicture"] owner:self];
+ [cb setData:data forType:@"tikzpicture"];
+ }
+}
+
+- (void)cut:(id)sender {
+ if ([[pickSupport selectedNodes] count] != 0) {
+ [self copy:sender];
+ [self delete:sender];
+
+ // otherwise, menu will say "Undo Delete Graph"
+ [documentUndoManager setActionName:@"Cut Graph"];
+ }
+}
+
+- (void)paste:(id)sender {
+ NSPasteboard *cb = [NSPasteboard generalPasteboard];
+ NSString *type = [cb availableTypeFromArray:[NSArray arrayWithObject:@"tikzpicture"]];
+ if (type) {
+ NSData *data = [cb dataForType:type];
+ NSString *tikz = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
+ //NSLog(@"pasting tikz:\n%@",tikz);
+ TikzGraphAssembler *ass = [[TikzGraphAssembler alloc] init];
+ if ([ass parseTikz:tikz]) {
+ //NSLog(@"tikz pasted:\n%@",tikz);
+ Graph *clip = [ass graph];
+
+ NSRect graphBounds = [graph bounds];
+ NSRect clipBounds = [clip bounds];
+ float dx = graphBounds.origin.x +
+ graphBounds.size.width -
+ clipBounds.origin.x + 0.5f;
+ [clip shiftNodes:[clip nodes] byPoint:NSMakePoint(dx, 0)];
+
+ if ([[clip nodes] count] != 0) {
+ NSString *oldTikz = [graph tikz];
+ [self deselectAll:self];
+
+ // select everything from the clipboard
+ for (Node *n in [clip nodes]) {
+ [n attachStyleFromTable:[stylePaletteController nodeStyles]];
+ [self addNodeLayers:n];
+ [pickSupport selectNode:n];
+ [[[nodeLayers objectForKey:n] selection] select];
+ }
+
+ for (Edge *e in [clip edges]) {
+ [e attachStyleFromTable:[stylePaletteController edgeStyles]];
+ [self addEdgeLayers:e];
+ }
+
+ [graph insertGraph:clip];
+
+ [self registerUndo:oldTikz withActionName:@"Paste Graph"];
+ [self refreshLayers];
+ [self postGraphChange];
+ }
+ } else {
+ NSLog(@"Error: couldn't parse tikz picture from clipboard.");
+ }
+
+ }
+}
+
+- (void)flipHorizonal:(id)sender {
+ NSString *oldTikz = [graph tikz];
+ [graph flipHorizontalNodes:[pickSupport selectedNodes]];
+ [self registerUndo:oldTikz withActionName:@"Flip Horizontal"];
+ [self postGraphChange];
+ [self refreshLayers];
+}
+
+- (void)flipVertical:(id)sender {
+ NSString *oldTikz = [graph tikz];
+ [graph flipVerticalNodes:[pickSupport selectedNodes]];
+ [self registerUndo:oldTikz withActionName:@"Flip Vertical"];
+ [self postGraphChange];
+ [self refreshLayers];
+}
+
+- (void)reverseEdgeDirection:(id)sender {
+ NSString *oldTikz = [graph tikz];
+
+ NSSet *es;
+ if ([[pickSupport selectedEdges] count] != 0) {
+ es = [pickSupport selectedEdges];
+ } else {
+ es = [graph incidentEdgesForNodes:[pickSupport selectedNodes]];
+ }
+
+ for (Edge *e in es) [e reverse];
+
+ [self registerUndo:oldTikz withActionName:@"Flip Edge Direction"];
+ [self postGraphChange];
+ [self refreshLayers];
+}
+
+- (BOOL)acceptsFirstResponder { return YES; }
+- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent { return YES; }
+- (BOOL)canBecomeKeyView { return YES; }
+
+
+@end
diff --git a/tikzit/src/osx/Grid.h b/tikzit/src/osx/Grid.h
new file mode 100644
index 0000000..76826e2
--- /dev/null
+++ b/tikzit/src/osx/Grid.h
@@ -0,0 +1,48 @@
+//
+// Grid.h
+// TikZiT
+//
+// Copyright 2010 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#import <Cocoa/Cocoa.h>
+#import "Transformer.h"
+
+@interface Grid : NSObject {
+ float gridX, gridY;
+ //float gridCellX, gridCellY;
+ int subdivisions;
+ Transformer *transformer;
+ NSSize size;
+}
+
+@property NSSize size;
+
+- (id)initWithSpacing:(float)spacing subdivisions:(int)subs transformer:(Transformer*)t;
++ (Grid*)gridWithSpacing:(float)spacing subdivisions:(int)subs transformer:(Transformer*)t;
+- (NSPoint)snapScreenPoint:(NSPoint)p;
+- (float)gridX;
+- (float)gridY;
+- (int)subdivisions;
+- (void)setSubdivisions:(int)subs;
+
+// Grid can also draw itself on a layer
+- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx;
+
+@end
diff --git a/tikzit/src/osx/Grid.m b/tikzit/src/osx/Grid.m
new file mode 100644
index 0000000..3e412a3
--- /dev/null
+++ b/tikzit/src/osx/Grid.m
@@ -0,0 +1,152 @@
+//
+// Grid.m
+// TikZiT
+//
+// Copyright 2010 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#import "Grid.h"
+
+
+@implementation Grid
+
+@synthesize size;
+
+- (id)initWithSpacing:(float)spacing
+ subdivisions:(int)subs
+ transformer:(Transformer*)t
+{
+ [super init];
+ gridX = spacing;
+ gridY = spacing;
+ subdivisions = subs;
+ size.width = 0;
+ size.height = 0;
+ transformer = t;
+ return self;
+}
+
++ (Grid*)gridWithSpacing:(float)spacing
+ subdivisions:(int)subs
+ transformer:(Transformer*)t
+{
+ return [[Grid alloc] initWithSpacing:spacing
+ subdivisions:subs
+ transformer:t];
+}
+
+- (float)gridX {
+ return gridX;
+}
+
+- (float)gridY {
+ return gridY;
+}
+
+- (int)subdivisions {
+ return subdivisions;
+}
+
+- (void)setSubdivisions:(int)subs {
+ subdivisions = subs;
+}
+
+- (NSPoint)snapScreenPoint:(NSPoint)p {
+ NSPoint snap;
+
+ float gridCellX = [transformer scaleToScreen:gridX] / (float)subdivisions;
+ float gridCellY = [transformer scaleToScreen:gridY] / (float)subdivisions;
+
+ // snap along grid lines, relative to the origin
+ snap.x = floor(((p.x-[transformer origin].x)/gridCellX)+0.5)*gridCellX + [transformer origin].x;
+ snap.y = floor(((p.y-[transformer origin].y)/gridCellY)+0.5)*gridCellY + [transformer origin].y;
+ return snap;
+}
+
+-(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)context
+{
+ CGContextSaveGState(context);
+
+ CGContextSetShouldAntialias(context, NO);
+
+ float x,y;
+ float grX = [transformer scaleToScreen:gridX];
+ float grY = [transformer scaleToScreen:gridY];
+
+ float gridCellX = grX / (float)subdivisions;
+ float gridCellY = grY / (float)subdivisions;
+
+ for (x = [transformer origin].x + gridCellX; x < size.width; x += gridCellX) {
+ CGContextMoveToPoint(context, x, 0);
+ CGContextAddLineToPoint(context, x, size.height);
+ }
+
+ for (x = [transformer origin].x - gridCellX; x > 0; x -= gridCellX) {
+ CGContextMoveToPoint(context, x, 0);
+ CGContextAddLineToPoint(context, x, size.height);
+ }
+
+ for (y = [transformer origin].y + gridCellY; y < size.height; y += gridCellY) {
+ CGContextMoveToPoint(context, 0, y);
+ CGContextAddLineToPoint(context, size.width, y);
+ }
+
+ for (y = [transformer origin].y - gridCellY; y > 0; y -= gridCellY) {
+ CGContextMoveToPoint(context, 0, y);
+ CGContextAddLineToPoint(context, size.width, y);
+ }
+
+ CGContextSetRGBStrokeColor(context, 0.9, 0.9, 1, 1);
+ CGContextStrokePath(context);
+
+ for (x = [transformer origin].x + grX; x < size.width; x += grX) {
+ CGContextMoveToPoint(context, x, 0);
+ CGContextAddLineToPoint(context, x, size.height);
+ }
+
+ for (x = [transformer origin].x - grX; x > 0; x -= grX) {
+ CGContextMoveToPoint(context, x, 0);
+ CGContextAddLineToPoint(context, x, size.height);
+ }
+
+ for (y = [transformer origin].y + grY; y < size.height; y += grY) {
+ CGContextMoveToPoint(context, 0, y);
+ CGContextAddLineToPoint(context, size.width, y);
+ }
+
+ for (y = [transformer origin].y + grY; y > 0; y -= grY) {
+ CGContextMoveToPoint(context, 0, y);
+ CGContextAddLineToPoint(context, size.width, y);
+ }
+
+ CGContextSetRGBStrokeColor(context, 0.8, 0.8, 0.9, 1);
+ CGContextStrokePath(context);
+
+ CGContextMoveToPoint(context, [transformer origin].x, 0);
+ CGContextAddLineToPoint(context, [transformer origin].x, size.height);
+ CGContextMoveToPoint(context, 0, [transformer origin].y);
+ CGContextAddLineToPoint(context, size.width, [transformer origin].y);
+
+ CGContextSetRGBStrokeColor(context, 0.6, 0.6, 0.7, 1);
+ CGContextStrokePath(context);
+
+ CGContextRestoreGState(context);
+}
+
+@end
diff --git a/tikzit/src/osx/MultiCombo.h b/tikzit/src/osx/MultiCombo.h
new file mode 100644
index 0000000..c8ec769
--- /dev/null
+++ b/tikzit/src/osx/MultiCombo.h
@@ -0,0 +1,18 @@
+//
+// MultiCombo.h
+// TikZiT
+//
+// Created by Aleks Kissinger on 21/04/2011.
+// Copyright 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+
+@interface MultiCombo : NSComboBox {
+ BOOL multi;
+}
+
+@property (readwrite,assign) BOOL multi;
+
+@end
diff --git a/tikzit/src/osx/MultiCombo.m b/tikzit/src/osx/MultiCombo.m
new file mode 100644
index 0000000..8930460
--- /dev/null
+++ b/tikzit/src/osx/MultiCombo.m
@@ -0,0 +1,38 @@
+//
+// MultiCombo.m
+// TikZiT
+//
+// Created by Aleks Kissinger on 21/04/2011.
+// Copyright 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import "MultiCombo.h"
+
+
+@implementation MultiCombo
+
+- (void)textDidChange:(NSNotification *)notification {
+ [super textDidChange:notification];
+ [self setMulti:NO];
+}
+
+- (void)setMulti:(BOOL)m {
+ multi = m;
+ if (multi) {
+ [self setTextColor:[NSColor grayColor]];
+ [self setStringValue:@"multiple values"];
+ }
+}
+
+- (BOOL)multi { return multi; }
+
+- (BOOL)becomeFirstResponder {
+ [super becomeFirstResponder];
+ if ([self multi]) {
+ [self setTextColor:[NSColor blackColor]];
+ [self setStringValue:@""];
+ }
+ return YES;
+}
+
+@end
diff --git a/tikzit/src/osx/MultiField.h b/tikzit/src/osx/MultiField.h
new file mode 100644
index 0000000..39eeefa
--- /dev/null
+++ b/tikzit/src/osx/MultiField.h
@@ -0,0 +1,18 @@
+//
+// LabelField.h
+// TikZiT
+//
+// Created by Aleks Kissinger on 20/04/2011.
+// Copyright 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+
+@interface MultiField : NSTextField {
+ BOOL multi;
+}
+
+@property (readwrite,assign) BOOL multi;
+
+@end
diff --git a/tikzit/src/osx/MultiField.m b/tikzit/src/osx/MultiField.m
new file mode 100644
index 0000000..7c5aac3
--- /dev/null
+++ b/tikzit/src/osx/MultiField.m
@@ -0,0 +1,53 @@
+//
+// LabelField.m
+// TikZiT
+//
+// Created by Aleks Kissinger on 20/04/2011.
+// Copyright 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import "MultiField.h"
+
+
+@implementation MultiField
+
+- (void)textDidChange:(NSNotification *)notification {
+ [super textDidChange:notification];
+ [self setMulti:NO];
+}
+
+- (void)setMulti:(BOOL)m {
+ multi = m;
+ if (multi) {
+ [self setTextColor:[NSColor grayColor]];
+ [self setStringValue:@"multiple values"];
+ }
+}
+
+- (BOOL)multi { return multi; }
+
+- (BOOL)becomeFirstResponder {
+ [super becomeFirstResponder];
+ if ([self multi]) {
+ [self setTextColor:[NSColor blackColor]];
+ [self setStringValue:@""];
+ }
+ return YES;
+}
+
+//- (BOOL)textShouldBeginEditing:(NSText *)textObject {
+// [super textShouldBeginEditing:textObject];
+// NSLog(@"about to type");
+// return YES;
+//}
+
+//- (void)textDidEndEditing:(NSNotification *)obj {
+// [super textDidEndEditing:obj];
+//
+// NSLog(@"focus lost");
+// if ([self multi]) {
+// [self setMulti:YES];
+// }
+//}
+
+@end
diff --git a/tikzit/src/osx/NilToEmptyStringTransformer.h b/tikzit/src/osx/NilToEmptyStringTransformer.h
new file mode 100644
index 0000000..1445a94
--- /dev/null
+++ b/tikzit/src/osx/NilToEmptyStringTransformer.h
@@ -0,0 +1,28 @@
+//
+// NilToEmptyStringTransformer.h
+// TikZiT
+//
+// Copyright 2011 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#import <Foundation/Foundation.h>
+
+@interface NilToEmptyStringTransformer : NSValueTransformer
+
+@end
diff --git a/tikzit/src/osx/NilToEmptyStringTransformer.m b/tikzit/src/osx/NilToEmptyStringTransformer.m
new file mode 100644
index 0000000..97267e3
--- /dev/null
+++ b/tikzit/src/osx/NilToEmptyStringTransformer.m
@@ -0,0 +1,53 @@
+//
+// NilToEmptyStringTransformer.m
+// TikZiT
+//
+// Copyright 2011 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#import "NilToEmptyStringTransformer.h"
+
+@implementation NilToEmptyStringTransformer
+
+- (id)init {
+ [super init];
+ return self;
+}
+
++ (Class)transformedValueClass {
+ return [NSString class];
+}
+
++ (BOOL)allowsReverseTransformation {
+ return YES;
+}
+
+- (id)transformedValue:(id)value {
+ if (value == nil) {
+ return @"";
+ } else {
+ return value;
+ }
+}
+
+- (id)reverseTransformedValue:(id)value {
+ return [self transformedValue:value];
+}
+
+@end
diff --git a/tikzit/src/osx/NodeLayer.h b/tikzit/src/osx/NodeLayer.h
new file mode 100644
index 0000000..dfe05e8
--- /dev/null
+++ b/tikzit/src/osx/NodeLayer.h
@@ -0,0 +1,62 @@
+//
+// NodeLayer.h
+// TikZiT
+//
+// Copyright 2010 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#import <Cocoa/Cocoa.h>
+#import <QuartzCore/CoreAnimation.h>
+#import "Transformer.h"
+#import "Shape.h"
+#import "Node.h"
+#import "NodeStyle+Coder.h"
+#import "NodeSelectionLayer.h"
+
+@interface NodeLayer : CALayer {
+ Node *node;
+ Shape *shape;
+ CGMutablePathRef path;
+ float textwidth;
+ NSPoint center;
+ Transformer *transformer;
+ Transformer *localTrans;
+ NodeSelectionLayer *selection;
+ BOOL rescale;
+ BOOL dirty; // need to rebuild CGBezierPath of the shape
+}
+
+@property Node *node;
+@property (assign) NSPoint center;
+@property (assign) BOOL rescale;
+@property (retain) NodeSelectionLayer *selection;
+@property (readonly) CGMutablePathRef path;
+
+- (id)initWithNode:(Node*)n transformer:(Transformer*)t;
+- (NSColor*)strokeColor;
+- (NSColor*)fillColor;
+- (float)strokeWidth;
+
+- (void)setCenter:(NSPoint)ctr andAnimateWhen:(BOOL)anim;
+- (void)updateFrame;
+- (BOOL)nodeContainsPoint:(NSPoint)p;
+
+- (void)drawInContext:(CGContextRef)context;
+
+@end
diff --git a/tikzit/src/osx/NodeLayer.m b/tikzit/src/osx/NodeLayer.m
new file mode 100644
index 0000000..da01bec
--- /dev/null
+++ b/tikzit/src/osx/NodeLayer.m
@@ -0,0 +1,239 @@
+//
+// NodeLayer.m
+// TikZiT
+//
+// Copyright 2010 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#import "NodeLayer.h"
+#import "CALayer+DrawLabel.h"
+#import "NSString+LatexConstants.h"
+#import "Shape.h"
+#import "ShapeNames.h"
+#import "Node.h"
+#import "Edge.h"
+
+@implementation NodeLayer
+
+@synthesize node, selection, rescale;
+
+- (id)initWithNode:(Node *)n transformer:(Transformer*)t {
+ [super init];
+ node = n;
+ selection = [[NodeSelectionLayer alloc] init];
+ [selection setNodeLayer:self];
+ localTrans = [[Transformer alloc] init];
+
+ [self addSublayer:selection];
+ textwidth = 0.0f;
+ center = NSMakePoint(0.0f, 0.0f);
+ transformer = t;
+
+ path = NULL;
+ rescale = YES;
+ dirty = YES;
+
+ [self updateFrame];
+ return self;
+}
+
+- (NSColor*)strokeColor {
+ if ([node style] != nil) {
+ return [[[node style] strokeColor] colorUsingColorSpace:[NSColorSpace deviceRGBColorSpace]];
+ } else {
+ return nil;
+ }
+}
+
+- (NSColor*)fillColor {
+ if ([node style] != nil) {
+ return [[[node style] fillColor] colorUsingColorSpace:[NSColorSpace deviceRGBColorSpace]];
+ } else {
+ return nil;
+ }
+}
+
+- (float)strokeWidth {
+ if ([node style] != nil) {
+ return [node.style strokeThickness];
+ } else {
+ return 1;
+ }
+}
+
+- (NSPoint)center { return center; }
+
+- (void)setCenter:(NSPoint)ctr {
+ center.x = round(ctr.x);
+ center.y = round(ctr.y);
+ [self updateFrame];
+}
+
+- (void)setCenter:(NSPoint)ctr andAnimateWhen:(BOOL)anim {
+ [CATransaction begin];
+ if (!anim) {
+ [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
+ }
+ [self setCenter:ctr];
+ [CATransaction commit];
+}
+
+- (void)updateShape {
+ Shape *s = ([node style] != nil) ?
+ [Shape shapeForName:[[node style] shapeName]] :
+ [Shape shapeForName:SHAPE_CIRCLE];
+ if (s != shape) { // straight pointer comparison
+ shape = s;
+ dirty = YES;
+ }
+}
+
+- (void)updateLocalTrans {
+ float scale = ([node style] != nil) ? [[node style] scale] : 1.0f;
+
+ Transformer *t = [Transformer transformer];
+ float rad = ([transformer scaleToScreen:scale] / 2.0f) + 8.0f;
+ [t setOrigin:NSMakePoint(rad, rad)];
+ [t setScale:[transformer scale]*((rescale)?scale:0.8f)];
+
+ if (![localTrans isEqual:t]) {
+ dirty = YES;
+ localTrans = t;
+ }
+}
+
+- (void)updateFrame {
+ [self updateLocalTrans];
+ [self updateShape];
+ float rad = [localTrans origin].x;
+ [self setFrame:CGRectIntegral(CGRectMake(center.x - rad, center.y - rad, 2*rad, 2*rad))];
+ NSRect bds = NSMakeRect(0, 0, 2*rad, 2*rad);
+ [selection setFrame:NSRectToCGRect(bds)];
+
+ [self setNeedsDisplay];
+ [selection setNeedsDisplay];
+}
+
+- (CGMutablePathRef)path {
+ if (dirty) {
+ CGMutablePathRef pth = CGPathCreateMutable();
+ NSPoint p, cp1, cp2;
+ for (NSArray *arr in [shape paths]) {
+ BOOL fst = YES;
+ for (Edge *e in arr) {
+ if (fst) {
+ fst = NO;
+ p = [localTrans toScreen:[[e source] point]];
+ CGPathMoveToPoint(pth, nil, p.x, p.y);
+ }
+
+ p = [localTrans toScreen:[[e target] point]];
+ if ([e isStraight]) {
+ CGPathAddLineToPoint(pth, nil, p.x, p.y);
+ } else {
+ cp1 = [localTrans toScreen:[e cp1]];
+ cp2 = [localTrans toScreen:[e cp2]];
+ CGPathAddCurveToPoint(pth, nil, cp1.x, cp1.y, cp2.x, cp2.y, p.x, p.y);
+ }
+ }
+
+ CGPathCloseSubpath(pth);
+ }
+
+ if (path != NULL) CFRelease(path);
+ path = pth;
+ dirty = NO;
+ }
+
+
+ return path;
+}
+
+- (BOOL)nodeContainsPoint:(NSPoint)p {
+ CGPoint p1 = CGPointMake(p.x - [self frame].origin.x, p.y - [self frame].origin.y);
+ return CGPathContainsPoint([self path],nil,p1,NO);
+}
+
+
+- (void)drawInContext:(CGContextRef)context {
+ CGContextSaveGState(context);
+
+
+ if ([node style] == nil) {
+ CGContextSetRGBStrokeColor(context, 0.4f, 0.4f, 0.7f, 1.0f);
+ CGContextSetRGBFillColor(context, 0.4f, 0.4f, 0.7f, 1.0f);
+ //CGRect fr = [self frame];
+ CGRect bds = NSRectToCGRect([localTrans rectToScreen:NSMakeRect(-0.5, -0.5, 1, 1)]);
+ CGRect pt = CGRectMake(CGRectGetMidX(bds)-1.0f, CGRectGetMidY(bds)-1.0f, 2.0f, 2.0f);
+ CGContextSetLineWidth(context, 0);
+ CGContextAddEllipseInRect(context, pt);
+ CGContextFillPath(context);
+
+ // HACK: for some reason, CGFloat isn't getting typedef'ed properly
+
+#ifdef __x86_64__
+ const double dash[2] = {2.0,2.0};
+#else
+ const float dash[2] = {2.0,2.0};
+#endif
+ CGContextSetLineDash(context, 0.0, dash, 2);
+ CGContextSetLineWidth(context, 1);
+ CGContextAddPath(context, [self path]);
+ CGContextStrokePath(context);
+ } else {
+ NSColor *stroke = [self strokeColor];
+ NSColor *fill = [self fillColor];
+
+ CGContextSetRGBStrokeColor(context,
+ [stroke redComponent],
+ [stroke greenComponent],
+ [stroke blueComponent],
+ [stroke alphaComponent]);
+
+ CGContextSetLineWidth(context, [self strokeWidth]);
+
+ CGContextSetRGBFillColor(context,
+ [fill redComponent],
+ [fill greenComponent],
+ [fill blueComponent],
+ [fill alphaComponent]);
+
+
+ CGContextSetLineWidth(context, [self strokeWidth]);
+ CGContextAddPath(context, [self path]);
+ CGContextDrawPath(context, kCGPathFillStroke);
+ }
+
+ if (!([node label] == nil || [[node label] isEqual:@""])) {
+ NSPoint labelPt = NSMakePoint([self frame].size.width/2, [self frame].size.height/2);
+ [self drawLabel:[[node label] stringByExpandingLatexConstants]
+ atPoint:labelPt
+ inContext:context
+ usingTrans:transformer];
+ }
+
+ CGContextRestoreGState(context);
+}
+
+- (void)dealloc {
+ if (path != NULL) CFRelease(path);
+ [super dealloc];
+}
+
+@end
diff --git a/tikzit/src/osx/NodeSelectionLayer.h b/tikzit/src/osx/NodeSelectionLayer.h
new file mode 100644
index 0000000..99ee75f
--- /dev/null
+++ b/tikzit/src/osx/NodeSelectionLayer.h
@@ -0,0 +1,45 @@
+//
+// NodeSelectionLayer.h
+// TikZiT
+//
+// Copyright 2010 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#import <Cocoa/Cocoa.h>
+#import <QuartzCore/CoreAnimation.h>
+#import "Shape.h"
+
+@class NodeLayer;
+
+@interface NodeSelectionLayer : CALayer {
+ BOOL selected;
+ CGMutablePathRef path;
+ NSLock *drawLock;
+ NodeLayer *nodeLayer;
+}
+
+@property NodeLayer *nodeLayer;
+
+- (id)init;
+- (void)select;
+- (void)deselect;
+- (void)highlight;
+- (void)unhighlight;
+
+@end
diff --git a/tikzit/src/osx/NodeSelectionLayer.m b/tikzit/src/osx/NodeSelectionLayer.m
new file mode 100644
index 0000000..5efcbf7
--- /dev/null
+++ b/tikzit/src/osx/NodeSelectionLayer.m
@@ -0,0 +1,93 @@
+//
+// NodeSelectionLayer.m
+// TikZiT
+//
+// Copyright 2010 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#import "NodeSelectionLayer.h"
+#import "NodeLayer.h"
+#import "CircleShape.h"
+
+@implementation NodeSelectionLayer
+
+@synthesize nodeLayer;
+
+- (id)init {
+ [super init];
+ selected = NO;
+ drawLock = [[NSLock alloc] init];
+ nodeLayer = nil;
+ [self setOpacity:0.0f];
+ return self;
+}
+
+
+- (void)select {
+ selected = YES;
+ [self setOpacity:0.5f];
+}
+
+- (void)deselect {
+ selected = NO;
+ [self setOpacity:0.0f];
+}
+
+- (void)highlight {
+ if (!selected) {
+ [self setOpacity:0.25f];
+ }
+}
+
+- (void)unhighlight {
+ if (!selected) {
+ [self setOpacity:0.0f];
+ }
+}
+
+//- (CGMutablePathRef)path {
+// return path;
+//}
+//
+//- (void)setPath:(CGMutablePathRef)p {
+// path = CGPathCreateMutableCopy(p);
+// CFMakeCollectable(path);
+//}
+
+- (void)drawInContext:(CGContextRef)context {
+ [drawLock lock];
+ CGContextSaveGState(context);
+
+ //CGContextSetRGBStrokeColor(context, 0.61f, 0.735f, 1.0f, 1.0f);
+ CGContextSetRGBStrokeColor(context, 0.61f, 0.735f, 1.0f, 1.0f);
+ CGContextSetRGBFillColor(context, 0.61f, 0.735f, 1.0f, 1.0f);
+ CGContextSetLineWidth(context, 6.0f);
+
+ if (nodeLayer != nil) {
+ CGContextAddPath(context, [nodeLayer path]);
+ } else {
+ NSLog(@"WARNING: attempting to draw selection with path = nil.");
+ }
+ CGContextDrawPath(context, kCGPathFillStroke);
+
+ CGContextRestoreGState(context);
+ [drawLock unlock];
+}
+
+@end
diff --git a/tikzit/src/osx/NodeStyle+Coder.h b/tikzit/src/osx/NodeStyle+Coder.h
new file mode 100644
index 0000000..b6443af
--- /dev/null
+++ b/tikzit/src/osx/NodeStyle+Coder.h
@@ -0,0 +1,36 @@
+//
+// NodeStyle+Coder.h
+// TikZiT
+//
+// Copyright 2010 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#import <Cocoa/Cocoa.h>
+#import "NodeStyle.h"
+
+
+@interface NodeStyle(Coder)
+
+@property (copy) NSColor *fillColor;
+@property (copy) NSColor *strokeColor;
+
+- (id)initWithCoder:(NSCoder *)coder;
+- (void)encodeWithCoder:(NSCoder *)coder;
+
+@end
diff --git a/tikzit/src/osx/NodeStyle+Coder.m b/tikzit/src/osx/NodeStyle+Coder.m
new file mode 100644
index 0000000..8da91c1
--- /dev/null
+++ b/tikzit/src/osx/NodeStyle+Coder.m
@@ -0,0 +1,91 @@
+//
+// NodeStyle+Coder.m
+// TikZiT
+//
+// Copyright 2010 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#import "NodeStyle+Coder.h"
+#import "ShapeNames.h"
+
+@implementation NodeStyle(Coder)
+
+- (NSColor*)fillColor {
+ return [NSColor colorWithDeviceRed:fillColorRGB.redFloat
+ green:fillColorRGB.greenFloat
+ blue:fillColorRGB.blueFloat
+ alpha:1.0f];
+}
+
+- (void)setFillColor:(NSColor*)c {
+ NSColor *c1 = [c colorUsingColorSpaceName:NSDeviceRGBColorSpace];
+ [self willChangeValueForKey:@"fillColorIsKnown"];
+ fillColorRGB = [ColorRGB colorWithFloatRed:c1.redComponent
+ green:c1.greenComponent
+ blue:c1.blueComponent];
+ [self didChangeValueForKey:@"fillColorIsKnown"];
+}
+
+- (NSColor*)strokeColor {
+ return [NSColor colorWithDeviceRed:strokeColorRGB.redFloat
+ green:strokeColorRGB.greenFloat
+ blue:strokeColorRGB.blueFloat
+ alpha:1.0f];
+}
+
+- (void)setStrokeColor:(NSColor*)c {
+ NSColor *c1 = [c colorUsingColorSpaceName:NSDeviceRGBColorSpace];
+ [self willChangeValueForKey:@"strokeColorIsKnown"];
+ strokeColorRGB = [ColorRGB colorWithFloatRed:c1.redComponent
+ green:c1.greenComponent
+ blue:c1.blueComponent];
+ [self didChangeValueForKey:@"strokeColorIsKnown"];
+}
+
+- (id)initWithCoder:(NSCoder *)coder {
+ [super init];
+
+ // decode keys
+ name = [coder decodeObjectForKey:@"name"];
+ [self setStrokeColor:[coder decodeObjectForKey:@"strokeColor"]];
+ [self setFillColor:[coder decodeObjectForKey:@"fillColor"]];
+ strokeThickness = [coder decodeIntForKey:@"strokeThickness"];
+ shapeName = [coder decodeObjectForKey:@"shapeName"];
+ category = [coder decodeObjectForKey:@"category"];
+ scale = [coder decodeFloatForKey:@"scale"];
+
+ // apply defaults
+ if (scale == 0.0f) scale = 1.0f;
+ if (shapeName == nil) shapeName = SHAPE_CIRCLE;
+
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder {
+ [coder encodeObject:name forKey:@"name"];
+ [coder encodeObject:[self strokeColor] forKey:@"strokeColor"];
+ [coder encodeObject:[self fillColor] forKey:@"fillColor"];
+ [coder encodeInt:strokeThickness forKey:@"strokeThickness"];
+ [coder encodeObject:shapeName forKey:@"shapeName"];
+ [coder encodeObject:category forKey:@"category"];
+ [coder encodeFloat:scale forKey:@"scale"];
+}
+
+
+@end
diff --git a/tikzit/src/osx/PreambleController.h b/tikzit/src/osx/PreambleController.h
new file mode 100644
index 0000000..4f7fb33
--- /dev/null
+++ b/tikzit/src/osx/PreambleController.h
@@ -0,0 +1,57 @@
+//
+// PreambleController.h
+// TikZiT
+//
+// Copyright 2010 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#import <Cocoa/Cocoa.h>
+#import "Preambles.h"
+#import "Preambles+Coder.h"
+
+@interface PreambleController : NSWindowController {
+ Preambles *preambles;
+ IBOutlet NSTextView *textView;
+ IBOutlet NSToolbar *toolbar;
+ IBOutlet NSToolbarItem *defaultToolbarItem;
+ IBOutlet NSToolbarItem *customToolbarItem;
+ IBOutlet NSDictionaryController *preambleDictionaryController;
+ NSDictionary *textAttrs;
+ NSAttributedString *preambleText;
+ NSColor *ghostColor;
+
+ NSIndexSet *selectionIndexes;
+}
+
+@property (readonly) BOOL useDefaultPreamble;
+@property (readonly) Preambles *preambles;
+@property (retain) NSAttributedString *preambleText;
+@property (retain) NSIndexSet *selectionIndexes;
+
+- (id)initWithWindowNibName:(NSString *)windowNibName plist:(NSString*)plist styles:(NSArray*)sty;
+- (void)savePreambles:(NSString*)plist;
+- (NSString*)currentPreamble;
+- (NSString*)currentPostamble;
+- (IBAction)setPreamble:(id)sender;
+- (IBAction)insertDefaultStyles:(id)sender;
+
+- (IBAction)addPreamble:(id)sender;
+- (IBAction)duplicatePreamble:(id)sender;
+
+@end
diff --git a/tikzit/src/osx/PreambleController.m b/tikzit/src/osx/PreambleController.m
new file mode 100644
index 0000000..ea95716
--- /dev/null
+++ b/tikzit/src/osx/PreambleController.m
@@ -0,0 +1,168 @@
+//
+// PreambleController.m
+// TikZiT
+//
+// Copyright 2010 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#import "PreambleController.h"
+
+
+@implementation PreambleController
+
+@synthesize preambleText, preambles;
+
+- (id)initWithWindowNibName:(NSString *)windowNibName plist:(NSString*)plist styles:(NSArray*)sty {
+ [super initWithWindowNibName:windowNibName];
+
+ preambles = (Preambles*)[NSKeyedUnarchiver unarchiveObjectWithFile:plist];
+ [preambles setStyles:sty];
+ if (preambles == nil) preambles = [[Preambles alloc] init];
+
+ preambleText = nil;
+
+ NSFont *font = [NSFont userFixedPitchFontOfSize:11.0f];
+ textAttrs = [NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName];
+ ghostColor = [NSColor colorWithDeviceRed:0.9f green:0.9f blue:0.9f alpha:1.0f];
+
+
+
+ return self;
+}
+
+- (void)awakeFromNib {
+ NSArray *arr = [preambleDictionaryController arrangedObjects];
+ NSString *current = [preambles selectedPreambleName];
+
+ if (current != nil && ![current isEqual:@"default"]) {
+ for (int i = 0; i < [arr count]; ++i) {
+ if ([[[arr objectAtIndex:i] key] isEqual:current]) {
+ [self setSelectionIndexes:[NSIndexSet indexSetWithIndex:i]];
+ break;
+ }
+ }
+ }
+}
+
+- (BOOL)useDefaultPreamble {
+ return [[preambles selectedPreambleName] isEqualToString:@"default"];
+}
+
+- (void)flushText {
+ if (preambleText != nil && ![self useDefaultPreamble]) {
+ [preambles setCurrentPreamble:[preambleText string]];
+ }
+}
+
+- (void)setCurrentPreamble:(NSString*)current {
+ [self flushText];
+
+ [self willChangeValueForKey:@"useDefaultPreamble"];
+ [preambles setSelectedPreambleName:current];
+ [self didChangeValueForKey:@"useDefaultPreamble"];
+
+ [self setPreambleText:
+ [[NSAttributedString alloc] initWithString:[preambles currentPreamble]
+ attributes:textAttrs]];
+}
+
+- (void)showWindow:(id)sender {
+ [super showWindow:sender];
+ if ([self useDefaultPreamble]) {
+ [toolbar setSelectedItemIdentifier:[defaultToolbarItem itemIdentifier]];
+ } else {
+ [toolbar setSelectedItemIdentifier:[customToolbarItem itemIdentifier]];
+ }
+
+ [self setPreamble:self];
+}
+
+- (void)savePreambles:(NSString*)plist {
+ [self flushText];
+ [NSKeyedArchiver archiveRootObject:preambles toFile:plist];
+}
+
+- (NSString*)currentPreamble {
+ [self flushText];
+ return [preambles currentPreamble];
+}
+
+- (NSString*)currentPostamble {
+ return [preambles currentPostamble];
+}
+
+- (void)setSelectionIndexes:(NSIndexSet *)idx {
+ [self willChangeValueForKey:@"selectionIndexes"];
+ selectionIndexes = idx;
+ [self didChangeValueForKey:@"selectionIndexes"];
+
+ [self setPreamble:self];
+}
+
+- (NSIndexSet*)selectionIndexes {
+ return selectionIndexes;
+}
+
+- (IBAction)setPreamble:(id)sender {
+ if ([[toolbar selectedItemIdentifier] isEqualToString:[defaultToolbarItem itemIdentifier]]) {
+ [self setCurrentPreamble:@"default"];
+ [textView setBackgroundColor:ghostColor];
+ } else if ([toolbar.selectedItemIdentifier isEqualToString:[customToolbarItem itemIdentifier]]) {
+ NSString *key = nil;
+ if ([selectionIndexes count]==1) {
+ int i = [selectionIndexes firstIndex];
+ key = [[[preambleDictionaryController arrangedObjects] objectAtIndex:i] key];
+ }
+ if (key != nil) {
+ [self setCurrentPreamble:key];
+ //NSLog(@"preamble set to %@", key);
+ } else {
+ [self setCurrentPreamble:@"default"];
+ //NSLog(@"preamble set to default");
+ }
+ [textView setBackgroundColor:[NSColor whiteColor]];
+ }
+}
+
+- (IBAction)insertDefaultStyles:(id)sender {
+ [textView insertText:[preambles styleDefinitions]];
+}
+
+- (IBAction)addPreamble:(id)sender {
+ [preambleDictionaryController setInitialKey:@"new preamble"];
+ [preambleDictionaryController setInitialValue:[preambles defaultPreamble]];
+ [preambleDictionaryController add:sender];
+}
+
+- (void)controlTextDidEndEditing:(NSNotification *)obj {
+ //NSLog(@"got a text change");
+ [self setPreamble:[obj object]];
+}
+
+
+// NOT IMPLEMENTED
+- (IBAction)duplicatePreamble:(id)sender {
+// NSLog(@"set text to: %@", [preambles currentPreamble]);
+// [preambleDictionaryController setInitialKey:[preambles selectedPreambleName]];
+// [preambleDictionaryController setInitialValue:[preambles currentPreamble]];
+// [preambleDictionaryController add:sender];
+}
+
+
+@end
diff --git a/tikzit/src/osx/Preambles+Coder.h b/tikzit/src/osx/Preambles+Coder.h
new file mode 100644
index 0000000..5a270c5
--- /dev/null
+++ b/tikzit/src/osx/Preambles+Coder.h
@@ -0,0 +1,32 @@
+//
+// Preambles+Coder.h
+// TikZiT
+//
+// Copyright 2010 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#import "Preambles.h"
+#import <Cocoa/Cocoa.h>
+
+@interface Preambles (Coder)
+
+- (id)initWithCoder:(NSCoder *)coder;
+- (void)encodeWithCoder:(NSCoder *)coder;
+
+@end
diff --git a/tikzit/src/osx/Preambles+Coder.m b/tikzit/src/osx/Preambles+Coder.m
new file mode 100644
index 0000000..6b9768a
--- /dev/null
+++ b/tikzit/src/osx/Preambles+Coder.m
@@ -0,0 +1,41 @@
+//
+// Preambles+Coder.m
+// TikZiT
+//
+// Copyright 2010 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#import "Preambles+Coder.h"
+
+
+@implementation Preambles (Coder)
+
+- (id)initWithCoder:(NSCoder *)coder {
+ [super init];
+ selectedPreambleName = [coder decodeObjectForKey:@"selectedPreamble"];
+ preambleDict = [coder decodeObjectForKey:@"preambles"];
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder {
+ [coder encodeObject:selectedPreambleName forKey:@"selectedPreamble"];
+ [coder encodeObject:preambleDict forKey:@"preambles"];
+}
+
+@end
diff --git a/tikzit/src/osx/PreviewController.h b/tikzit/src/osx/PreviewController.h
new file mode 100644
index 0000000..d6d855e
--- /dev/null
+++ b/tikzit/src/osx/PreviewController.h
@@ -0,0 +1,51 @@
+//
+// PreviewController.h
+// TikZiT
+//
+// Copyright 2010 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+
+
+#import <Cocoa/Cocoa.h>
+
+@class PDFView;
+@class PreambleController;
+
+@interface PreviewController : NSWindowController {
+ IBOutlet PDFView *pdfView;
+ IBOutlet NSProgressIndicator *progressIndicator;
+ IBOutlet NSScrollView *errorTextView;
+ IBOutlet NSTextView *errorText;
+ PreambleController *preambleController;
+ NSString *tempDir;
+ NSLock *latexLock;
+ int typesetCount;
+}
+
+
+- (id)initWithWindowNibName:(NSString*)nib
+ preambleController:(PreambleController*)pc
+ tempDir:(NSString*)dir;
+
+- (void)buildTikz:(NSString*)tikz;
+
++ (void)setDefaultPreviewController:(PreviewController*)pc;
++ (PreviewController*)defaultPreviewController;
+
+@end
diff --git a/tikzit/src/osx/PreviewController.m b/tikzit/src/osx/PreviewController.m
new file mode 100644
index 0000000..e54a91f
--- /dev/null
+++ b/tikzit/src/osx/PreviewController.m
@@ -0,0 +1,137 @@
+//
+// PreviewController.m
+// TikZiT
+//
+// Copyright 2010 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#import "PreviewController.h"
+#import "AppDelegate.h"
+#import "PreambleController.h"
+#import <Quartz/Quartz.h>
+
+@implementation PreviewController
+
+static PreviewController *preview = nil;
+
+- (id)initWithWindowNibName:(NSString*)nib
+ preambleController:(PreambleController*)pc
+ tempDir:(NSString*)dir {
+ [super initWithWindowNibName:nib];
+ tempDir = [dir copy];
+ typesetCount = 0;
+ preambleController = pc;
+ latexLock = [[NSLock alloc] init];
+ return self;
+}
+
+- (void)runLatex:(id)tikz {
+ // Only build one tex file at a time, so we don't get funky results.
+ [latexLock lock];
+ [progressIndicator startAnimation:self];
+
+ int fnum = typesetCount++;
+
+ NSString *tex = [NSString stringWithFormat:@"%@%@%@",
+ [preambleController currentPreamble],
+ tikz,
+ [preambleController currentPostamble]];
+
+ NSString *texFile = [NSString stringWithFormat:@"%@/tikzit_%d.tex", tempDir, fnum];
+ NSString *pdfFile = [NSString stringWithFormat:@"%@/tikzit_%d.pdf", tempDir, fnum];
+
+ [tex writeToFile:texFile atomically:NO encoding:NSUTF8StringEncoding error:NULL];
+
+ // We run pdflatex in a bash shell to have easy access to the setup from unix-land
+ NSTask *latexTask = [[NSTask alloc] init];
+ [latexTask setCurrentDirectoryPath:tempDir];
+ [latexTask setLaunchPath:@"/bin/bash"];
+
+ // This assumes the user has $PATH set up to find pdflatex in either .profile
+ // or .bashrc. This should be improved to take other path setups into account
+ // and to be customisable.
+ NSString *latexCmd =
+ [NSString stringWithFormat:
+ @"if [ -e ~/.profile ]; then source ~/.profile; fi\n"
+ @"if [ -e ~/.bashrc ]; then source ~/.bashrc; fi\n"
+ @"pdflatex -interaction=nonstopmode -halt-on-error '%@'\n",
+ texFile];
+
+ NSPipe *pout = [NSPipe pipe];
+ NSPipe *pin = [NSPipe pipe];
+ [latexTask setStandardOutput:pout];
+ [latexTask setStandardInput:pin];
+
+ NSFileHandle *latexIn = [pin fileHandleForWriting];
+ NSFileHandle *latexOut = [pout fileHandleForReading];
+
+ [latexTask launch];
+ [latexIn writeData:[latexCmd dataUsingEncoding:NSUTF8StringEncoding]];
+ [latexIn closeFile];
+ [latexTask waitUntilExit];
+
+ NSData *data = [latexOut readDataToEndOfFile];
+ NSString *str = [[NSString alloc] initWithData:data
+ encoding:NSUTF8StringEncoding];
+
+ if ([latexTask terminationStatus] != 0) {
+ [errorTextView setHidden:YES];
+ errorText.string = [@"\nAN ERROR HAS OCCURRED, PDFLATEX SAID:\n\n" stringByAppendingString:str];
+ [errorTextView setHidden:NO];
+ } else {
+ errorText.string = @"";
+ [errorTextView setHidden:YES];
+
+ data = [NSData dataWithContentsOfFile:pdfFile];
+ PDFDocument *doc = [[PDFDocument alloc] initWithData:data];
+
+ // pad the PDF by a couple of pixels
+ if ([doc pageCount] >= 1) {
+ PDFPage *page = [doc pageAtIndex:0];
+ NSRect box = [page boundsForBox:kPDFDisplayBoxCropBox];
+ box.origin.x -= 2.0f;
+ box.origin.y -= 2.0f;
+ box.size.width += 4.0f;
+ box.size.height += 4.0f;
+ [page setBounds:box forBox:kPDFDisplayBoxCropBox];
+ [page setBounds:box forBox:kPDFDisplayBoxMediaBox];
+ }
+
+ [pdfView setDocument:doc];
+ }
+
+ [progressIndicator stopAnimation:self];
+ [latexLock unlock];
+}
+
+- (void)buildTikz:(NSString*)tikz {
+ // Build on a separate thread to keep the interface responsive.
+ [NSThread detachNewThreadSelector:@selector(runLatex:) toTarget:self withObject:tikz];
+}
+
++ (void)setDefaultPreviewController:(PreviewController*)pc {
+ preview = pc;
+}
+
++ (PreviewController*)defaultPreviewController {
+ return preview;
+}
+
+
+@end
diff --git a/tikzit/src/osx/PropertyInspectorController.h b/tikzit/src/osx/PropertyInspectorController.h
new file mode 100644
index 0000000..cb14021
--- /dev/null
+++ b/tikzit/src/osx/PropertyInspectorController.h
@@ -0,0 +1,73 @@
+//
+// PropertyInspectorController.h
+// TikZiT
+//
+// Created by Aleks Kissinger on 17/07/2011.
+// Copyright 2011 Aleks Kissinger. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+#import "NodeStyle.h"
+#import "GraphElementData.h"
+
+@class SFBInspectorView;
+@class StylePaletteController;
+
+@interface PropertyInspectorController : NSWindowController {
+ IBOutlet SFBInspectorView *propertyInspectorView;
+ IBOutlet NSView *nodePropertiesView;
+ IBOutlet NSView *graphPropertiesView;
+ IBOutlet NSView *edgePropertiesView;
+ IBOutlet NSTextField *edgeNodeLabelField;
+ IBOutlet NSButton *edgeNodeCheckbox;
+ IBOutlet NSArrayController *nodeDataArrayController;
+ IBOutlet NSArrayController *graphDataArrayController;
+ IBOutlet NSArrayController *edgeDataArrayController;
+ IBOutlet NSArrayController *edgeNodeDataArrayController;
+
+ NSMutableArray *selectedNodes;
+ IBOutlet NSArrayController *selectedNodesArrayController;
+
+ NSMutableArray *selectedEdges;
+ IBOutlet NSArrayController *selectedEdgesArrayController;
+
+ // this data lists exists solely for displaying messages in disabled data tables
+ GraphElementData *noSelection;
+ GraphElementData *multipleSelection;
+ GraphElementData *noEdgeNode;
+ GraphElementData *noGraph;
+
+
+ // used to get access to the global style table
+ StylePaletteController *stylePaletteController;
+}
+
+//@property (readonly) BOOL enableNodeDataControls;
+//@property (readonly) BOOL enableEdgeDataControls;
+@property (retain) NSMutableArray *selectedNodes;
+@property (retain) NSMutableArray *selectedEdges;
+@property (retain) StylePaletteController *stylePaletteController;
+
+- (id)initWithWindowNibName:(NSString *)windowNibName;
+- (void)graphSelectionChanged:(NSNotification*)notification;
+
+- (IBAction)addNodeProperty:(id)sender;
+- (IBAction)addNodeAtom:(id)sender;
+- (IBAction)removeNodeProperty:(id)sender;
+
+- (IBAction)addGraphProperty:(id)sender;
+- (IBAction)addGraphAtom:(id)sender;
+- (IBAction)removeGraphProperty:(id)sender;
+
+- (IBAction)addEdgeProperty:(id)sender;
+- (IBAction)addEdgeAtom:(id)sender;
+- (IBAction)removeEdgeProperty:(id)sender;
+
+- (IBAction)addEdgeNodeProperty:(id)sender;
+- (IBAction)addEdgeNodeAtom:(id)sender;
+- (IBAction)removeEdgeNodeProperty:(id)sender;
+
+//- (IBAction)addRemoveChildNode:(id)sender;
+- (IBAction)refreshDocument:(id)sender;
+
+@end
diff --git a/tikzit/src/osx/PropertyInspectorController.m b/tikzit/src/osx/PropertyInspectorController.m
new file mode 100644
index 0000000..8254949
--- /dev/null
+++ b/tikzit/src/osx/PropertyInspectorController.m
@@ -0,0 +1,272 @@
+//
+// PropertyInspectorController.m
+// TikZiT
+//
+// Created by Aleks Kissinger on 17/07/2011.
+// Copyright 2011 Aleks Kissinger. All rights reserved.
+//
+
+#import "PropertyInspectorController.h"
+#import "StylePaletteController.h"
+#import "TikzDocument.h"
+#import "SFBInspectors/SFBInspectorView.h"
+#import "PickSupport.h"
+#import "Node.h"
+#import "Edge.h"
+#import "NodeStyle.h"
+#import "GraphicsView.h"
+#import "GraphElementProperty.h"
+#import "Shape.h"
+
+@implementation PropertyInspectorController
+
+@synthesize stylePaletteController;
+@synthesize selectedNodes, selectedEdges;
+
+- (id)initWithWindowNibName:(NSString *)windowNibName {
+ [super initWithWindowNibName:windowNibName];
+
+ noSelection = [[GraphElementData alloc] init];
+ [noSelection setProperty:@"" forKey:@"No Selection"];
+ multipleSelection = [[GraphElementData alloc] init];
+ [multipleSelection setProperty:@"" forKey:@"Mult. Selection"];
+ noEdgeNode = [[GraphElementData alloc] init];
+ [noEdgeNode setProperty:@"" forKey:@"No Child"];
+ noGraph = [[GraphElementData alloc] init];
+ [noGraph setProperty:@"" forKey:@"No Graph"];
+
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(graphSelectionChanged:)
+ name:@"SelectionChanged"
+ object:nil];
+
+// [[NSDocumentController sharedDocumentController] addObserver:self
+// forKeyPath:@"currentDocument"
+// options:NSKeyValueObservingOptionNew
+// context:NULL];
+
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(graphSelectionChanged:)
+ name:@"NSWindowDidBecomeMainNotification"
+ object:nil];
+
+
+
+
+ [[self window] setLevel:NSNormalWindowLevel];
+ [self showWindow:self];
+ return self;
+}
+
+- (void)observeValueForKeyPath:(NSString*)keyPath
+ ofObject:(id)object
+ change:(NSDictionary*)change
+ context:(void*)context {
+ [self graphSelectionChanged:nil];
+}
+
+//- (void)willChangeValueForKey:(NSString *)key {
+// [super willChangeValueForKey:key];
+// NSLog(@"will: %@",key);
+//}
+//
+//- (void)didChangeValueForKey:(NSString *)key {
+// [super didChangeValueForKey:key];
+// NSLog(@"did: %@",key);
+//}
+
+- (void)windowDidLoad {
+ [[self window] setMovableByWindowBackground:YES];
+
+ [propertyInspectorView addInspectorPane:graphPropertiesView
+ title:@"Graph Properties"];
+ [propertyInspectorView addInspectorPane:nodePropertiesView
+ title:@"Node Properties"];
+ [propertyInspectorView addInspectorPane:edgePropertiesView
+ title:@"Edge Properties"];
+ [super windowDidLoad];
+}
+
+- (IBAction)refreshDocument:(id)sender {
+ NSDocumentController *dc = [NSDocumentController sharedDocumentController];
+ TikzDocument *doc = (TikzDocument*)[dc currentDocument];
+
+ if (doc != nil) {
+ [[doc graphicsView] postGraphChange];
+ [[doc graphicsView] refreshLayers];
+ }
+}
+
+
+- (void)updateGraphFields {
+ NSDocumentController *dc = [NSDocumentController sharedDocumentController];
+ TikzDocument *doc = (TikzDocument*)[dc currentDocument];
+
+ if (doc != nil) {
+ [graphDataArrayController setContent:[[[doc graphicsView] graph] data]];
+ [graphDataArrayController setSelectionIndexes:[NSIndexSet indexSet]];
+ [graphDataArrayController setEditable:YES];
+ } else {
+ [graphDataArrayController setContent:noGraph];
+ [graphDataArrayController setSelectionIndexes:[NSIndexSet indexSet]];
+ [graphDataArrayController setEditable:NO];
+ }
+}
+
+- (void)updateNodeFields {
+ NSDocumentController *dc = [NSDocumentController sharedDocumentController];
+ TikzDocument *doc = (TikzDocument*)[dc currentDocument];
+ if (doc != nil) {
+ NSSet *sel = [[[doc graphicsView] pickSupport] selectedNodes];
+ [self setSelectedNodes:[[sel allObjects] mutableCopy]];
+ [selectedNodesArrayController setSelectedObjects:selectedNodes];
+ if ([sel count] == 1) {
+ Node *n = [sel anyObject];
+ [nodeDataArrayController setContent:[n data]];
+ [nodeDataArrayController setSelectionIndexes:[NSIndexSet indexSet]];
+ [nodeDataArrayController setEditable:YES];
+ } else if ([sel count] == 0) {
+ [nodeDataArrayController setContent:noSelection];
+ [nodeDataArrayController setSelectionIndexes:[NSIndexSet indexSet]];
+ [nodeDataArrayController setEditable:NO];
+ } else {
+ [nodeDataArrayController setContent:multipleSelection];
+ [nodeDataArrayController setSelectionIndexes:[NSIndexSet indexSet]];
+ [nodeDataArrayController setEditable:NO];
+ }
+ } else {
+ [nodeDataArrayController setContent:noGraph];
+ [nodeDataArrayController setEditable:NO];
+ }
+}
+
+- (void)updateEdgeFields {
+ NSDocumentController *dc = [NSDocumentController sharedDocumentController];
+ TikzDocument *doc = (TikzDocument*)[dc currentDocument];
+
+ if (doc != nil) {
+ NSSet *sel = [[[doc graphicsView] pickSupport] selectedEdges];
+ [self setSelectedEdges:[[sel allObjects] mutableCopy]];
+ [selectedEdgesArrayController setSelectedObjects:selectedEdges];
+ if ([sel count] == 1) {
+ Edge *e = [sel anyObject];
+ [edgeDataArrayController setContent:[e data]];
+ [edgeDataArrayController setSelectionIndexes:[NSIndexSet indexSet]];
+ [edgeDataArrayController setEditable:YES];
+ if ([e hasEdgeNode]) {
+ Node *n = [e edgeNode];
+ [edgeNodeDataArrayController setContent:[n data]];
+ [edgeNodeDataArrayController setSelectionIndexes:[NSIndexSet indexSet]];
+ [edgeNodeDataArrayController setEditable:YES];
+ } else {
+ [edgeNodeDataArrayController setContent:noEdgeNode];
+ [edgeNodeDataArrayController setSelectionIndexes:[NSIndexSet indexSet]];
+ [edgeNodeDataArrayController setEditable:NO];
+ }
+ } else if ([sel count] == 0) {
+ [edgeDataArrayController setContent:noSelection];
+ [edgeDataArrayController setSelectionIndexes:[NSIndexSet indexSet]];
+ [edgeDataArrayController setEditable:NO];
+ [edgeNodeDataArrayController setContent:noSelection];
+ [edgeNodeDataArrayController setSelectionIndexes:[NSIndexSet indexSet]];
+ [edgeNodeDataArrayController setEditable:NO];
+ } else {
+ [edgeDataArrayController setContent:multipleSelection];
+ [edgeDataArrayController setSelectionIndexes:[NSIndexSet indexSet]];
+ [edgeDataArrayController setEditable:NO];
+ [edgeNodeDataArrayController setContent:multipleSelection];
+ [edgeNodeDataArrayController setSelectionIndexes:[NSIndexSet indexSet]];
+ [edgeNodeDataArrayController setEditable:NO];
+ }
+ } else {
+ [edgeDataArrayController setContent:noGraph];
+ [edgeDataArrayController setSelectionIndexes:[NSIndexSet indexSet]];
+ [edgeDataArrayController setEditable:NO];
+ [edgeNodeDataArrayController setContent:noGraph];
+ [edgeNodeDataArrayController setSelectionIndexes:[NSIndexSet indexSet]];
+ [edgeNodeDataArrayController setEditable:NO];
+ }
+}
+
+- (void)graphSelectionChanged:(NSNotification*)notification {
+ [self updateNodeFields];
+ [self updateEdgeFields];
+ [self updateGraphFields];
+}
+
+- (void)controlTextDidEndEditing:(NSNotification*)notification {
+ NSDocumentController *dc = [NSDocumentController sharedDocumentController];
+ TikzDocument *doc = (TikzDocument*)[dc currentDocument];
+ if (doc != nil) {
+ PickSupport *pick = [[doc graphicsView] pickSupport];
+ for (Node *n in [pick selectedNodes]) {
+ [n attachStyleFromTable:[stylePaletteController nodeStyles]];
+ }
+
+ for (Edge *e in [pick selectedEdges]) {
+ [e attachStyleFromTable:[stylePaletteController edgeStyles]];
+ }
+ }
+
+ [self refreshDocument:[notification object]];
+}
+
+- (void)addPropertyToAC:(NSArrayController*)ac {
+ [ac addObject:[[GraphElementProperty alloc] initWithPropertyValue:@"val" forKey:@"new_property"]];
+ [self refreshDocument:nil];
+}
+
+- (void)addAtomToAC:(NSArrayController*)ac {
+ [ac addObject:[[GraphElementProperty alloc] initWithAtomName:@"new_atom"]];
+ [self refreshDocument:nil];
+}
+
+- (void)removeFromAC:(NSArrayController*)ac {
+ [ac remove:nil];
+ [self refreshDocument:nil];
+}
+
+- (IBAction)addNodeProperty:(id)sender { [self addPropertyToAC:nodeDataArrayController]; }
+- (IBAction)addNodeAtom:(id)sender { [self addAtomToAC:nodeDataArrayController]; }
+- (IBAction)removeNodeProperty:(id)sender { [self removeFromAC:nodeDataArrayController]; }
+
+- (IBAction)addGraphProperty:(id)sender { [self addPropertyToAC:graphDataArrayController]; }
+- (IBAction)addGraphAtom:(id)sender { [self addAtomToAC:graphDataArrayController]; }
+- (IBAction)removeGraphProperty:(id)sender { [self removeFromAC:graphDataArrayController]; }
+
+- (IBAction)addEdgeProperty:(id)sender { [self addPropertyToAC:edgeDataArrayController]; }
+- (IBAction)addEdgeAtom:(id)sender { [self addAtomToAC:edgeDataArrayController]; }
+- (IBAction)removeEdgeProperty:(id)sender { [self removeFromAC:edgeDataArrayController]; }
+
+- (IBAction)addEdgeNodeProperty:(id)sender { [self addPropertyToAC:edgeNodeDataArrayController]; }
+- (IBAction)addEdgeNodeAtom:(id)sender { [self addAtomToAC:edgeNodeDataArrayController]; }
+- (IBAction)removeEdgeNodeProperty:(id)sender { [self removeFromAC:edgeNodeDataArrayController]; }
+
+//- (BOOL)enableEdgeDataControls {
+// NSDocumentController *dc = [NSDocumentController sharedDocumentController];
+// TikzDocument *doc = (TikzDocument*)[dc currentDocument];
+//
+// if (doc != nil) {
+// return ([[[[doc graphicsView] pickSupport] selectedEdges] count] == 1);
+// } else {
+// return NO;
+// }
+//}
+//
+//- (BOOL)enableEdgeNodeDataControls {
+// NSDocumentController *dc = [NSDocumentController sharedDocumentController];
+// TikzDocument *doc = (TikzDocument*)[dc currentDocument];
+//
+// if (doc != nil) {
+// PickSupport *pick = [[doc graphicsView] pickSupport];
+// if ([[pick selectedEdges] count] == 1) {
+// return ([[[pick selectedEdges] anyObject] hasEdgeNode]);
+// } else {
+// return NO;
+// }
+// } else {
+// return NO;
+// }
+//}
+
+@end
diff --git a/tikzit/src/osx/SelectBoxLayer.h b/tikzit/src/osx/SelectBoxLayer.h
new file mode 100644
index 0000000..45b43c7
--- /dev/null
+++ b/tikzit/src/osx/SelectBoxLayer.h
@@ -0,0 +1,22 @@
+//
+// SelectBoxLayer.h
+// TikZiT
+//
+// Created by Aleks Kissinger on 14/06/2010.
+// Copyright 2010 __MyCompanyName__. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+#import <QuartzCore/CoreAnimation.h>
+
+@interface SelectBoxLayer : CALayer {
+ BOOL active;
+ CGRect box;
+}
+
+@property (assign) BOOL active;
+@property (assign) NSRect selectBox;
+
++ (SelectBoxLayer*)layer;
+
+@end
diff --git a/tikzit/src/osx/SelectBoxLayer.m b/tikzit/src/osx/SelectBoxLayer.m
new file mode 100644
index 0000000..c198ffa
--- /dev/null
+++ b/tikzit/src/osx/SelectBoxLayer.m
@@ -0,0 +1,48 @@
+//
+// SelectBoxLayer.m
+// TikZiT
+//
+// Created by Aleks Kissinger on 14/06/2010.
+// Copyright 2010 __MyCompanyName__. All rights reserved.
+//
+
+#import "SelectBoxLayer.h"
+
+
+@implementation SelectBoxLayer
+
+@synthesize active;
+
+- (id)init {
+ [super init];
+ box = CGRectMake(0.0f, 0.0f, 0.0f, 0.0f);
+ active = NO;
+ return self;
+}
+
+- (void)setSelectBox:(NSRect)r {
+ box = NSRectToCGRect(r);
+}
+
+- (NSRect)selectBox {
+ return NSRectFromCGRect(box);
+}
+
+- (void)drawInContext:(CGContextRef)context {
+ if (active) {
+ CGContextAddRect(context, box);
+
+ CGContextSetRGBStrokeColor(context, 0.6, 0.6, 0.6, 1);
+ CGContextSetRGBFillColor(context, 0.8, 0.8, 0.8, 0.2);
+ CGContextSetLineWidth(context, 1);
+
+ CGContextSetShouldAntialias(context, NO);
+ CGContextDrawPath(context, kCGPathFillStroke);
+ }
+}
+
++ (SelectBoxLayer*)layer {
+ return [[[SelectBoxLayer alloc] init] autorelease];
+}
+
+@end
diff --git a/tikzit/src/osx/SelectableCollectionViewItem.h b/tikzit/src/osx/SelectableCollectionViewItem.h
new file mode 100644
index 0000000..4a2c571
--- /dev/null
+++ b/tikzit/src/osx/SelectableCollectionViewItem.h
@@ -0,0 +1,33 @@
+//
+// SelectableCollectionViewItem.h
+// TikZiT
+//
+// Copyright 2010 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#import <Cocoa/Cocoa.h>
+#import "StylePaletteController.h"
+
+@interface SelectableCollectionViewItem : NSCollectionViewItem {
+ IBOutlet StylePaletteController *stylePaletteController;
+}
+
+- (void)setStylePaletteController:(StylePaletteController*)spc;
+
+@end
diff --git a/tikzit/src/osx/SelectableCollectionViewItem.m b/tikzit/src/osx/SelectableCollectionViewItem.m
new file mode 100644
index 0000000..880c37b
--- /dev/null
+++ b/tikzit/src/osx/SelectableCollectionViewItem.m
@@ -0,0 +1,54 @@
+//
+// SelectableCollectionViewItem.m
+// TikZiT
+//
+// Copyright 2010 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#import "SelectableCollectionViewItem.h"
+#import "SelectableNodeView.h"
+
+@implementation SelectableCollectionViewItem
+
+- (id)copyWithZone:(NSZone *)zone {
+ SelectableCollectionViewItem *item = [super copyWithZone:zone];
+ [item setStylePaletteController:stylePaletteController];
+ return (id)item;
+}
+
+- (void)setSelected:(BOOL)flag {
+ [super setSelected:flag];
+ [(SelectableNodeView*)[self view] setSelected:flag];
+
+ // only fire this event from the view that lost selection
+ //if (flag == NO) [stylePaletteController selectionDidChange];
+
+ [[self view] setNeedsDisplay:YES];
+}
+
+- (void)setRepresentedObject:(id)object {
+ [super setRepresentedObject:object];
+ [(SelectableNodeView*)[self view] setNodeStyle:(NodeStyle*)object];
+}
+
+- (void)setStylePaletteController:(StylePaletteController*)spc {
+ stylePaletteController = spc;
+}
+
+@end
diff --git a/tikzit/src/osx/SelectableNodeView.h b/tikzit/src/osx/SelectableNodeView.h
new file mode 100644
index 0000000..be7d1a1
--- /dev/null
+++ b/tikzit/src/osx/SelectableNodeView.h
@@ -0,0 +1,38 @@
+//
+// SelectableView.h
+// TikZiT
+//
+// Copyright 2010 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#import <Cocoa/Cocoa.h>
+#import "NodeLayer.h"
+#import "NodeStyle.h"
+#import "NodeStyle+Coder.h"
+
+@interface SelectableNodeView : NSView {
+ BOOL selected;
+ NodeLayer *nodeLayer;
+}
+
+@property (assign) BOOL selected;
+@property (retain) NodeStyle *nodeStyle;
+
+
+@end
diff --git a/tikzit/src/osx/SelectableNodeView.m b/tikzit/src/osx/SelectableNodeView.m
new file mode 100644
index 0000000..6fdd283
--- /dev/null
+++ b/tikzit/src/osx/SelectableNodeView.m
@@ -0,0 +1,96 @@
+//
+// SelectableView.m
+// TikZiT
+//
+// Copyright 2010 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#import "SelectableNodeView.h"
+#import "Shape.h"
+#import "Transformer.h"
+
+@implementation SelectableNodeView
+
+@synthesize selected;
+
+- (id)initWithFrame:(NSRect)frameRect {
+ [super initWithFrame:frameRect];
+ nodeLayer = nil;
+ return self;
+}
+
+-(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)context {
+// NSLog(@"got draw");
+// CGContextSaveGState(context);
+//
+// if (selected) {
+// CGContextSetRGBStrokeColor(context, 0.61f, 0.735f, 1.0f, 1.0f);
+// CGContextSetRGBFillColor(context, 0.61f, 0.735f, 1.0f, 0.5f);
+// CGContextSetLineWidth(context, 1.0f);
+//
+// CGRect box = CGRectMake([layer frame].origin.x + 2,
+// [layer frame].origin.y + 2,
+// [layer frame].size.width - 4,
+// [layer frame].size.height - 4);
+//
+// //CGContextAddRect(context, box);
+// CGContextDrawPath(context, kCGPathFillStroke);
+// }
+//
+// CGContextRestoreGState(context);
+
+ if (nodeLayer!=nil) {
+ if (![[[self layer] sublayers] containsObject:nodeLayer]) {
+ [[self layer] addSublayer:nodeLayer];
+ NSPoint c = NSMakePoint(CGRectGetMidX([[self layer] frame]),
+ CGRectGetMidY([[self layer] frame]));
+ [nodeLayer setCenter:c andAnimateWhen:NO];
+ }
+
+ if (selected) [[nodeLayer selection] select];
+ else [[nodeLayer selection] deselect];
+
+ [nodeLayer updateFrame];
+ }
+}
+
+- (void)drawRect:(NSRect)rect {
+ [super drawRect:rect];
+}
+
+- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent { return YES; }
+
+- (void)setNodeStyle:(NodeStyle *)sty {
+ if (nodeLayer == nil) {
+ nodeLayer = [[NodeLayer alloc] initWithNode:[Node node]
+ transformer:[Transformer defaultTransformer]];
+ [nodeLayer setRescale:NO];
+ }
+
+ [[nodeLayer node] setStyle:sty];
+ [nodeLayer updateFrame];
+}
+
+- (NodeStyle*)nodeStyle {
+ if (nodeLayer != nil) return [[nodeLayer node] style];
+ else return nil;
+}
+
+
+@end
diff --git a/tikzit/src/osx/StylePaletteController.h b/tikzit/src/osx/StylePaletteController.h
new file mode 100644
index 0000000..ed30b58
--- /dev/null
+++ b/tikzit/src/osx/StylePaletteController.h
@@ -0,0 +1,80 @@
+//
+// StylePaletteController.h
+// TikZiT
+//
+// Copyright 2010 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#import <Cocoa/Cocoa.h>
+#import "NodeStyle.h"
+#import "EdgeStyle.h"
+
+@class SFBInspectorView;
+
+@interface StylePaletteController : NSWindowController {
+ NSMutableArray *nodeStyles;
+ NSMutableArray *edgeStyles;
+ IBOutlet NSArrayController *nodeStyleArrayController;
+ IBOutlet NSArrayController *filteredNodeStyleArrayController;
+ IBOutlet NSArrayController *edgeStyleArrayController;
+ IBOutlet NSArrayController *filteredEdgeStyleArrayController;
+ IBOutlet NSCollectionView *collectionView;
+ IBOutlet SFBInspectorView *nodeStyleInspectorView;
+ IBOutlet NSView *nodeStyleView;
+ IBOutlet NSView *edgeStyleView;
+ IBOutlet NSPopUpButton *shapeDropdown;
+ NSString *displayedNodeStyleCategory;
+ NSString *displayedEdgeStyleCategory;
+}
+
+@property (readonly) NSMutableArray *nodeStyles;
+@property (readonly) NSMutableArray *edgeStyles;
+@property (readonly) BOOL documentActive;
+@property (assign) NodeStyle *activeNodeStyle;
+@property (assign) EdgeStyle *activeEdgeStyle;
+@property (copy) NSString *displayedNodeStyleCategory;
+@property (copy) NSString *displayedEdgeStyleCategory;
+@property (readonly) NSPredicate *displayedNodeStylePredicate;
+@property (readonly) NSPredicate *displayedEdgeStylePredicate;
+
+//@property NSString *nodeLabel;
+
+- (id)initWithWindowNibName:(NSString *)windowNibName
+ supportDir:(NSString*)supportDir;
+- (void)saveStyles:(NSString *)plist;
+
+- (IBAction)refreshCollection:(id)sender;
+
+- (IBAction)applyActiveNodeStyle:(id)sender;
+- (IBAction)clearActiveNodeStyle:(id)sender;
+- (IBAction)addNodeStyle:(id)sender;
+
+- (IBAction)appleActiveEdgeStyle:(id)sender;
+- (IBAction)clearActiveEdgeStyle:(id)sender;
+- (IBAction)addEdgeStyle:(id)sender;
+- (void)setActiveEdgeStyle:(EdgeStyle*)style;
+
+- (IBAction)setFillToClosestHashed:(id)sender;
+- (IBAction)setStrokeToClosestHashed:(id)sender;
+
+
+//- (IBAction)changeShape:(id)sender;
+
+
+@end
diff --git a/tikzit/src/osx/StylePaletteController.m b/tikzit/src/osx/StylePaletteController.m
new file mode 100644
index 0000000..8f87bd9
--- /dev/null
+++ b/tikzit/src/osx/StylePaletteController.m
@@ -0,0 +1,247 @@
+//
+// StylePaletteController.m
+// TikZiT
+//
+// Copyright 2010 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#import "StylePaletteController.h"
+#import "TikzDocument.h"
+#import "SFBInspectors/SFBInspectorView.h"
+#import "PickSupport.h"
+#import "Node.h"
+#import "Edge.h"
+#import "NodeStyle.h"
+#import "GraphicsView.h"
+#import "GraphElementProperty.h"
+#import "Shape.h"
+
+@implementation StylePaletteController
+
+@synthesize nodeStyles, edgeStyles;
+
+- (id)initWithWindowNibName:(NSString *)windowNibName
+ supportDir:(NSString*)supportDir
+{
+ if (self = [super initWithWindowNibName:windowNibName]) {
+ NSString *ns = [supportDir stringByAppendingPathComponent:@"nodeStyles.plist"];
+ NSString *es = [supportDir stringByAppendingPathComponent:@"edgeStyles.plist"];
+ nodeStyles = (NSMutableArray*)[NSKeyedUnarchiver
+ unarchiveObjectWithFile:ns];
+ edgeStyles = (NSMutableArray*)[NSKeyedUnarchiver
+ unarchiveObjectWithFile:es];
+
+ if (nodeStyles == nil) nodeStyles = [NSMutableArray array];
+ if (edgeStyles == nil) edgeStyles = [NSMutableArray array];
+
+ [[self window] setLevel:NSNormalWindowLevel];
+ [self showWindow:self];
+ }
+
+ return self;
+}
+
+- (void)windowDidLoad {
+ [[self window] setMovableByWindowBackground:YES];
+ [shapeDropdown addItemsWithTitles:[[Shape shapeDictionary] allKeys]];
+ if ([self activeNodeStyle] != nil) {
+ [shapeDropdown setTitle:[[self activeNodeStyle] shapeName]];
+ }
+
+ [nodeStyleInspectorView addInspectorPane:nodeStyleView
+ title:@"Node Styles"];
+
+ [nodeStyleInspectorView addInspectorPane:edgeStyleView
+ title:@"Edge Styles"];
+
+ [super windowDidLoad];
+}
+
+- (void)saveStyles:(NSString*)supportDir {
+ NSString *ns = [supportDir stringByAppendingPathComponent:@"nodeStyles.plist"];
+ NSString *es = [supportDir stringByAppendingPathComponent:@"edgeStyles.plist"];
+ [NSKeyedArchiver archiveRootObject:nodeStyles toFile:ns];
+ [NSKeyedArchiver archiveRootObject:edgeStyles toFile:es];
+}
+
+- (IBAction)refreshCollection:(id)sender {
+ [collectionView setNeedsDisplay:YES];
+}
+
+
+- (BOOL)documentActive {
+ NSDocumentController *dc = [NSDocumentController sharedDocumentController];
+ return dc.currentDocument != nil;
+}
+
+-(BOOL)collectionView:(NSCollectionView*)collectionView canDragItemsAtIndexes:(NSIndexSet*)indexes withEvent:(NSEvent*)event {
+ return YES;
+}
+
+
+//===========================
+//= setting SVG-safe colors =
+//===========================
+- (IBAction)setFillToClosestHashed:(id)sender {
+ NSArray *sel = [nodeStyleArrayController selectedObjects];
+ if ([sel count] != 0) {
+ NodeStyle *sty = [sel objectAtIndex:0];
+ [sty willChangeValueForKey:@"fillColor"];
+ [sty willChangeValueForKey:@"fillColorIsKnown"];
+ [sty.fillColorRGB setToClosestHashed];
+ [sty didChangeValueForKey:@"fillColor"];
+ [sty didChangeValueForKey:@"fillColorIsKnown"];
+ }
+}
+
+- (IBAction)setStrokeToClosestHashed:(id)sender {
+ NSArray *sel = [nodeStyleArrayController selectedObjects];
+ if ([sel count] != 0) {
+ NodeStyle *sty = [sel objectAtIndex:0];
+ [sty willChangeValueForKey:@"strokeColor"];
+ [sty willChangeValueForKey:@"strokeColorIsKnown"];
+ [sty.strokeColorRGB setToClosestHashed];
+ [sty didChangeValueForKey:@"strokeColor"];
+ [sty didChangeValueForKey:@"strokeColorIsKnown"];
+ }
+}
+
+//=================================================
+//= setting filter predicates for nodes and edges =
+//=================================================
+- (NSString*)displayedNodeStyleCategory {
+ return displayedNodeStyleCategory;
+}
+
+- (void)setDisplayedNodeStyleCategory:(NSString *)cat {
+ [self willChangeValueForKey:@"displayedNodeStylePredicate"];
+ displayedNodeStyleCategory = cat;
+ [self didChangeValueForKey:@"displayedNodeStylePredicate"];
+}
+
+- (NSString*)displayedEdgeStyleCategory {
+ return displayedEdgeStyleCategory;
+}
+
+- (void)setDisplayedEdgeStyleCategory:(NSString *)cat {
+ [self willChangeValueForKey:@"displayedEdgeStylePredicate"];
+ displayedEdgeStyleCategory = cat;
+ [self didChangeValueForKey:@"displayedEdgeStylePredicate"];
+}
+
+- (NSPredicate*)displayedNodeStylePredicate {
+ return [NSPredicate predicateWithFormat:@"category == %@", displayedNodeStyleCategory];
+}
+
+- (NSPredicate*)displayedEdgeStylePredicate {
+ return [NSPredicate predicateWithFormat:@"category == %@", displayedEdgeStyleCategory];
+}
+
+
+//==============================
+//= getting and setting styles =
+//==============================
+
+- (IBAction)applyActiveNodeStyle:(id)sender {
+ NSDocumentController *dc = [NSDocumentController sharedDocumentController];
+ TikzDocument *doc = (TikzDocument*)[dc currentDocument];
+
+ if (doc != nil) {
+ [[doc graphicsView] applyStyleToSelectedNodes:[self activeNodeStyle]];
+ }
+
+ [[doc graphicsView] postSelectionChange];
+}
+
+- (IBAction)clearActiveNodeStyle:(id)sender {
+ [self setActiveNodeStyle:nil];
+
+ NSDocumentController *dc = [NSDocumentController sharedDocumentController];
+ TikzDocument *doc = (TikzDocument*)[dc currentDocument];
+
+ if (doc != nil) {
+ [[doc graphicsView] applyStyleToSelectedNodes:nil];
+ }
+
+ [[doc graphicsView] postSelectionChange];
+}
+
+- (NodeStyle*)activeNodeStyle {
+ NSArray *sel = [filteredNodeStyleArrayController selectedObjects];
+ if ([sel count] == 0) return nil;
+ else return [sel objectAtIndex:0];
+}
+
+- (void)setActiveNodeStyle:(NodeStyle*)style {
+ if ([nodeStyles containsObject:style]) {
+ [filteredNodeStyleArrayController setSelectedObjects:[NSArray arrayWithObject:style]];
+ } else {
+ [filteredNodeStyleArrayController setSelectedObjects:[NSArray array]];
+ }
+}
+
+- (IBAction)appleActiveEdgeStyle:(id)sender {
+ NSDocumentController *dc = [NSDocumentController sharedDocumentController];
+ TikzDocument *doc = (TikzDocument*)[dc currentDocument];
+
+ if (doc != nil) {
+ [[doc graphicsView] applyStyleToSelectedEdges:[self activeEdgeStyle]];
+ }
+}
+
+- (IBAction)clearActiveEdgeStyle:(id)sender {
+ [self setActiveEdgeStyle:nil];
+ [self appleActiveEdgeStyle:sender];
+}
+
+- (EdgeStyle*)activeEdgeStyle {
+ NSArray *sel = [filteredEdgeStyleArrayController selectedObjects];
+ if ([sel count] == 0) return nil;
+ else return [sel objectAtIndex:0];
+}
+
+- (void)setActiveEdgeStyle:(EdgeStyle*)style {
+ if ([edgeStyles containsObject:style]) {
+ [filteredEdgeStyleArrayController setSelectedObjects:[NSArray arrayWithObject:style]];
+ } else {
+ [filteredEdgeStyleArrayController setSelectedObjects:[NSArray array]];
+ }
+}
+
+
+//=================
+//= adding styles =
+//=================
+
+- (IBAction)addEdgeStyle:(id)sender {
+ EdgeStyle *sty = [[EdgeStyle alloc] init];
+ [sty setCategory:displayedEdgeStyleCategory];
+ [edgeStyleArrayController addObject:sty];
+ [self setActiveEdgeStyle:sty];
+}
+
+- (IBAction)addNodeStyle:(id)sender {
+ NodeStyle *sty = [[NodeStyle alloc] init];
+ [sty setCategory:displayedNodeStyleCategory];
+ [nodeStyleArrayController addObject:sty];
+ [self setActiveNodeStyle:sty];
+}
+
+
+@end
diff --git a/tikzit/src/osx/TikzDocument.h b/tikzit/src/osx/TikzDocument.h
new file mode 100644
index 0000000..d817b2e
--- /dev/null
+++ b/tikzit/src/osx/TikzDocument.h
@@ -0,0 +1,37 @@
+//
+// TikzDocument.h
+// TikZiT
+//
+// Copyright 2010 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#import <Cocoa/Cocoa.h>
+#import "GraphicsView.h"
+#import "TikzSourceController.h"
+#import "PreviewController.h"
+#import "GraphicsView.h"
+
+@interface TikzDocument : NSDocument {
+ NSString *tikz;
+}
+
+@property (readonly) NSString *tikz;
+@property (readonly) GraphicsView *graphicsView;
+
+@end
diff --git a/tikzit/src/osx/TikzDocument.m b/tikzit/src/osx/TikzDocument.m
new file mode 100644
index 0000000..ef5908d
--- /dev/null
+++ b/tikzit/src/osx/TikzDocument.m
@@ -0,0 +1,84 @@
+//
+// TikzDocument.m
+// TikZiT
+//
+// Copyright 2010 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#import "TikzDocument.h"
+#import "TikzWindowController.h"
+
+@implementation TikzDocument
+
+@synthesize tikz;
+
+- (id)init {
+ self = [super init];
+ if (self) {
+ tikz = nil;
+ }
+ return self;
+}
+
+//- (NSString *)windowNibName {
+// // Override returning the nib file name of the document
+// // If you need to use a subclass of NSWindowController or if your document supports multiple NSWindowControllers, you should remove this method and override -makeWindowControllers instead.
+// return @"TikzDocument";
+//}
+
+- (void)makeWindowControllers {
+ TikzWindowController *wc = [[TikzWindowController alloc] initWithDocument:self];
+ [self addWindowController:wc];
+}
+
+- (void)windowControllerDidLoadNib:(NSWindowController *) aController {
+ [super windowControllerDidLoadNib:aController];
+ [[self graphicsView] refreshLayers];
+ // Add any code here that needs to be executed once the windowController has loaded the document's window.
+}
+
+- (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError {
+ TikzWindowController *wc =
+ (TikzWindowController*)[[self windowControllers] objectAtIndex:0];
+ NSData *outData = [[[wc tikzSourceController] tikz] dataUsingEncoding:NSUTF8StringEncoding];
+
+ if ( outError != NULL ) {
+ *outError = [NSError errorWithDomain:NSOSStatusErrorDomain code:unimpErr userInfo:NULL];
+ }
+ return outData;
+}
+
+- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError {
+ tikz = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
+
+ if ( outError != NULL ) {
+ *outError = [NSError errorWithDomain:NSOSStatusErrorDomain code:unimpErr userInfo:NULL];
+ }
+
+ return YES;
+}
+
+- (GraphicsView*)graphicsView {
+ TikzWindowController *wc =
+ (TikzWindowController*)[[self windowControllers] objectAtIndex:0];
+ return [wc graphicsView];
+}
+
+
+@end
diff --git a/tikzit/src/osx/TikzParser.h b/tikzit/src/osx/TikzParser.h
new file mode 100644
index 0000000..18b183f
--- /dev/null
+++ b/tikzit/src/osx/TikzParser.h
@@ -0,0 +1,66 @@
+//
+// TikzParser.h
+// TikZiT
+//
+// Copyright 2010 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#import <Cocoa/Cocoa.h>
+#import <ParseKit/ParseKit.h>
+
+#import "Graph.h"
+
+@interface TikzParser : NSObject {
+ PKParser *nodeParser;
+ PKParser *edgeParser;
+ PKParser *tikzPictureParser;
+ PKTokenizer *tokenizer;
+
+ NSString *currentKey;
+ NSString *currentSourceArrow;
+
+ NSMutableArray *atoms;
+ NSMutableDictionary *properties;
+ NSString *leftArrow;
+ NSString *rightArrow;
+
+ GraphElementData *elementData;
+
+ Graph *graph;
+ Node *currentNode;
+ NSMutableDictionary *nodeTable;
+
+ Edge *currentEdge;
+ BOOL matchingEdgeNode;
+ NSString *sourceName;
+ NSString *targName;
+
+ NSPoint bbox1, bbox2;
+ BOOL bboxFirstPoint;
+}
+
+@property (retain) Graph *graph;
+
+- (id)init;
+- (BOOL)parseNode:(NSString*)str;
+- (BOOL)parseEdge:(NSString*)str;
+- (BOOL)parseTikzPicture:(NSString*)str forGraph:(Graph*)g;
+- (BOOL)parseTikzPicture:(NSString *)str;
+
+@end
diff --git a/tikzit/src/osx/TikzParser.m b/tikzit/src/osx/TikzParser.m
new file mode 100644
index 0000000..2bb5a15
--- /dev/null
+++ b/tikzit/src/osx/TikzParser.m
@@ -0,0 +1,574 @@
+//
+// TikzParser.m
+// TikZiT
+//
+// Copyright 2010 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#import "TikzParser.h"
+
+// custom parsekit extensions
+#import "PKNaturalNumber.h"
+#import "PKRepetition+RepeatPlus.h"
+#import "PKSpecificDelimitedString.h"
+#import "PKBalancingDelimitState.h"
+
+#import "util.h"
+
+@interface TikzParser ()
+
+- (NSString*)popAllToString:(PKAssembly*)a withSeparator:(NSString*)sep;
+- (PKParser*)eatSymbol:(NSString*)s;
+- (PKParser*)eatLiteral:(NSString*)s;
+- (PKParser*)eatWord;
+
+// properties
+- (void)willMatchProplist:(PKAssembly*)a;
+- (void)didMatchProplist:(PKAssembly*)a;
+- (void)willMatchProperty:(PKAssembly*)a;
+- (void)didMatchArrowSpecDash:(PKAssembly*)a;
+
+// nodes
+- (void)didMatchNodeCommand:(PKAssembly*)a;
+- (void)didMatchNodeLabel:(PKAssembly*)a;
+- (void)didMatchNode:(PKAssembly*)a;
+- (void)didMatchNodeName:(PKAssembly*)a;
+- (void)didMatchCoords:(PKAssembly*)a;
+
+// edges
+- (void)didMatchDrawCommand:(PKAssembly *)a;
+- (void)didMatchEdge:(PKAssembly*)a;
+- (void)didMatchEdgeNodeCommand:(PKAssembly*)a;
+- (void)didMatchEdgeNode:(PKAssembly*)a;
+
+// bounding box
+- (void)didMatchPathCommand:(PKAssembly*)a;
+- (void)didMatchBoundingBox:(PKAssembly*)a;
+
+// tikzpicture
+- (void)willMatchTikzPicture:(PKAssembly*)a;
+- (void)didMatchTikzPicture:(PKAssembly*)a;
+
+@end
+
+
+@implementation TikzParser
+
+@synthesize graph;
+
+- (id)init {
+ [super init];
+
+ currentKey = nil;
+ currentSourceArrow = nil;
+ tokenizer = [PKTokenizer tokenizer];
+
+ graph = nil;
+ currentNode = nil;
+ currentEdge = nil;
+ matchingEdgeNode = NO;
+
+ // tweak the tokenizer a bit
+ [tokenizer.symbolState remove:@"<="];
+ [tokenizer.symbolState remove:@">="];
+ [tokenizer.symbolState remove:@"{"];
+ [tokenizer.symbolState remove:@"}"];
+ [tokenizer setTokenizerState:tokenizer.wordState from:'\\' to:'\\'];
+ [tokenizer.wordState setWordChars:NO from:'-' to:'-'];
+ tokenizer.delimitState = [[PKBalancingDelimitState alloc] init];
+ [tokenizer.delimitState addStartMarker:@"{"
+ endMarker:@"}"
+ allowedCharacterSet:nil];
+ [tokenizer setTokenizerState:tokenizer.delimitState from:'{' to:'{'];
+
+ /*
+
+
+ GRAMMAR FOR TIKZPICTURE
+
+ tikzpicture = '\begin' '{tikzpicture}'
+ optproplist
+ expr*
+ '\end' '{tikzpicture}'
+ expr = node | edge | boundingbox | layerexpr
+ layerexpr = '\begin' '{pgfonlayer}' DelimitedString | '\end' '{pgfonlayer}'
+
+
+ GRAMMAR FOR PROPERTY LISTS
+
+ optproplist = proplist | Empty;
+ proplist = '[' property property1* ']';
+ property = arrowspec | keyval | atom;
+ property1 = ',' property;
+ keyval = key '=' val;
+ atom = propsym+;
+ arrowspec = propsym* '-' propsym*;
+ key = propsym+;
+ val = propsym+ | QuotedString;
+ propsym = (Word | Number | '<' | '>');
+
+
+ GRAMMAR FOR NODES
+
+ node = '\node' optproplist name 'at' coords DelimitedString ';';
+ nodename = '(' nodeid ')';
+ nodeid = Word | NaturalNumber;
+ coords = '(' Number ',' Number ')';
+
+
+ GRAMMAR FOR EDGES
+
+ edge = '\draw' optproplist nodename 'to' optedgenode ( nodename | selfloop ) ';';
+ selfloop = '(' ')';
+ optedgenode = Empty | edgenode
+ edgenode = 'node' optproplist name coords '{' '}' ';';
+
+ GRAMMAR FOR BOUNDING BOX
+
+ boundingbox = '\path' '[' 'use' 'as' 'bounding' 'box' ']' coords 'rectangle' coords ';'
+
+ */
+
+
+ PKAlternation *nodeid = [PKAlternation alternation];
+ nodeid.name = @"node identifier";
+ [nodeid add:[PKWord word]];
+ [nodeid add:[PKNaturalNumber number]];
+
+ PKAlternation *propsym = [PKAlternation alternation];
+ propsym.name = @"property symbol";
+ [propsym add:[PKWord word]];
+ [propsym add:[PKNumber number]];
+ [propsym add:[PKSymbol symbolWithString:@"<"]];
+ [propsym add:[PKSymbol symbolWithString:@">"]];
+
+ PKSequence *anchor = [PKSequence sequence];
+ [anchor add:[self eatSymbol:@"."]];
+ [anchor add:[self eatWord]];
+
+ PKAlternation *optanchor = [PKAlternation alternation];
+ [optanchor add:anchor];
+ [optanchor add:[PKEmpty empty]];
+
+ PKSequence *nodename = [PKSequence sequence];
+ nodename.name = @"node name";
+ [nodename add:[self eatSymbol:@"("]];
+ [nodename add:nodeid];
+ [nodename add:optanchor];
+ [nodename add:[self eatSymbol:@")"]];
+ [nodename setAssembler:self selector:@selector(didMatchNodeName:)];
+
+ PKTrack *coords = [PKTrack track];
+ coords.name = @"coordinate definition";
+ [coords add:[self eatSymbol:@"("]];
+ [coords add:[PKNumber number]];
+ [coords add:[self eatSymbol:@","]];
+ [coords add:[PKNumber number]];
+ [coords add:[self eatSymbol:@")"]];
+ [coords setAssembler:self selector:@selector(didMatchCoords:)];
+
+ PKSequence *key = [PKRepetition repetitionPlusWithSubparser:propsym];
+
+ PKAlternation *val = [PKAlternation alternation];
+ [val add:[PKRepetition repetitionPlusWithSubparser:propsym]];
+ [val add:[PKQuotedString quotedString]];
+ [val setPreassembler:self selector:@selector(willMatchVal:)];
+
+ PKSequence *keyval = [PKSequence sequence];
+ [keyval add:key];
+ [keyval add:[self eatLiteral:@"="]];
+ [keyval add:val];
+
+ PKSequence *atom = [PKRepetition repetitionPlusWithSubparser:propsym];
+
+ PKSymbol *arrowspecdash = [PKSymbol symbolWithString:@"-"];
+ [arrowspecdash setAssembler:self selector:@selector(didMatchArrowSpecDash:)];
+
+ PKSequence *arrowspec = [PKSequence sequence];
+ [arrowspec add:[PKRepetition repetitionWithSubparser:propsym]];
+ [arrowspec add:arrowspecdash];
+ [arrowspec add:[PKRepetition repetitionWithSubparser:propsym]];
+
+ PKAlternation *property = [PKAlternation alternation];
+ property.name = @"property, atom, or arrow specification";
+ [property add:keyval];
+ [property add:arrowspec];
+ [property add:atom];
+ [property setPreassembler:self selector:@selector(willMatchProperty:)];
+
+ PKSequence *property1 = [PKSequence sequence];
+ [property1 add:[self eatLiteral:@","]];
+ [property1 add:property];
+
+ PKTrack *proplist = [PKTrack track];
+ proplist.name = @"property list";
+ [proplist add:[self eatSymbol:@"["]];
+ [proplist add:property];
+ [proplist add:[PKRepetition repetitionWithSubparser:property1]];
+ [proplist add:[self eatSymbol:@"]"]];
+ [proplist setPreassembler:self selector:@selector(willMatchProplist:)];
+ [proplist setAssembler:self selector:@selector(didMatchProplist:)];
+
+ PKAlternation *optproplist = [PKAlternation alternation];
+ [optproplist add:proplist];
+ [optproplist add:[PKEmpty empty]];
+
+ PKLiteral *nodeCommand = [PKLiteral literalWithString:@"\\node"];
+ [nodeCommand setAssembler:self selector:@selector(didMatchNodeCommand:)];
+
+ PKTerminal *nodeLabel = [PKDelimitedString delimitedString];
+ nodeLabel.name = @"Possibly empty node label";
+ [nodeLabel setAssembler:self selector:@selector(didMatchNodeLabel:)];
+
+ PKTrack *node = [PKTrack track];
+ [node add:nodeCommand];
+ [node add:optproplist];
+ [node add:nodename];
+ [node add:[self eatLiteral:@"at"]];
+ [node add:coords];
+ [node add:nodeLabel];
+ //[node add:[[PKDelimitedString delimitedString] discard]];
+ [node add:[self eatSymbol:@";"]];
+ [node setAssembler:self selector:@selector(didMatchNode:)];
+
+ PKLiteral *drawCommand = [PKLiteral literalWithString:@"\\draw"];
+ [drawCommand setAssembler:self selector:@selector(didMatchDrawCommand:)];
+
+ PKSequence *parens = [PKSequence sequence];
+ [parens add:[self eatSymbol:@"("]];
+ [parens add:[self eatSymbol:@")"]];
+
+ PKAlternation *nodenamealt = [PKAlternation alternation];
+ nodenamealt.name = @"node name or '()'";
+ [nodenamealt add:nodename];
+ [nodenamealt add:parens];
+
+ PKLiteral *edgenodeCommand = [PKLiteral literalWithString:@"node"];
+ edgenodeCommand.name = @"edge node command";
+ [edgenodeCommand setAssembler:self selector:@selector(didMatchEdgeNodeCommand:)];
+
+ PKSequence *edgenode = [PKSequence sequence];
+ [edgenode add:edgenodeCommand];
+ [edgenode add:optproplist];
+ [edgenode add:nodeLabel];
+ edgenode.name = @"edge node";
+ [edgenode setAssembler:self selector:@selector(didMatchEdgeNode:)];
+
+
+ PKAlternation *optedgenode = [PKAlternation alternation];
+ [optedgenode add:[PKEmpty empty]];
+ [optedgenode add:edgenode];
+
+
+ PKTrack *edge = [PKTrack track];
+ [edge add:drawCommand];
+ [edge add:optproplist];
+ [edge add:nodename];
+ [edge add:[self eatLiteral:@"to"]];
+ [edge add:optedgenode];
+ [edge add:nodenamealt];
+ [edge add:[self eatSymbol:@";"]];
+ [edge setAssembler:self selector:@selector(didMatchEdge:)];
+
+
+ PKLiteral *pathliteral = [PKLiteral literalWithString:@"\\path"];
+ [pathliteral setAssembler:self selector:@selector(didMatchPathCommand:)];
+
+ PKTrack *boundingbox = [PKTrack track];
+ [boundingbox add:pathliteral];
+ [boundingbox add:[self eatSymbol:@"["]];
+ [boundingbox add:[self eatLiteral:@"use"]];
+ [boundingbox add:[self eatLiteral:@"as"]];
+ [boundingbox add:[self eatLiteral:@"bounding"]];
+ [boundingbox add:[self eatLiteral:@"box"]];
+ [boundingbox add:[self eatSymbol:@"]"]];
+ [boundingbox add:coords];
+ [boundingbox add:[self eatLiteral:@"rectangle"]];
+ [boundingbox add:coords];
+ [boundingbox add:[self eatSymbol:@";"]];
+ [boundingbox setAssembler:self selector:@selector(didMatchBoundingBox:)];
+
+ PKTerminal *layerLiteral =
+ [[PKSpecificDelimitedString delimitedStringWithValue:@"{pgfonlayer}"]
+ discard];
+
+ PKSequence *beginLayer = [PKSequence sequence];
+ [beginLayer add:[self eatLiteral:@"\\begin"]];
+ [beginLayer add:layerLiteral];
+ [beginLayer add:[[PKDelimitedString delimitedString] discard]];
+
+ PKSequence *endLayer = [PKSequence sequence];
+ [endLayer add:[self eatLiteral:@"\\end"]];
+ [endLayer add:layerLiteral];
+
+ PKAlternation *expr = [PKAlternation alternation];
+ [expr add:node];
+ [expr add:edge];
+ [expr add:boundingbox];
+ [expr add:beginLayer];
+ [expr add:endLayer];
+
+ PKTerminal *tikzpicLiteral =
+ [[PKSpecificDelimitedString delimitedStringWithValue:@"{tikzpicture}"]
+ discard];
+
+ //tikzpicLiteral.name = @"{tikzpicture}";
+
+ PKTrack *tikzpic = [PKTrack track];
+ [tikzpic add:[PKEmpty empty]];
+ [tikzpic add:[self eatLiteral:@"\\begin"]];
+ [tikzpic add:tikzpicLiteral];
+ [tikzpic add:optproplist];
+ [tikzpic add:[PKRepetition repetitionWithSubparser:expr]];
+ [tikzpic add:[self eatLiteral:@"\\end"]];
+ [tikzpic add:tikzpicLiteral];
+ [tikzpic setPreassembler:self selector:@selector(willMatchTikzPicture:)];
+ [tikzpic setAssembler:self selector:@selector(didMatchTikzPicture:)];
+
+ nodeParser = node;
+ edgeParser = edge;
+ tikzPictureParser = tikzpic;
+
+ return self;
+}
+
+- (NSString*)popAllToString:(PKAssembly*)a withSeparator:(NSString*)sep {
+ NSString *str = @"";
+ BOOL fst = YES;
+
+ while (![a isStackEmpty]) {
+ if (fst) fst = NO;
+ else str = [sep stringByAppendingString:str];
+
+ PKToken *tok = [a pop];
+ str = [tok.stringValue stringByAppendingString:str];
+ }
+
+ return str;
+}
+
+- (PKParser*)eatSymbol:(NSString*)s {
+ return [[PKSymbol symbolWithString:s] discard];
+}
+
+- (PKParser*)eatLiteral:(NSString*)s {
+ return [[PKLiteral literalWithString:s] discard];
+}
+
+- (PKParser*)eatWord {
+ return [[PKWord word] discard];
+}
+
+- (void)packProperty:(PKAssembly*)a {
+ BOOL empty = [a isStackEmpty];
+ NSString *val = [self popAllToString:a withSeparator:@" "];
+
+ if (currentKey != nil) {
+ [elementData setProperty:val forKey:currentKey];
+// NSLog(@" keyval: (%@) => (%@)", currentKey, val);
+ } else if (currentSourceArrow != nil) {
+ [elementData setArrowSpecFrom:currentSourceArrow to:val];
+// NSLog(@" arrowspec: (%@-%@)", currentSourceArrow, val);
+ } else if (!empty) {
+ [elementData setAtom:val];
+// NSLog(@" atom: (%@)", val);
+ }
+
+ currentKey = nil;
+ currentSourceArrow = nil;
+}
+
+- (BOOL)parseNode:(NSString *)str {
+ tokenizer.string = str;
+ PKAssembly *res = [nodeParser completeMatchFor:[PKTokenAssembly assemblyWithTokenizer:tokenizer]];
+
+// NSLog(@"result: %@", res);
+ return res != nil;
+}
+
+- (BOOL)parseEdge:(NSString *)str {
+ tokenizer.string = str;
+ PKAssembly *res = [edgeParser completeMatchFor:[PKTokenAssembly assemblyWithTokenizer:tokenizer]];
+
+// NSLog(@"result: %@", res);
+ return res != nil;
+}
+
+- (BOOL)parseTikzPicture:(NSString*)str forGraph:(Graph*)g {
+ self.graph = g;
+ tokenizer.string = str;
+ PKTokenAssembly *assm = [PKTokenAssembly assemblyWithTokenizer:tokenizer];
+ PKAssembly *res = [tikzPictureParser completeMatchFor:assm];
+
+// NSLog(@"result: %@", res);
+ return res != nil;
+}
+
+- (BOOL)parseTikzPicture:(NSString *)str {
+ return [self parseTikzPicture:str forGraph:[Graph graph]];
+}
+
+
+- (void)didMatchNodeCommand:(PKAssembly*)a {
+ [a pop];
+ currentNode = [Node node];
+ [currentNode updateData];
+// NSLog(@"<node>");
+}
+
+- (void)didMatchNodeLabel:(PKAssembly*)a {
+ PKToken *tok = [a pop];
+ NSString *s = tok.stringValue;
+ s = [s substringWithRange:NSMakeRange(1, [s length]-2)];
+ if (matchingEdgeNode) currentEdge.edgeNode.label = s;
+ else currentNode.label = s;
+}
+
+- (void)didMatchNode:(PKAssembly*)a {
+ [nodeTable setObject:currentNode forKey:currentNode.name];
+ [graph addNode:currentNode];
+ currentNode = nil;
+// NSLog(@"</node>");
+}
+
+- (void)didMatchDrawCommand:(PKAssembly*)a {
+ [a pop];
+ currentEdge = [Edge edge];
+ sourceName = nil;
+ targName = nil;
+// NSLog(@"<edge>");
+}
+
+- (void)didMatchEdge:(PKAssembly*)a {
+ Node *src = [nodeTable objectForKey:sourceName];
+ currentEdge.source = src;
+ currentEdge.target = (targName == nil) ? src : [nodeTable objectForKey:targName];
+ [currentEdge setAttributesFromData];
+ [graph addEdge:currentEdge];
+ currentEdge = nil;
+// NSLog(@"</edge>");
+}
+
+- (void)didMatchEdgeNodeCommand:(PKAssembly*)a {
+ [a pop];
+ matchingEdgeNode = YES;
+ currentEdge.edgeNode = [Node node];
+}
+
+- (void)didMatchEdgeNode:(PKAssembly*)a {
+ matchingEdgeNode = NO;
+}
+
+- (void)willMatchVal:(PKAssembly*)a {
+ currentKey = [self popAllToString:a withSeparator:@" "];
+// NSLog(@"key: %@", currentKey);
+}
+
+- (void)willMatchProperty:(PKAssembly*)a {
+ [self packProperty:a];
+}
+
+- (void)willMatchProplist:(PKAssembly*)a {
+ elementData = [[GraphElementData alloc] init];
+}
+
+- (void)didMatchProplist:(PKAssembly*)a {
+ [self packProperty:a];
+
+ if (currentNode != nil) {
+ currentNode.data = elementData;
+ } else if (currentEdge != nil) {
+ if (matchingEdgeNode) currentEdge.edgeNode.data = elementData;
+ else currentEdge.data = elementData;
+ } else { // add properties to to graph
+ graph.data = elementData;
+ }
+
+ elementData = nil;
+}
+
+- (void)didMatchNodeName:(PKAssembly*)a {
+ NSString *name = ((PKToken*)[a pop]).stringValue;
+
+ if (currentNode != nil) {
+ currentNode.name = name;
+// NSLog(@" name: (%@)", name);
+ } else if (currentEdge != nil) {
+ if (sourceName == nil) {
+ sourceName = name;
+// NSLog(@" source: (%@)", name);
+ } else {
+ targName = name;
+// NSLog(@" target: (%@)", name);
+ }
+ }
+}
+
+- (void)didMatchCoords:(PKAssembly*)a {
+ NSPoint p;
+ p.y = ((PKToken*)[a pop]).floatValue;
+ p.x = ((PKToken*)[a pop]).floatValue;
+
+ if (currentNode != nil) {
+ currentNode.point = p;
+ } else {
+ if (bboxFirstPoint) {
+ bboxFirstPoint = NO;
+ bbox1 = p;
+ } else {
+ bbox2 = p;
+ }
+ }
+
+// NSLog(@" coord: (%f, %f)", p.x, p.y);
+}
+
+- (void)didMatchPathCommand:(PKAssembly*)a {
+ [a pop];
+ bboxFirstPoint = YES;
+}
+
+- (void)didMatchBoundingBox:(PKAssembly*)a {
+ graph.boundingBox = NSRectAroundPoints(bbox1, bbox2);
+}
+
+- (void)didMatchArrowSpecDash:(PKAssembly*)a {
+ [a pop]; // pop off the dash
+ currentSourceArrow = [self popAllToString:a withSeparator:@" "];
+}
+
+- (void)willMatchTikzPicture:(PKAssembly*)a {
+// NSLog(@"<tikz>");
+ nodeTable = [NSMutableDictionary dictionary];
+}
+
+- (void)didMatchTikzPicture:(PKAssembly*)a {
+// NSLog(@"</tikz>");
+// NSLog(@"%@", [graph tikz]);
+}
+
+- (void)finalize {
+// NSLog(@"releasing subparser trees");
+ PKReleaseSubparserTree(nodeParser);
+ PKReleaseSubparserTree(edgeParser);
+ PKReleaseSubparserTree(tikzPictureParser);
+ [super finalize];
+}
+
+@end
diff --git a/tikzit/src/osx/TikzSourceController.h b/tikzit/src/osx/TikzSourceController.h
new file mode 100644
index 0000000..01c2cf3
--- /dev/null
+++ b/tikzit/src/osx/TikzSourceController.h
@@ -0,0 +1,66 @@
+//
+// TikzSourceController.h
+// TikZiT
+//
+// Copyright 2010 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#import <Cocoa/Cocoa.h>
+
+#import "GraphicsView.h"
+#import "TikzGraphAssembler.h"
+
+@interface TikzSourceController : NSObject {
+ GraphicsView *graphicsView;
+ NSTextView *sourceView;
+ NSAttributedString *source;
+ NSTextField *status;
+ NSDictionary *textAttrs;
+ NSColor *successColor;
+ NSColor *failedColor;
+
+
+ NSUndoManager *documentUndoManager;
+
+ BOOL tikzChanged;
+ BOOL justUndid;
+
+ TikzGraphAssembler *assembler;
+}
+
+@property BOOL tikzChanged;
+@property IBOutlet GraphicsView *graphicsView;
+@property IBOutlet NSTextView *sourceView;
+@property IBOutlet NSTextField *status;
+@property NSUndoManager *documentUndoManager;
+@property (copy) NSAttributedString *source;
+@property (copy) NSString *tikz;
+
+- (void)updateTikzFromGraph;
+- (void)graphChanged:(NSNotification*)n;
+
+// called by code, these do not register an undo
+- (BOOL)tryParseTikz;
+- (void)doRevertTikz;
+
+// called by user, these do register an undo
+- (void)parseTikz:(id)sender;
+- (void)revertTikz:(id)sender;
+
+@end
diff --git a/tikzit/src/osx/TikzSourceController.m b/tikzit/src/osx/TikzSourceController.m
new file mode 100644
index 0000000..6d1580c
--- /dev/null
+++ b/tikzit/src/osx/TikzSourceController.m
@@ -0,0 +1,196 @@
+//
+// TikzSourceController.m
+// TikZiT
+//
+// Copyright 2010 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#import "TikzSourceController.h"
+#import "Graph.h"
+
+@implementation TikzSourceController
+
+@synthesize graphicsView, sourceView, source, status;
+@synthesize documentUndoManager, tikzChanged;
+
+- (void)endEditing {
+ NSResponder *res = [[sourceView window] firstResponder];
+ [[sourceView window] makeFirstResponder:nil];
+ [[sourceView window] makeFirstResponder:res];
+}
+
+- (void)undoParseTikz:(Graph *)oldGraph {
+ [graphicsView setGraph:oldGraph];
+ [graphicsView setEnabled:NO];
+ [graphicsView postGraphChange];
+ [graphicsView refreshLayers];
+
+ [documentUndoManager registerUndoWithTarget:self
+ selector:@selector(parseTikz:)
+ object:self];
+ [documentUndoManager setActionName:@"Parse Tikz"];
+}
+
+- (void)undoRevertTikz:(NSString*)oldTikz {
+ [self setTikz:oldTikz];
+ [graphicsView setEnabled:NO];
+ [graphicsView refreshLayers];
+
+ [documentUndoManager registerUndoWithTarget:self
+ selector:@selector(revertTikz:)
+ object:self];
+ [documentUndoManager setActionName:@"Revert Tikz"];
+}
+
+- (void)undoTikzChange:(id)ignore {
+ [graphicsView setEnabled:YES];
+ [graphicsView refreshLayers];
+ [self endEditing];
+ [self updateTikzFromGraph];
+ [documentUndoManager registerUndoWithTarget:self
+ selector:@selector(redoTikzChange:)
+ object:nil];
+ [documentUndoManager setActionName:@"Tikz Change"];
+}
+
+- (void)redoTikzChange:(id)ignore {
+ [graphicsView setEnabled:NO];
+ [graphicsView refreshLayers];
+ [documentUndoManager registerUndoWithTarget:self
+ selector:@selector(undoTikzChange:)
+ object:nil];
+ [documentUndoManager setActionName:@"Tikz Change"];
+}
+
+
+- (void)awakeFromNib {
+ justUndid = NO;
+ assembler = [[TikzGraphAssembler alloc] init];
+
+ successColor = [NSColor colorWithCalibratedRed:0.0f
+ green:0.5f
+ blue:0.0f
+ alpha:1.0f];
+ failedColor = [NSColor redColor];
+
+ NSFont *font = [NSFont userFixedPitchFontOfSize:11.0f];
+
+ if (font != nil) {
+ textAttrs = [NSDictionary dictionaryWithObject:font
+ forKey:NSFontAttributeName];
+ } else {
+ NSLog(@"WARNING: couldn't find monospaced font.");
+ textAttrs = [NSDictionary dictionary];
+ }
+
+
+ [self graphChanged:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(graphChanged:)
+ name:@"GraphChanged"
+ object:graphicsView];
+}
+
+- (void)setTikz:(NSString *)str {
+ [self willChangeValueForKey:@"source"];
+ source = [[NSAttributedString alloc] initWithString:str attributes:textAttrs];
+ [self didChangeValueForKey:@"source"];
+}
+
+- (NSString*)tikz {
+ return [source string];
+}
+
+- (void)updateTikzFromGraph {
+ [self setTikz:[[graphicsView graph] tikz]];
+}
+
+- (void)graphChanged:(NSNotification*)n {
+ if ([graphicsView enabled]) [self updateTikzFromGraph];
+}
+
+- (void)textDidBeginEditing:(NSNotification *)notification {
+ if ([graphicsView enabled]) {
+ [graphicsView setEnabled:NO];
+ [graphicsView refreshLayers];
+ [documentUndoManager registerUndoWithTarget:self
+ selector:@selector(undoTikzChange:)
+ object:nil];
+ [documentUndoManager setActionName:@"Tikz Change"];
+ }
+}
+
+- (BOOL)tryParseTikz {
+ BOOL success = [assembler parseTikz:[self tikz]];
+
+ if (success) {
+ [graphicsView deselectAll:self];
+ [graphicsView setGraph:[assembler graph]];
+ [graphicsView refreshLayers];
+ [self doRevertTikz];
+ }
+
+ return success;
+}
+
+- (void)doRevertTikz {
+ [self updateTikzFromGraph];
+ [self endEditing];
+ [graphicsView setEnabled:YES];
+ [graphicsView refreshLayers];
+ [status setStringValue:@""];
+}
+
+- (void)parseTikz:(id)sender {
+ if (![graphicsView enabled]) {
+ Graph *oldGraph = [graphicsView graph];
+ if ([self tryParseTikz]) {
+ [self endEditing];
+ [documentUndoManager registerUndoWithTarget:self
+ selector:@selector(undoParseTikz:)
+ object:oldGraph];
+ [documentUndoManager setActionName:@"Parse Tikz"];
+
+ [status setStringValue:@"success"];
+ [status setTextColor:successColor];
+ } else {
+ [status setStringValue:@"parse error"];
+ [status setTextColor:failedColor];
+ }
+ }
+}
+
+- (void)revertTikz:(id)sender {
+ if (![graphicsView enabled]) {
+ NSString *oldTikz = [[self tikz] copy];
+ [self doRevertTikz];
+
+ [documentUndoManager registerUndoWithTarget:self
+ selector:@selector(undoRevertTikz:)
+ object:oldTikz];
+ [documentUndoManager setActionName:@"Revert Tikz"];
+ }
+}
+
+- (void)finalize {
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+ [super finalize];
+}
+
+@end
diff --git a/tikzit/src/osx/TikzWindowController.h b/tikzit/src/osx/TikzWindowController.h
new file mode 100644
index 0000000..e35b7eb
--- /dev/null
+++ b/tikzit/src/osx/TikzWindowController.h
@@ -0,0 +1,31 @@
+//
+// TikzWindowController.h
+// TikZiT
+//
+// Created by Aleks Kissinger on 26/01/2011.
+// Copyright 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+@class TikzDocument, GraphicsView, TikzSourceController;
+
+@interface TikzWindowController : NSWindowController {
+ GraphicsView *graphicsView;
+ TikzSourceController *tikzSourceController;
+ TikzDocument *document;
+}
+
+@property IBOutlet GraphicsView *graphicsView;
+@property IBOutlet TikzSourceController *tikzSourceController;
+
+- (id)initWithDocument:(TikzDocument*)doc;
+
+// pass these straight to the tikz source controller
+- (void)parseTikz:(id)sender;
+- (void)revertTikz:(id)sender;
+- (void)zoomIn:(id)sender;
+- (void)zoomOut:(id)sender;
+- (void)zoomToActualSize:(id)sender;
+
+@end
diff --git a/tikzit/src/osx/TikzWindowController.m b/tikzit/src/osx/TikzWindowController.m
new file mode 100644
index 0000000..2e672d2
--- /dev/null
+++ b/tikzit/src/osx/TikzWindowController.m
@@ -0,0 +1,66 @@
+//
+// TikzWindowController.m
+// TikZiT
+//
+// Created by Aleks Kissinger on 26/01/2011.
+// Copyright 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import "TikzWindowController.h"
+#import "TikzDocument.h"
+#import "GraphicsView.h"
+#import "TikzSourceController.h"
+
+@implementation TikzWindowController
+
+@synthesize graphicsView, tikzSourceController;
+
+- (id)initWithDocument:(TikzDocument*)doc {
+ [super initWithWindowNibName:@"TikzDocument"];
+ document = doc;
+ return self;
+}
+
+- (void)awakeFromNib {
+ if ([document tikz] != nil) {
+ [graphicsView setEnabled:NO];
+ [tikzSourceController setTikz:[document tikz]];
+ [tikzSourceController parseTikz:self];
+ }
+
+ [graphicsView setDocumentUndoManager:[document undoManager]];
+ [tikzSourceController setDocumentUndoManager:[document undoManager]];
+}
+
+- (void)parseTikz:(id)sender {
+ [tikzSourceController parseTikz:sender];
+}
+
+- (void)revertTikz:(id)sender {
+ [tikzSourceController revertTikz:sender];
+}
+
+- (void)previewTikz:(id)sender {
+ PreviewController *pc = [PreviewController defaultPreviewController];
+ if (![[pc window] isVisible]) [pc showWindow:sender];
+ [pc buildTikz:[tikzSourceController tikz]];
+}
+
+- (void)zoomIn:(id)sender {
+ float scale = [[graphicsView transformer] scale] * 1.25f;
+ [[graphicsView transformer] setScale:scale];
+ [graphicsView refreshLayers];
+}
+
+- (void)zoomOut:(id)sender {
+ float scale = [[graphicsView transformer] scale] * 0.8f;
+ [[graphicsView transformer] setScale:scale];
+ [graphicsView refreshLayers];
+}
+
+- (void)zoomToActualSize:(id)sender {
+ [[graphicsView transformer] setScale:50.0f];
+ [graphicsView refreshLayers];
+}
+
+@end
diff --git a/tikzit/src/osx/ToolPaletteController.h b/tikzit/src/osx/ToolPaletteController.h
new file mode 100644
index 0000000..e45c08d
--- /dev/null
+++ b/tikzit/src/osx/ToolPaletteController.h
@@ -0,0 +1,42 @@
+//
+// ToolPaletteController.h
+// TikZiT
+//
+// Copyright 2010 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+#import <Cocoa/Cocoa.h>
+
+typedef enum {
+ TikzToolSelect,
+ TikzToolNode,
+ TikzToolEdge,
+ TikzToolCrop
+} TikzTool;
+
+@interface ToolPaletteController : NSObject {
+ NSPanel *toolPalette;
+ NSMatrix *toolMatrix;
+}
+
+@property TikzTool selectedTool;
+@property IBOutlet NSPanel *toolPalette;
+@property IBOutlet NSMatrix *toolMatrix;
+
+
+@end
diff --git a/tikzit/src/osx/ToolPaletteController.m b/tikzit/src/osx/ToolPaletteController.m
new file mode 100644
index 0000000..000287d
--- /dev/null
+++ b/tikzit/src/osx/ToolPaletteController.m
@@ -0,0 +1,58 @@
+//
+// ToolPaletteController.m
+// TikZiT
+//
+// Copyright 2010 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#import "ToolPaletteController.h"
+
+
+@implementation ToolPaletteController
+
+@synthesize toolPalette, toolMatrix;
+
+- (TikzTool)selectedTool {
+ switch (toolMatrix.selectedRow) {
+ case 0: return TikzToolSelect;
+ case 1: return TikzToolNode;
+ case 2: return TikzToolEdge;
+ case 3: return TikzToolCrop;
+ }
+ return TikzToolSelect;
+}
+
+- (void)setSelectedTool:(TikzTool)tool {
+ switch (tool) {
+ case TikzToolSelect:
+ [toolMatrix selectCellAtRow:0 column:0];
+ break;
+ case TikzToolNode:
+ [toolMatrix selectCellAtRow:1 column:0];
+ break;
+ case TikzToolEdge:
+ [toolMatrix selectCellAtRow:2 column:0];
+ break;
+ case TikzToolCrop:
+ [toolMatrix selectCellAtRow:3 column:0];
+ break;
+ }
+}
+
+@end
diff --git a/tikzit/src/osx/main.m b/tikzit/src/osx/main.m
new file mode 100644
index 0000000..e6b4499
--- /dev/null
+++ b/tikzit/src/osx/main.m
@@ -0,0 +1,26 @@
+//
+// main.m
+// TikZiT
+//
+// Copyright 2010 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+
+#import <Cocoa/Cocoa.h>
+
+int main(int argc, char *argv[])
+{
+ return NSApplicationMain(argc, (const char **) argv);
+}
diff --git a/tikzit/src/osx/test/main.m b/tikzit/src/osx/test/main.m
new file mode 100644
index 0000000..ad0c1f7
--- /dev/null
+++ b/tikzit/src/osx/test/main.m
@@ -0,0 +1,56 @@
+//
+// main.m
+// TikZiT
+//
+// Copyright 2010 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#import "test/test.h"
+#include <string.h>
+
+void testCommon();
+void testOSX();
+
+int main(int argc, char **argv) {
+ if (argc == 2 && strcmp(argv[1], "--disable-color")==0) {
+ setColorEnabled(NO);
+ } else {
+ setColorEnabled(YES);
+ }
+
+ NSBundle *bund = [NSBundle bundleWithPath:@"TikZiT.app"];
+
+
+ PUTS(@"");
+ PUTS(@"**********************************************");
+ PUTS(@"TikZiT TESTS, OS X BUNDLE VERSION %@",
+ [bund objectForInfoDictionaryKey:@"CFBundleVersion"]);
+ PUTS(@"**********************************************");
+ PUTS(@"");
+
+ startTests();
+ testCommon();
+ testOSX();
+
+ PUTS(@"");
+ PUTS(@"**********************************************");
+ endTests();
+ PUTS(@"**********************************************");
+ PUTS(@"");
+} \ No newline at end of file
diff --git a/tikzit/src/osx/test/osx.m b/tikzit/src/osx/test/osx.m
new file mode 100644
index 0000000..f9565ab
--- /dev/null
+++ b/tikzit/src/osx/test/osx.m
@@ -0,0 +1,64 @@
+//
+// osx.m
+// TikZiT
+//
+// Copyright 2010 Aleks Kissinger. All rights reserved.
+//
+//
+// This file is part of TikZiT.
+//
+// TikZiT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// TikZiT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with TikZiT. If not, see <http://www.gnu.org/licenses/>.
+//
+#import "test/test.h"
+
+#import <Cocoa/Cocoa.h>
+
+void testOSX() {
+// char template[] = "/tmp/tikzit_test_tmp_XXXXXXX";
+// char *dir = mkdtemp(template);
+// NSString *tempDir = [NSString stringWithUTF8String:dir];
+//
+// NSString *testLatex =
+// @"\\documentclass{article}\n"
+// @"\\begin{document}\n"
+// @"test document\n"
+// @"\\end{document}\n";
+//
+// NSString *texFile = [NSString stringWithFormat:@"%@/test.tex", tempDir];
+// NSString *pdfFile = [NSString stringWithFormat:@"%@/test.pdf", tempDir];
+//
+// [testLatex writeToFile:texFile atomically:NO encoding:NSUTF8StringEncoding error:NULL];
+//
+// NSTask *task = [[NSTask alloc] init];
+// [task setLaunchPath:@"/bin/bash"];
+// NSPipe *inpt = [NSPipe pipe];
+// NSPipe *outpt = [NSPipe pipe];
+// [task setStandardInput:inpt];
+// [task setStandardOutput:outpt];
+//
+// [task launch];
+//
+// NSFileHandle *wr = [inpt fileHandleForWriting];
+// NSString *cmd =
+// [NSString stringWithFormat:
+// @"if [ -e ~/.profile ]; then source ~/.profile; fi"
+// @"if [ -e ~/.profile ]; then source ~/.profile; fi";
+// [wr writeData:[cmd dataUsingEncoding:NSUTF8StringEncoding]];
+// [wr closeFile];
+//
+// NSFileHandle *rd = [outpt fileHandleForReading];
+// NSString *res = [[NSString alloc] initWithData:[rd readDataToEndOfFile]
+// encoding:NSUTF8StringEncoding];
+// NSLog(@"got:\n %@", res);
+}