// // TikzDocument.h // TikZiT // // Copyright 2010 Chris Heunen // Copyright 2010 Alex Merry // // 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" @interface TikzDocument (Private) - (void) styleRenamed:(NSNotification*)n; - (void) setPath:(NSString*)path; - (void) setGraph:(Graph*)g; - (void) registerUndoForChange:(GraphChange*)change; - (void) registerUndoGroupForChange:(GraphChange*)change withName:(NSString*)name; - (void) undoGraphChange:(GraphChange*)change; - (void) completedGraphChange:(GraphChange*)change withName:(NSString*)name; - (void) attachStylesToGraph:(Graph*)g; - (void) regenerateTikz; @end @implementation TikzDocument + (TikzDocument*) documentWithStyleManager:(StyleManager*)manager { return [[[TikzDocument alloc] initWithStyleManager:manager] autorelease]; } + (TikzDocument*) documentWithGraph:(Graph*)g styleManager:(StyleManager*)manager { return [[[TikzDocument alloc] initWithGraph:g styleManager:manager] autorelease]; } + (TikzDocument*) documentWithTikz:(NSString*)t styleManager:(StyleManager*)manager error:(NSError**)error { return [[[TikzDocument alloc] initWithTikz:t styleManager:manager error:error] autorelease]; } + (TikzDocument*) documentFromFile:(NSString*)pth styleManager:(StyleManager*)manager error:(NSError**)error { return [[[TikzDocument alloc] initFromFile:pth styleManager:manager error:error] autorelease]; } - (id) initWithStyleManager:(StyleManager*)manager { self = [self initWithGraph:[Graph graph] styleManager:manager]; return self; } - (id) initWithGraph:(Graph*)g styleManager:(StyleManager*)manager { self = [super init]; if (self) { graph = nil; styleManager = [manager retain]; pickSupport = [[PickSupport alloc] init]; undoManager = [[NSUndoManager alloc] init]; [undoManager setGroupsByEvent:NO]; tikz = nil; path = nil; nodesetBeingModified = nil; nodesetBeingModifiedOldCopy = nil; nodeBeingModified = nil; nodeBeingModifiedOldCopy = nil; edgeBeingModified = nil; edgeBeingModifiedOldCopy = nil; [undoManager disableUndoRegistration]; [self setGraph:g]; [undoManager enableUndoRegistration]; hasChanges = NO; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(styleRenamed:) name:@"NodeStyleRenamed" object:styleManager]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(styleRenamed:) name:@"EdgeStyleRenamed" object:styleManager]; } return self; } - (id) initWithTikz:(NSString*)t styleManager:(StyleManager*)manager error:(NSError**)error { self = [self initWithStyleManager:manager]; if (self) { [undoManager disableUndoRegistration]; BOOL success = [self updateTikz:t error:error]; if (!success) { [self release]; return nil; } [undoManager enableUndoRegistration]; hasChanges = NO; } return self; } - (id) initFromFile:(NSString*)pth styleManager:(StyleManager*)manager error:(NSError**)error { NSStringEncoding enc; // we can't pass in NULL here... NSString *t = [NSString stringWithContentsOfFile:pth usedEncoding:&enc error:error]; if (t == nil) { [self release]; return nil; } self = [self initWithTikz:t styleManager:manager error:error]; if (self) { [self setPath:pth]; } return self; } - (void) dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; [styleManager release]; [graph release]; [pickSupport release]; [undoManager release]; [tikz release]; [path release]; [nodesetBeingModified release]; [nodesetBeingModifiedOldCopy release]; [nodeBeingModified release]; [nodeBeingModifiedOldCopy release]; [edgeBeingModified release]; [edgeBeingModifiedOldCopy release]; [oldGraphData release]; [super dealloc]; } @synthesize graph, pickSupport, path; - (NSString*) name { if (path) { return [[NSFileManager defaultManager] displayNameAtPath: path]; } else { return @"Untitled"; } } - (NSString*) suggestedFileName { if (path) { return [path lastPathComponent]; } else { return @"untitled.tikz"; } } - (BOOL) hasUnsavedChanges { return hasChanges; } - (StyleManager*) styleManager { return styleManager; } - (void) setStyleManager:(StyleManager*)manager { StyleManager *oldManager = styleManager; [[NSNotificationCenter defaultCenter] removeObserver:self name:nil object:oldManager]; styleManager = [manager retain]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(styleRenamed:) name:@"NodeStyleRenamed" object:styleManager]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(styleRenamed:) name:@"EdgeStyleRenamed" object:styleManager]; [self attachStylesToGraph:graph]; [oldManager release]; } - (void) postGraphReplaced { [[NSNotificationCenter defaultCenter] postNotificationName:@"GraphReplaced" object:self]; } - (void) postGraphChange:(GraphChange*)change { NSDictionary *info = [NSDictionary dictionaryWithObject:change forKey:@"change"]; [[NSNotificationCenter defaultCenter] postNotificationName:@"GraphChanged" object:self userInfo:info]; } - (void) postIncompleteGraphChange:(GraphChange*)change { NSDictionary *info = [NSDictionary dictionaryWithObject:change forKey:@"change"]; [[NSNotificationCenter defaultCenter] postNotificationName:@"GraphBeingChanged" object:self userInfo:info]; } - (void) postCancelledGraphChange:(GraphChange*)change { NSDictionary *info = [NSDictionary dictionaryWithObject:change forKey:@"change"]; [[NSNotificationCenter defaultCenter] postNotificationName:@"GraphChangeCancelled" object:self userInfo:info]; } - (void) postTikzChanged { [[NSNotificationCenter defaultCenter] postNotificationName:@"TikzChanged" object:self]; } - (void) postUndoStackChanged { [[NSNotificationCenter defaultCenter] postNotificationName:@"UndoStackChanged" object:self]; } - (NSString*) tikz { return tikz; } - (BOOL) updateTikz:(NSString*)t error:(NSError**)error { if (t == nil) { t = [NSString string]; } if (t == tikz || [t isEqual:tikz]) { return YES; } Graph *g = [Graph graphFromTikz:t error:error]; if (g) { // updateTikz actually generates a graph from the tikz, // and generates the final tikz from that [self startUndoGroup]; [self setGraph:g]; [self nameAndEndUndoGroup:@"Update tikz"]; return YES; } return NO; } - (Graph*) cutSelection { Graph *selection = [self copySelection]; [self startUndoGroup]; [self removeSelected]; [self nameAndEndUndoGroup:@"Cut"]; return selection; } - (Graph*) copySelection { return [[graph copyOfSubgraphWithNodes:[pickSupport selectedNodes]] autorelease]; } - (void) paste:(Graph*)g { if (g == nil || [[g nodes] count] == 0) { // nothing to paste return; } // place to the right of the existing graph NSRect bounds = [graph bounds]; NSRect gBounds = [g bounds]; float dx = NSMaxX (bounds) - gBounds.origin.x + 0.5f; [g shiftNodes:[g nodes] byPoint:NSMakePoint (dx, 0)]; GraphChange *change = [graph insertGraph:g]; [self completedGraphChange:change withName:@"Paste"]; // select everything from the clipboard [pickSupport deselectAllEdges]; [pickSupport selectAllNodes:[NSSet setWithArray:[g nodes]] replacingSelection:YES]; } - (void) pasteFromTikz:(NSString*)t { Graph *clipboard = [Graph graphFromTikz:t]; if (clipboard) { [self attachStylesToGraph:clipboard]; [self paste:clipboard]; } } - (BOOL) isNodeSelected:(Node*)node { return [pickSupport isNodeSelected:node]; } - (BOOL) isEdgeSelected:(Edge*)edge { return [pickSupport isEdgeSelected:edge]; } - (NSEnumerator*) nodeEnumerator { return [[graph nodes] objectEnumerator]; } - (NSEnumerator*) edgeEnumerator { return [[graph edges] objectEnumerator]; } - (BOOL) canUndo { return [undoManager canUndo]; } - (void) undo { [undoManager undo]; [self postUndoStackChanged]; } - (BOOL) canRedo { return [undoManager canRedo]; } - (void) redo { [undoManager redo]; [self postUndoStackChanged]; } - (NSString*) undoName { return [undoManager undoActionName]; } - (NSString*) redoName { return [undoManager redoActionName]; } - (void) startUndoGroup { [undoManager beginUndoGrouping]; } - (void) nameAndEndUndoGroup:(NSString*)nm { [undoManager setActionName:nm]; [undoManager endUndoGrouping]; [self postUndoStackChanged]; } - (void) endUndoGroup { [undoManager endUndoGrouping]; [self postUndoStackChanged]; } - (void) startModifyNode:(Node*)node { if (nodeBeingModified != nil) { [NSException raise:@"NSInternalInconsistencyException" format:@"Already modifying a node"]; } nodeBeingModified = [node retain]; nodeBeingModifiedOldCopy = [node copy]; } - (void) modifyNodeCheckPoint { [self regenerateTikz]; GraphChange *change = [GraphChange propertyChangeOfNode:nodeBeingModified fromOld:nodeBeingModifiedOldCopy toNew:[[nodeBeingModified copy] autorelease]]; [self postIncompleteGraphChange:change]; } - (void) _finishModifySequence:(GraphChange*)change withName:(NSString*)chName cancelled:(BOOL)cancelled { if (cancelled) { change = [change invert]; [graph applyGraphChange:change]; [self regenerateTikz]; [self postCancelledGraphChange:change]; } else { [self registerUndoGroupForChange:change withName:chName]; [self regenerateTikz]; [self postGraphChange:change]; } } - (void) _finishModifyNodeCancelled:(BOOL)cancelled { if (nodeBeingModified == nil) { [NSException raise:@"NSInternalInconsistencyException" format:@"Not modifying a node"]; } GraphChange *change = [GraphChange propertyChangeOfNode:nodeBeingModified fromOld:nodeBeingModifiedOldCopy toNew:[[nodeBeingModified copy] autorelease]]; [self _finishModifySequence:change withName:@"Modify node" cancelled:cancelled]; [nodeBeingModified release]; nodeBeingModified = nil; [nodeBeingModifiedOldCopy release]; nodeBeingModifiedOldCopy = nil; } - (void) endModifyNode { [self _finishModifyNodeCancelled:NO]; } - (void) cancelModifyNode { [self _finishModifyNodeCancelled:YES]; } - (void) startModifyNodes:(NSSet*)nodes { if (nodesetBeingModified != nil) { [NSException raise:@"NSInternalInconsistencyException" format:@"Already modifying a node set"]; } nodesetBeingModified = [nodes copy]; nodesetBeingModifiedOldCopy = [[Graph nodeTableForNodes:nodes] retain]; } - (void) modifyNodesCheckPoint { [self regenerateTikz]; GraphChange *change = [GraphChange propertyChangeOfNodesFromOldCopies:nodesetBeingModifiedOldCopy toNewCopies:[Graph nodeTableForNodes:nodesetBeingModified]]; [self postIncompleteGraphChange:change]; } - (void) _finishModifyNodes:(BOOL)cancelled { if (nodesetBeingModified == nil) { [NSException raise:@"NSInternalInconsistencyException" format:@"Not modifying a node set"]; } GraphChange *change = [GraphChange propertyChangeOfNodesFromOldCopies:nodesetBeingModifiedOldCopy toNewCopies:[Graph nodeTableForNodes:nodesetBeingModified]]; [self _finishModifySequence:change withName:@"Modify nodes" cancelled:cancelled]; [nodesetBeingModified release]; nodesetBeingModified = nil; [nodesetBeingModifiedOldCopy release]; nodesetBeingModifiedOldCopy = nil; } - (void) endModifyNodes { [self _finishModifyNodes:NO]; } - (void) cancelModifyNodes { [self _finishModifyNodes:YES]; } - (void) startShiftNodes:(NSSet*)nodes { if (nodesetBeingModified != nil) { [NSException raise:@"NSInternalInconsistencyException" format:@"Already modifying a node set"]; } nodesetBeingModified = [nodes copy]; currentNodeShift = NSZeroPoint; } - (void) shiftNodesUpdate:(NSPoint)currentShift { if (nodesetBeingModified == nil) { [NSException raise:@"NSInternalInconsistencyException" format:@"Not modifying a node set"]; } currentNodeShift = currentShift; [self regenerateTikz]; GraphChange *change = [GraphChange shiftNodes:nodesetBeingModified byPoint:currentNodeShift]; [self postIncompleteGraphChange:change]; } - (void) _finishShiftNodesCancelled:(BOOL)cancelled { if (nodesetBeingModified == nil) { [NSException raise:@"NSInternalInconsistencyException" format:@"Not modifying a node set"]; } if (!NSEqualPoints (currentNodeShift, NSZeroPoint)) { GraphChange *change = [GraphChange shiftNodes:nodesetBeingModified byPoint:currentNodeShift]; [self _finishModifySequence:change withName:@"Move nodes" cancelled:cancelled]; } [nodesetBeingModified release]; nodesetBeingModified = nil; } - (void) endShiftNodes { [self _finishShiftNodesCancelled:NO]; } - (void) cancelShiftNodes { [self _finishShiftNodesCancelled:YES]; } - (void) startModifyEdge:(Edge*)edge { if (edgeBeingModified != nil) { [NSException raise:@"NSInternalInconsistencyException" format:@"Already modifying an edge"]; } edgeBeingModified = [edge retain]; edgeBeingModifiedOldCopy = [edge copy]; } - (void) modifyEdgeCheckPoint { [self regenerateTikz]; GraphChange *change = [GraphChange propertyChangeOfEdge:edgeBeingModified fromOld:edgeBeingModifiedOldCopy toNew:[[edgeBeingModified copy] autorelease]]; [self postIncompleteGraphChange:change]; } - (void) _finishModifyEdgeCancelled:(BOOL)cancelled { if (edgeBeingModified == nil) { [NSException raise:@"NSInternalInconsistencyException" format:@"Not modifying an edge"]; } GraphChange *change = [GraphChange propertyChangeOfEdge:edgeBeingModified fromOld:edgeBeingModifiedOldCopy toNew:[[edgeBeingModified copy] autorelease]]; [self _finishModifySequence:change withName:@"Modify edge" cancelled:cancelled]; [edgeBeingModified release]; edgeBeingModified = nil; [edgeBeingModifiedOldCopy release]; edgeBeingModifiedOldCopy = nil; } - (void) endModifyEdge { [self _finishModifyEdgeCancelled:NO]; } - (void) cancelModifyEdge { [self _finishModifyEdgeCancelled:YES]; } - (void) startModifyEdges:(NSSet*)edges { if (edgesetBeingModified != nil) { [NSException raise:@"NSInternalInconsistencyException" format:@"Already modifying an edge set"]; } edgesetBeingModified = [edges copy]; edgesetBeingModifiedOldCopy = [[Graph edgeTableForEdges:edges] retain]; } - (void) modifyEdgesCheckPoint { [self regenerateTikz]; GraphChange *change = [GraphChange propertyChangeOfEdgesFromOldCopies:edgesetBeingModifiedOldCopy toNewCopies:[Graph edgeTableForEdges:edgesetBeingModified]]; [self postIncompleteGraphChange:change]; } - (void) _finishModifyEdgesCancelled:(BOOL)cancelled { if (edgesetBeingModified == nil) { [NSException raise:@"NSInternalInconsistencyException" format:@"Not modifying an edge"]; } GraphChange *change = [GraphChange propertyChangeOfEdgesFromOldCopies:edgesetBeingModifiedOldCopy toNewCopies:[Graph edgeTableForEdges:edgesetBeingModified]]; [self _finishModifySequence:change withName:@"Modify edges" cancelled:cancelled]; [edgesetBeingModified release]; edgesetBeingModified = nil; [edgesetBeingModifiedOldCopy release]; edgesetBeingModifiedOldCopy = nil; } - (void) endModifyEdges { [self _finishModifyEdgesCancelled:NO]; } - (void) cancelModifyEdges { [self _finishModifyEdgesCancelled:YES]; } - (void) startChangeBoundingBox { oldGraphBounds = [graph boundingBox]; } - (void) changeBoundingBoxCheckPoint { [self regenerateTikz]; GraphChange *change = [GraphChange changeBoundingBoxFrom:oldGraphBounds to:[graph boundingBox]]; [self postIncompleteGraphChange:change]; } - (void) _finishChangeBoundingBoxCancelled:(BOOL)cancelled { GraphChange *change = [GraphChange changeBoundingBoxFrom:oldGraphBounds to:[graph boundingBox]]; [self _finishModifySequence:change withName:@"Set bounding box" cancelled:cancelled]; } - (void) endChangeBoundingBox { [self _finishChangeBoundingBoxCancelled:NO]; } - (void) cancelChangeBoundingBox { [self _finishChangeBoundingBoxCancelled:YES]; } - (void) startChangeGraphProperties { oldGraphData = [[graph data] copy]; } - (void) changeGraphPropertiesCheckPoint { [self regenerateTikz]; GraphChange *change = [GraphChange propertyChangeOfGraphFrom:oldGraphData to:[graph data]]; [self postIncompleteGraphChange:change]; } - (void) _finishChangeGraphPropertiesCancelled:(BOOL)cancelled { GraphChange *change = [GraphChange propertyChangeOfGraphFrom:oldGraphData to:[graph data]]; [self _finishModifySequence:change withName:@"Change graph properties" cancelled:cancelled]; [oldGraphData release]; oldGraphData = nil; } - (void) endChangeGraphProperties { [self _finishChangeGraphPropertiesCancelled:NO]; } - (void) cancelChangeGraphProperties { [self _finishChangeGraphPropertiesCancelled:YES]; } - (void) removeSelected { NSUInteger selEdges = [[pickSupport selectedEdges] count]; NSUInteger selNodes = [[pickSupport selectedNodes] count]; if (selEdges == 0 && selNodes == 0) { return; } NSString *actionName = @"Remove selection"; [self startUndoGroup]; if (selEdges > 0) { GraphChange *change = [graph removeEdges:[pickSupport selectedEdges]]; [self registerUndoForChange:change]; [pickSupport deselectAllEdges]; [self postGraphChange:change]; } else { actionName = (selNodes == 1 ? @"Remove node" : @"Remove nodes"); } if (selNodes > 0) { GraphChange *change = [graph removeNodes:[pickSupport selectedNodes]]; [self registerUndoForChange:change]; [pickSupport deselectAllNodes]; [self postGraphChange:change]; } else { actionName = (selEdges == 1 ? @"Remove edge" : @"Remove edges"); } [self nameAndEndUndoGroup:actionName]; [self regenerateTikz]; } - (void) addNode:(Node*)node { GraphChange *change = [graph addNode:node]; [self completedGraphChange:change withName:@"Add node"]; } - (void) removeNode:(Node*)node { [pickSupport deselectNode:node]; GraphChange *change = [graph removeNode:node]; [self completedGraphChange:change withName:@"Remove node"]; } - (void) addEdge:(Edge*)edge { GraphChange *change = [graph addEdge:edge]; [self completedGraphChange:change withName:@"Add edge"]; } - (void) removeEdge:(Edge*)edge { [pickSupport deselectEdge:edge]; GraphChange *change = [graph removeEdge:edge]; [self completedGraphChange:change withName:@"Remove edge"]; } - (void) shiftSelectedNodesByPoint:(NSPoint)offset { if ([[pickSupport selectedNodes] count] > 0) { GraphChange *change = [graph shiftNodes:[pickSupport selectedNodes] byPoint:offset]; [self completedGraphChange:change withName:@"Move nodes"]; } } - (void) insertGraph:(Graph*)g { GraphChange *change = [graph insertGraph:g]; [self completedGraphChange:change withName:@"Insert graph"]; } - (void) flipSelectedNodesHorizontally { if ([[pickSupport selectedNodes] count] > 0) { GraphChange *change = [graph flipHorizontalNodes:[pickSupport selectedNodes]]; [self completedGraphChange:change withName:@"Flip nodes horizontally"]; } } - (void) flipSelectedNodesVertically { if ([[pickSupport selectedNodes] count] > 0) { GraphChange *change = [graph flipVerticalNodes:[pickSupport selectedNodes]]; [self completedGraphChange:change withName:@"Flip nodes vertically"]; } } - (void) reverseSelectedEdges { if ([[pickSupport selectedEdges] count] > 0) { GraphChange *change = [graph reverseEdges:[pickSupport selectedEdges]]; [self completedGraphChange:change withName:@"Reverse edges"]; } } - (void) bringSelectionForward { BOOL hasNodeSelection = [[pickSupport selectedNodes] count] > 0; BOOL hasEdgeSelection = [[pickSupport selectedEdges] count] > 0; if (!hasNodeSelection && !hasEdgeSelection) return; [self startUndoGroup]; GraphChange *nodeChange; GraphChange *edgeChange; if (hasNodeSelection) { nodeChange = [graph bringNodesForward:[pickSupport selectedNodes]]; [self registerUndoForChange:nodeChange]; } if (hasEdgeSelection) { edgeChange = [graph bringEdgesForward:[pickSupport selectedEdges]]; [self registerUndoForChange:edgeChange]; } [self nameAndEndUndoGroup:@"Bring forward"]; [self regenerateTikz]; if (hasNodeSelection) [self postGraphChange:nodeChange]; if (hasEdgeSelection) [self postGraphChange:edgeChange]; } - (void) bringSelectionToFront { BOOL hasNodeSelection = [[pickSupport selectedNodes] count] > 0; BOOL hasEdgeSelection = [[pickSupport selectedEdges] count] > 0; if (!hasNodeSelection && !hasEdgeSelection) return; [self startUndoGroup]; GraphChange *nodeChange; GraphChange *edgeChange; if (hasNodeSelection) { nodeChange = [graph bringNodesToFront:[pickSupport selectedNodes]]; [self registerUndoForChange:nodeChange]; } if (hasEdgeSelection) { edgeChange = [graph bringEdgesToFront:[pickSupport selectedEdges]]; [self registerUndoForChange:edgeChange]; } [self nameAndEndUndoGroup:@"Bring to front"]; [self regenerateTikz]; if (hasNodeSelection) [self postGraphChange:nodeChange]; if (hasEdgeSelection) [self postGraphChange:edgeChange]; } - (void) sendSelectionBackward { BOOL hasNodeSelection = [[pickSupport selectedNodes] count] > 0; BOOL hasEdgeSelection = [[pickSupport selectedEdges] count] > 0; if (!hasNodeSelection && !hasEdgeSelection) return; [self startUndoGroup]; GraphChange *nodeChange; GraphChange *edgeChange; if (hasNodeSelection) { nodeChange = [graph sendNodesBackward:[pickSupport selectedNodes]]; [self registerUndoForChange:nodeChange]; } if (hasEdgeSelection) { edgeChange = [graph sendNodesBackward:[pickSupport selectedEdges]]; [self registerUndoForChange:edgeChange]; } [self nameAndEndUndoGroup:@"Send backward"]; [self regenerateTikz]; if (hasNodeSelection) [self postGraphChange:nodeChange]; if (hasEdgeSelection) [self postGraphChange:edgeChange]; } - (void) sendSelectionToBack { BOOL hasNodeSelection = [[pickSupport selectedNodes] count] > 0; BOOL hasEdgeSelection = [[pickSupport selectedEdges] count] > 0; if (!hasNodeSelection && !hasEdgeSelection) return; [self startUndoGroup]; GraphChange *nodeChange; GraphChange *edgeChange; if (hasNodeSelection) { nodeChange = [graph sendNodesToBack:[pickSupport selectedNodes]]; [self registerUndoForChange:nodeChange]; } if (hasEdgeSelection) { edgeChange = [graph sendNodesToBack:[pickSupport selectedEdges]]; [self registerUndoForChange:edgeChange]; } [self nameAndEndUndoGroup:@"Send to back"]; [self regenerateTikz]; if (hasNodeSelection) [self postGraphChange:nodeChange]; if (hasEdgeSelection) [self postGraphChange:edgeChange]; } - (BOOL) saveCopyToPath: (NSString*)p error: (NSError**)error { if (!p) { [NSException raise:@"No document path" format:@"No path given"]; } // we use glib for writing the file, because GNUStep sucks in this regard // (older versions don't have -[NSString writeToFile:atomically:encoding:error:]) GError *gerror = NULL; gchar *filename = [p glibFilename]; BOOL success = g_file_set_contents (filename, [tikz UTF8String], -1, &gerror) ? YES : NO; if (gerror) { GErrorToNSError (gerror, error); g_error_free (gerror); } g_free (filename); return success; } - (BOOL) saveToPath: (NSString*)p error: (NSError**)error { BOOL success = [self saveCopyToPath:p error:error]; if (success) { [self setPath:p]; hasChanges = NO; } return success; } - (BOOL) save: (NSError**)error { if (!path) { [NSException raise:@"No document path" format:@"Tried to save a document when there was no path"]; } return [self saveToPath:path error:error]; } @end @implementation TikzDocument (Private) - (void) styleRenamed:(NSNotification*)n { [self regenerateTikz]; } - (void) setPath:(NSString*)p { [p retain]; [path release]; path = p; } - (void) setGraph:(Graph*)g { if (g == nil) { g = [Graph graph]; } if (g == graph) { return; } [pickSupport deselectAllNodes]; [pickSupport deselectAllEdges]; [self startUndoGroup]; [undoManager registerUndoWithTarget:self selector:@selector(setGraph:) object:graph]; [g retain]; [graph release]; graph = g; [self attachStylesToGraph:graph]; [self regenerateTikz]; [self postGraphReplaced]; [self nameAndEndUndoGroup:@"Replace graph"]; } - (void) registerUndoForChange:(GraphChange*)change { [undoManager registerUndoWithTarget:self selector:@selector(undoGraphChange:) object:change]; } - (void) registerUndoGroupForChange:(GraphChange*)change withName:(NSString*)nm { [self startUndoGroup]; [self registerUndoForChange:change]; [self nameAndEndUndoGroup:nm]; } - (void) undoGraphChange:(GraphChange*)change { GraphChange *inverse = [change invert]; [graph applyGraphChange:inverse]; [self startUndoGroup]; [undoManager registerUndoWithTarget:self selector:@selector(undoGraphChange:) object:inverse]; [self endUndoGroup]; [self regenerateTikz]; [self postGraphChange:change]; } - (void) completedGraphChange:(GraphChange*)change withName:(NSString*)name { if (change == nil) { NSLog(@"No graph change given for change %@", name); return; } [self registerUndoGroupForChange:change withName:name]; [self regenerateTikz]; [self postGraphChange:change]; } - (void) attachStylesToGraph:(Graph*)g { for (Node *n in [g nodes]) { [n attachStyleFromTable:[styleManager nodeStyles]]; } for (Edge *e in [g edges]) { [e attachStyleFromTable:[styleManager edgeStyles]]; } } - (void) regenerateTikz { [tikz release]; tikz = [[graph tikz] retain]; hasChanges = YES; [self postTikzChanged]; } @end // vim:ft=objc:sts=4:sw=4:et