From a8a8dfb90d6a51ae369c042c95162f45754c7c4b Mon Sep 17 00:00:00 2001 From: randomguy3 Date: Mon, 9 Jan 2012 11:00:50 +0000 Subject: Move tikzit into "trunk" directory git-svn-id: https://tikzit.svn.sourceforge.net/svnroot/tikzit/trunk@365 7c02a99a-9b00-45e3-bf44-6f3dd7fddb64 --- tikzit/src/osx/AppDelegate.h | 53 ++ tikzit/src/osx/AppDelegate.m | 105 +++ tikzit/src/osx/CALayer+DrawLabel.h | 21 + tikzit/src/osx/CALayer+DrawLabel.m | 84 ++ tikzit/src/osx/CoreGraphicsRenderContext.h | 44 + tikzit/src/osx/CoreGraphicsRenderContext.m | 234 +++++ tikzit/src/osx/EdgeControlLayer.h | 44 + tikzit/src/osx/EdgeControlLayer.m | 150 ++++ tikzit/src/osx/EdgeStyle+Coder.h | 30 + tikzit/src/osx/EdgeStyle+Coder.m | 50 ++ tikzit/src/osx/Graph+Coder.h | 17 + tikzit/src/osx/Graph+Coder.m | 25 + tikzit/src/osx/GraphicsView.h | 128 +++ tikzit/src/osx/GraphicsView.m | 1142 +++++++++++++++++++++++++ tikzit/src/osx/Grid.h | 48 ++ tikzit/src/osx/Grid.m | 152 ++++ tikzit/src/osx/MultiCombo.h | 18 + tikzit/src/osx/MultiCombo.m | 38 + tikzit/src/osx/MultiField.h | 18 + tikzit/src/osx/MultiField.m | 53 ++ tikzit/src/osx/NilToEmptyStringTransformer.h | 28 + tikzit/src/osx/NilToEmptyStringTransformer.m | 53 ++ tikzit/src/osx/NodeLayer.h | 62 ++ tikzit/src/osx/NodeLayer.m | 239 ++++++ tikzit/src/osx/NodeSelectionLayer.h | 45 + tikzit/src/osx/NodeSelectionLayer.m | 93 ++ tikzit/src/osx/NodeStyle+Coder.h | 36 + tikzit/src/osx/NodeStyle+Coder.m | 91 ++ tikzit/src/osx/PreambleController.h | 57 ++ tikzit/src/osx/PreambleController.m | 168 ++++ tikzit/src/osx/Preambles+Coder.h | 32 + tikzit/src/osx/Preambles+Coder.m | 41 + tikzit/src/osx/PreviewController.h | 51 ++ tikzit/src/osx/PreviewController.m | 137 +++ tikzit/src/osx/PropertyInspectorController.h | 73 ++ tikzit/src/osx/PropertyInspectorController.m | 272 ++++++ tikzit/src/osx/SelectBoxLayer.h | 22 + tikzit/src/osx/SelectBoxLayer.m | 48 ++ tikzit/src/osx/SelectableCollectionViewItem.h | 33 + tikzit/src/osx/SelectableCollectionViewItem.m | 54 ++ tikzit/src/osx/SelectableNodeView.h | 38 + tikzit/src/osx/SelectableNodeView.m | 96 +++ tikzit/src/osx/StylePaletteController.h | 80 ++ tikzit/src/osx/StylePaletteController.m | 247 ++++++ tikzit/src/osx/TikzDocument.h | 37 + tikzit/src/osx/TikzDocument.m | 84 ++ tikzit/src/osx/TikzParser.h | 66 ++ tikzit/src/osx/TikzParser.m | 574 +++++++++++++ tikzit/src/osx/TikzSourceController.h | 66 ++ tikzit/src/osx/TikzSourceController.m | 196 +++++ tikzit/src/osx/TikzWindowController.h | 31 + tikzit/src/osx/TikzWindowController.m | 66 ++ tikzit/src/osx/ToolPaletteController.h | 42 + tikzit/src/osx/ToolPaletteController.m | 58 ++ tikzit/src/osx/main.m | 26 + tikzit/src/osx/test/main.m | 56 ++ tikzit/src/osx/test/osx.m | 64 ++ 57 files changed, 5916 insertions(+) create mode 100644 tikzit/src/osx/AppDelegate.h create mode 100644 tikzit/src/osx/AppDelegate.m create mode 100644 tikzit/src/osx/CALayer+DrawLabel.h create mode 100644 tikzit/src/osx/CALayer+DrawLabel.m create mode 100644 tikzit/src/osx/CoreGraphicsRenderContext.h create mode 100644 tikzit/src/osx/CoreGraphicsRenderContext.m create mode 100644 tikzit/src/osx/EdgeControlLayer.h create mode 100644 tikzit/src/osx/EdgeControlLayer.m create mode 100644 tikzit/src/osx/EdgeStyle+Coder.h create mode 100644 tikzit/src/osx/EdgeStyle+Coder.m create mode 100644 tikzit/src/osx/Graph+Coder.h create mode 100644 tikzit/src/osx/Graph+Coder.m create mode 100644 tikzit/src/osx/GraphicsView.h create mode 100644 tikzit/src/osx/GraphicsView.m create mode 100644 tikzit/src/osx/Grid.h create mode 100644 tikzit/src/osx/Grid.m create mode 100644 tikzit/src/osx/MultiCombo.h create mode 100644 tikzit/src/osx/MultiCombo.m create mode 100644 tikzit/src/osx/MultiField.h create mode 100644 tikzit/src/osx/MultiField.m create mode 100644 tikzit/src/osx/NilToEmptyStringTransformer.h create mode 100644 tikzit/src/osx/NilToEmptyStringTransformer.m create mode 100644 tikzit/src/osx/NodeLayer.h create mode 100644 tikzit/src/osx/NodeLayer.m create mode 100644 tikzit/src/osx/NodeSelectionLayer.h create mode 100644 tikzit/src/osx/NodeSelectionLayer.m create mode 100644 tikzit/src/osx/NodeStyle+Coder.h create mode 100644 tikzit/src/osx/NodeStyle+Coder.m create mode 100644 tikzit/src/osx/PreambleController.h create mode 100644 tikzit/src/osx/PreambleController.m create mode 100644 tikzit/src/osx/Preambles+Coder.h create mode 100644 tikzit/src/osx/Preambles+Coder.m create mode 100644 tikzit/src/osx/PreviewController.h create mode 100644 tikzit/src/osx/PreviewController.m create mode 100644 tikzit/src/osx/PropertyInspectorController.h create mode 100644 tikzit/src/osx/PropertyInspectorController.m create mode 100644 tikzit/src/osx/SelectBoxLayer.h create mode 100644 tikzit/src/osx/SelectBoxLayer.m create mode 100644 tikzit/src/osx/SelectableCollectionViewItem.h create mode 100644 tikzit/src/osx/SelectableCollectionViewItem.m create mode 100644 tikzit/src/osx/SelectableNodeView.h create mode 100644 tikzit/src/osx/SelectableNodeView.m create mode 100644 tikzit/src/osx/StylePaletteController.h create mode 100644 tikzit/src/osx/StylePaletteController.m create mode 100644 tikzit/src/osx/TikzDocument.h create mode 100644 tikzit/src/osx/TikzDocument.m create mode 100644 tikzit/src/osx/TikzParser.h create mode 100644 tikzit/src/osx/TikzParser.m create mode 100644 tikzit/src/osx/TikzSourceController.h create mode 100644 tikzit/src/osx/TikzSourceController.m create mode 100644 tikzit/src/osx/TikzWindowController.h create mode 100644 tikzit/src/osx/TikzWindowController.m create mode 100644 tikzit/src/osx/ToolPaletteController.h create mode 100644 tikzit/src/osx/ToolPaletteController.m create mode 100644 tikzit/src/osx/main.m create mode 100644 tikzit/src/osx/test/main.m create mode 100644 tikzit/src/osx/test/osx.m (limited to 'tikzit/src/osx') 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 . +// + +#import +#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 . +// + +#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 +#import + +@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 + * + * 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 . + */ + +#import +#import "RenderContext.h" + +@interface CoreTextLayout: NSObject { + 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 { + 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 + * + * 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 . + */ + +#import +#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) 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 . +// + +#import +#import +#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 . +// + +#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 . +// + +#import +#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 . +// + +#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 +#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 . +// + +#import +#import +#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 . +// + +#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 . +// + +#import +#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 . +// + +#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 + + +@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 + + +@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 . +// + +#import + +@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 . +// + +#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 . +// + +#import +#import +#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 . +// + +#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 . +// + +#import +#import +#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 . +// + +#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 . +// + +#import +#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 . +// + +#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 . +// + +#import +#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 . +// + +#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 . +// + +#import "Preambles.h" +#import + +@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 . +// + +#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 . +// + + +#import + +@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 . +// + +#import "PreviewController.h" +#import "AppDelegate.h" +#import "PreambleController.h" +#import + +@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 +#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 +#import + +@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 . +// + +#import +#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 . +// + +#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 . +// + +#import +#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 . +// + +#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 . +// + +#import +#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 . +// + +#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 . +// + +#import +#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 . +// + +#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 . +// + +#import +#import + +#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 . +// + +#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(@""); +} + +- (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(@""); +} + +- (void)didMatchDrawCommand:(PKAssembly*)a { + [a pop]; + currentEdge = [Edge edge]; + sourceName = nil; + targName = nil; +// NSLog(@""); +} + +- (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(@""); +} + +- (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(@""); + nodeTable = [NSMutableDictionary dictionary]; +} + +- (void)didMatchTikzPicture:(PKAssembly*)a { +// NSLog(@""); +// 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 . +// + +#import + +#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 . +// + +#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 + +@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 . +// +#import + +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 . +// + +#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 + +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 . +// + +#import "test/test.h" +#include + +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 . +// +#import "test/test.h" + +#import + +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); +} -- cgit v1.2.3