Permalink
Browse files

Making toolbar actions undoable.

  • Loading branch information...
1 parent 783fa83 commit 260e1b0a4675293fbcb63e1a00752ba582ed7df6 Jan Wei�� committed Aug 31, 2012
Showing with 176 additions and 8 deletions.
  1. +14 −0 MarkdownLive.xcodeproj/project.pbxproj
  2. +36 −8 MyDocument.m
  3. +24 −0 NSTextView+EditPlainTextWithUndo.h
  4. +102 −0 NSTextView+EditPlainTextWithUndo.m
@@ -16,6 +16,7 @@
11EF12EE142D63050086C77F /* ulist.png in Resources */ = {isa = PBXBuildFile; fileRef = 11EF12EC142D63050086C77F /* ulist.png */; };
1DDD582C0DA1D0D100B32029 /* MyDocument.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1DDD58280DA1D0D100B32029 /* MyDocument.xib */; };
1DDD582D0DA1D0D100B32029 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1DDD582A0DA1D0D100B32029 /* MainMenu.xib */; };
+ 3D6A8BBC15F0D914002C8B62 /* NSTextView+EditPlainTextWithUndo.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D6A8BBB15F0D914002C8B62 /* NSTextView+EditPlainTextWithUndo.m */; };
795F6C87105D70A300D1F90A /* MarkdownLiveApp.icns in Resources */ = {isa = PBXBuildFile; fileRef = 795F6C86105D70A300D1F90A /* MarkdownLiveApp.icns */; };
795F6CCD105D741100D1F90A /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 795F6CCC105D741100D1F90A /* WebKit.framework */; };
8D15AC2C0486D014006FF6A4 /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 2A37F4B9FDCFA73011CA2CEA /* Credits.rtf */; };
@@ -106,6 +107,8 @@
2A37F4BAFDCFA73011CA2CEA /* English */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = English; path = English.lproj/Credits.rtf; sourceTree = "<group>"; };
2A37F4C4FDCFA73011CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
2A37F4C5FDCFA73011CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
+ 3D6A8BBA15F0D914002C8B62 /* NSTextView+EditPlainTextWithUndo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSTextView+EditPlainTextWithUndo.h"; sourceTree = "<group>"; };
+ 3D6A8BBB15F0D914002C8B62 /* NSTextView+EditPlainTextWithUndo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSTextView+EditPlainTextWithUndo.m"; sourceTree = "<group>"; };
3DD161871431FCED0003F6C7 /* PreferencesController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PreferencesController.h; sourceTree = "<group>"; };
3DD161881431FCF40003F6C7 /* EditPaneTypesetter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EditPaneTypesetter.h; sourceTree = "<group>"; };
3DD161891431FCFC0003F6C7 /* EditPaneTextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EditPaneTextView.h; sourceTree = "<group>"; };
@@ -216,6 +219,7 @@
children = (
ABECD96113C8D1C200B77CFD /* ORCDiscount */,
2A37F4ABFDCFA73011CA2CEA /* Classes */,
+ 3D6A8BBD15F0D919002C8B62 /* Categories */,
795F6C4D105D6EA500D1F90A /* discount */,
22ECEEC512E7C258003B50DC /* discount-config */,
795F6DB3105D75D300D1F90A /* discount_wrappers */,
@@ -283,6 +287,15 @@
name = Frameworks;
sourceTree = "<group>";
};
+ 3D6A8BBD15F0D919002C8B62 /* Categories */ = {
+ isa = PBXGroup;
+ children = (
+ 3D6A8BBA15F0D914002C8B62 /* NSTextView+EditPlainTextWithUndo.h */,
+ 3D6A8BBB15F0D914002C8B62 /* NSTextView+EditPlainTextWithUndo.m */,
+ );
+ name = Categories;
+ sourceTree = "<group>";
+ };
795F6C4D105D6EA500D1F90A /* discount */ = {
isa = PBXGroup;
children = (
@@ -450,6 +463,7 @@
ABF75D7613CDEBBB00B5E7AB /* PreferencesController.m in Sources */,
ABF75D7713CDEBBB00B5E7AB /* PreferencesManager.m in Sources */,
11EF12E0142D24BB0086C77F /* MLAppDelegate.m in Sources */,
+ 3D6A8BBC15F0D914002C8B62 /* NSTextView+EditPlainTextWithUndo.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
View
@@ -8,6 +8,7 @@
#import "ORCDiscount.h"
#import "MyDocument.h"
#import "EditPaneTextView.h"
+#import "NSTextView+EditPlainTextWithUndo.h"
#include "discountWrapper.h"
NSString *kMarkdownDocumentType = @"MarkdownDocumentType";
@@ -218,6 +219,30 @@ - (void)updateContent {
[[htmlPreviewWebView mainFrame] loadHTMLString:html baseURL:[self fileURL]];
}
+- (void)updateContentOnUndo {
+ NSUndoManager *undoManager = [self undoManager];
+
+ [undoManager registerUndoWithTarget:self
+ selector:@selector(updateContentOnUndo)
+ object:nil];
+
+ if ([undoManager isUndoing]) {
+ [self updateContent];
+ }
+}
+
+- (void)updateContentIncludingOnRedo {
+ NSUndoManager *undoManager = [self undoManager];
+
+ [undoManager registerUndoWithTarget:self
+ selector:@selector(updateContentIncludingOnRedo)
+ object:nil];
+
+ if ([undoManager isUndoing] == NO) {
+ [self updateContent];
+ }
+}
+
- (void)htmlPreviewTimer:(NSTimer*)timer_ {
#pragma unused(timer_)
@@ -276,7 +301,8 @@ - (IBAction)copyGeneratedHTMLAction:(id)sender {
}
- (void)_surroundSelectionWithString:(NSString *)string {
- NSMutableString *mutableString = markdownSourceTextView.textStorage.mutableString;
+ [self updateContentOnUndo];
+
NSMutableArray *newSelection = [[NSMutableArray alloc] init];
NSUInteger stringLength = string.length;
@@ -286,22 +312,24 @@ - (void)_surroundSelectionWithString:(NSString *)string {
NSRange range = [rangeInfo rangeValue];
range.location += insertedCharacters;
- [mutableString insertString:string atIndex:NSMaxRange(range)];
- [mutableString insertString:string atIndex:range.location];
+ [markdownSourceTextView insertText:string atIndex:NSMaxRange(range)];
+ [markdownSourceTextView insertText:string atIndex:range.location];
insertedCharacters += stringLength * 2;
range.location += stringLength;
[newSelection addObject:[NSValue valueWithRange:range]];
}
- [markdownSourceTextView setSelectedRanges:newSelection];
+ [markdownSourceTextView setSelectedRangesWithUndo:newSelection];
[newSelection release];
- [self updateContent];
+ [self updateContentIncludingOnRedo];
}
- (void)_addStringBeforeSelectedLines:(NSString *)string {
+ [self updateContentOnUndo];
+
NSMutableString *mutableString = markdownSourceTextView.textStorage.mutableString;
NSMutableArray *newSelection = [[NSMutableArray alloc] init];
NSUInteger stringLength = string.length;
@@ -325,7 +353,7 @@ - (void)_addStringBeforeSelectedLines:(NSString *)string {
if (startIndex < contentsEndIndex) {
// Prefix line with string
- [mutableString insertString:string atIndex:startIndex];
+ [markdownSourceTextView insertText:string atIndex:startIndex];
insertedCharacters += stringLength;
currentIndex = stringLength + lineEndIndex;
@@ -345,10 +373,10 @@ - (void)_addStringBeforeSelectedLines:(NSString *)string {
[newSelection addObject:[NSValue valueWithRange:range]];
}
- [markdownSourceTextView setSelectedRanges:newSelection];
+ [markdownSourceTextView setSelectedRangesWithUndo:newSelection];
[newSelection release];
- [self updateContent];
+ [self updateContentIncludingOnRedo];
}
- (IBAction)boldItalic:(NSSegmentedControl *)sender {
@@ -0,0 +1,24 @@
+//
+// NSTextView+EditPlainTextWithUndo.h
+// MarkdownLive
+//
+// Created by Jan Weiß on 31.08.12. Some rights reserved: <http://opensource.org/licenses/mit-license.php>
+//
+
+// Based on DrewThaler’s post at http://www.cocoadev.com/index.pl?UndoSupportForNSTextStorage
+
+#import <Foundation/Foundation.h>
+
+
+@interface NSTextView (EditPlainTextWithUndo)
+
+- (void)setSelectedRangeWithUndo:(NSRange)range;
+- (void)setSelectedRangesWithUndo:(NSArray *)ranges;
+
+- (BOOL)setText:(NSString *)string;
+- (BOOL)replaceCharactersInRange:(NSRange)range withText:(NSString *)string;
+- (BOOL)insertText:(NSString *)string atIndex:(NSUInteger)index;
+- (BOOL)insertText:(NSString *)string atIndex:(NSUInteger)index checkIndex:(BOOL)checkIndex;
+- (BOOL)insertText:(NSString *)string;
+
+@end
@@ -0,0 +1,102 @@
+//
+// NSTextView+EditPlainTextWithUndo.m
+// MarkdownLive
+//
+// Created by Jan Weiß on 31.08.12. Some rights reserved: <http://opensource.org/licenses/mit-license.php>
+//
+
+// Based on DrewThaler’s post at http://www.cocoadev.com/index.pl?UndoSupportForNSTextStorage
+
+#import "NSTextView+EditPlainTextWithUndo.h"
+
+
+@implementation NSTextView (EditPlainTextWithUndo)
+
+- (void)setSelectedRangeWithUndo:(NSRange)range;
+{
+ [self setSelectedRange:range];
+ [[self.undoManager prepareWithInvocationTarget:self] setSelectedRangeWithUndo:range];
+}
+
+- (void)setSelectedRangesWithUndo:(NSArray *)ranges;
+{
+ [self setSelectedRanges:ranges];
+ [[self.undoManager prepareWithInvocationTarget:self] setSelectedRangesWithUndo:ranges];
+}
+
+- (BOOL)setText:(NSString *)string;
+{
+ [[self.undoManager prepareWithInvocationTarget:self] setSelectedRangeWithUndo:self.selectedRange];
+
+ NSTextStorage *textStorage = [self textStorage];
+
+ if ([self shouldChangeTextInRange:NSMakeRange(0, [textStorage length])
+ replacementString:string]) {
+
+ [textStorage.mutableString setString:string];
+
+ [self didChangeText];
+
+ [self setSelectedRangeWithUndo:NSMakeRange(0, 0)];
+
+ return YES;
+ }
+ else {
+ return NO;
+ }
+}
+
+- (BOOL)replaceCharactersInRange:(NSRange)range withText:(NSString *)string;
+{
+ NSString *selectedText = [[self string] substringWithRange:range];
+ NSString *stringForDelegate = string;
+
+ // If only attributes are changing, pass nil.
+ if ([string isEqualToString:selectedText]) {
+ stringForDelegate = nil;
+ }
+
+ [[self.undoManager prepareWithInvocationTarget:self] setSelectedRangeWithUndo:self.selectedRange];
+
+ // Call delegate methods to force undo recording
+ if ([self shouldChangeTextInRange:range
+ replacementString:stringForDelegate]) {
+
+ [self.textStorage.mutableString replaceCharactersInRange:range
+ withString:string];
+
+ [self didChangeText];
+
+ [self setSelectedRangeWithUndo:NSMakeRange(range.location + [string length], 0)];
+
+ return YES;
+ }
+ else {
+ return NO;
+ }
+}
+
+- (BOOL)insertText:(NSString *)string atIndex:(NSUInteger)index;
+{
+ NSRange range = NSMakeRange(index, 0);
+ return [self replaceCharactersInRange:range withText:string];
+}
+
+- (BOOL)insertText:(NSString *)attributedString atIndex:(NSUInteger)index checkIndex:(BOOL)checkIndex;
+{
+ NSUInteger textLength = [self.textStorage length];
+
+ if (checkIndex && (index == NSNotFound || !(index <= textLength))) {
+ index = textLength; // AFTER the last character in textStorage
+ }
+
+ return [self insertText:attributedString atIndex:index];
+}
+
+- (BOOL)insertText:(NSString *)attributedString;
+{
+ NSRange range = [self selectedRange];
+ return [self replaceCharactersInRange:range withText:attributedString];
+}
+
+@end

0 comments on commit 260e1b0

Please sign in to comment.