cocoatogglenstextviewrichtext

how to toggle rich text formatting in NSTextView programmatically in cocoa


I want to toggle rich text formatting in NSTextView. I have tried following:

[contentView setRichText:NO];
[contentView setImportsGraphics:NO];

but, that didn't changed the NSTextView content and still allowing to do the text formatting.

Please let me know the simple way to toggle/switch rich text formatting in NSTextView just like TextEdit.

I already check the "TextEdit" sample project, but it seems to be very hard to find the usable code from it.

Thanks


Solution

  • I have created category for my view controller as follows:

    #define TabWidth @"TabWidth"
    
    @interface MyViewController (Helper)
    
    - (NSDictionary *)defaultTextAttributes:(BOOL)forRichText;
    - (void)removeAttachments;
    - (void)setRichText:(BOOL)flag;
    
    @end
    
    @implementation MyViewController (Helper)
    
    - (NSDictionary *)defaultTextAttributes:(BOOL)forRichText {
        static NSParagraphStyle *defaultRichParaStyle = nil;
        NSMutableDictionary *textAttributes = [[[NSMutableDictionary alloc] initWithCapacity:2] autorelease];
        if (forRichText) {
            [textAttributes setObject:[NSFont userFontOfSize:0.0] forKey:NSFontAttributeName];
            if (defaultRichParaStyle == nil) {  // We do this once...
                NSInteger cnt;
                NSString *measurementUnits = [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleMeasurementUnits"];
                CGFloat tabInterval = ([@"Centimeters" isEqual:measurementUnits]) ? (72.0 / 2.54) : (72.0 / 2.0);  // Every cm or half inch
                NSMutableParagraphStyle *paraStyle = [[[NSMutableParagraphStyle alloc] init] autorelease];
                [paraStyle setTabStops:[NSArray array]];    // This first clears all tab stops
                for (cnt = 0; cnt < 12; cnt++) {    // Add 12 tab stops, at desired intervals...
                    NSTextTab *tabStop = [[NSTextTab alloc] initWithType:NSLeftTabStopType location:tabInterval * (cnt + 1)];
                    [paraStyle addTabStop:tabStop];
                    [tabStop release];
                }
                defaultRichParaStyle = [paraStyle copy];
            }
            [textAttributes setObject:defaultRichParaStyle forKey:NSParagraphStyleAttributeName];
        } else {
            NSFont *plainFont = [NSFont userFixedPitchFontOfSize:0.0];
            NSInteger tabWidth = [[NSUserDefaults standardUserDefaults] integerForKey:TabWidth];
            CGFloat charWidth = [@" " sizeWithAttributes:[NSDictionary dictionaryWithObject:plainFont forKey:NSFontAttributeName]].width;
            if (charWidth == 0) charWidth = [[plainFont screenFontWithRenderingMode:NSFontDefaultRenderingMode] maximumAdvancement].width;
            
            // Now use a default paragraph style, but with the tab width adjusted
            NSMutableParagraphStyle *mStyle = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] autorelease];
            [mStyle setTabStops:[NSArray array]];
            [mStyle setDefaultTabInterval:(charWidth * tabWidth)];
            [textAttributes setObject:[[mStyle copy] autorelease] forKey:NSParagraphStyleAttributeName];
            
            // Also set the font
            [textAttributes setObject:plainFont forKey:NSFontAttributeName];
        }
        return textAttributes;
    }
    
    /* Used when converting to plain text
     */
    - (void)removeAttachments {
        NSTextStorage *attrString = [contentView textStorage];
        NSUInteger loc = 0;
        NSUInteger end = [attrString length];
        [attrString beginEditing];
        while (loc < end) { /* Run through the string in terms of attachment runs */
            NSRange attachmentRange;    /* Attachment attribute run */
            NSTextAttachment *attachment = [attrString attribute:NSAttachmentAttributeName atIndex:loc longestEffectiveRange:&attachmentRange inRange:NSMakeRange(loc, end-loc)];
            if (attachment) {   /* If there is an attachment and it is on an attachment character, remove the character */
                unichar ch = [[attrString string] characterAtIndex:loc];
                if (ch == NSAttachmentCharacter) {
                    if ([contentView shouldChangeTextInRange:NSMakeRange(loc, 1) replacementString:@""]) {
                        [attrString replaceCharactersInRange:NSMakeRange(loc, 1) withString:@""];
                        [contentView didChangeText];
                    }
                    end = [attrString length];  /* New length */
                }
                else loc++; /* Just skip over the current character... */
            }
            else loc = NSMaxRange(attachmentRange);
        }
        [attrString endEditing];
    }
    
    - (void)setRichText:(BOOL)flag {
        NSDictionary *textAttributes;
        
        BOOL isRichText = flag;
        
        if (!isRichText) [self removeAttachments];
        
        [contentView setRichText:isRichText];
        [contentView setUsesRuler:isRichText];    /* If NO, this correctly gets rid
                                                   of the ruler if it was up */
        if (isRichText && NO)
            [contentView setRulerVisible:YES];    /* Show ruler if rich, and desired */
        [contentView setImportsGraphics:isRichText];
        
        textAttributes = [self defaultTextAttributes:isRichText];
        
        if ([[contentView textStorage] length]) {
            [[contentView textStorage] setAttributes:textAttributes range: NSMakeRange(0,[[contentView textStorage] length])];
        }
        [contentView setTypingAttributes:textAttributes];
    }
    
    @end
    

    Where contentView is IBOutlet of NSTextView. Hope this will help someone or let me know if someone has shorter method.

    Thanks