Reputation: 16758
I am trying to use NSTextList
to display a numeric list in a multi-line NSTextField
but it is not working as intended.
The sample code used is:
- (IBAction)displayResult:(id)sender
{
NSTextList *list = [[NSTextList alloc] initWithMarkerFormat:self.markerFormat options:1]; // {Decimal} passed as marker format
NSMutableParagraphStyle *paragraph = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
[paragraph setTextLists:[NSArray arrayWithObject:list]];
[list release];
NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:paragraph, NSParagraphStyleAttributeName, nil];
NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:self.inputString attributes:attributes] ; // input string is- George \n Thomas \n Ashok
[self setOutputString:attrString];
[attrString release];
[paragraph release];
}
The input is -
George
Thomas
Ashok
The output should be -
1 George
2 Thomas
3 Ashok
but output it is showing is -
George
Thomas
Ashok
Can anyone suggest how to achieve the expected output?
Upvotes: 3
Views: 2125
Reputation: 387
I know this is four years late, but I encountered the same problem recently and was able to solve it using Swift.
I wanted to be able to convert the selected text in an NSTextView into a bulleted list, but a selection can obviously span across parts of multiple paragraphs. The first step is to figure out the proper selection range by accessing NSTextView.rangeForUserParagraphAttributeChange, which guarantees that you'll always be selecting a range that includes complete paragraphs, not partial ones.
Now, the more difficult part: You have to find each paragraph inside the modified selection range and add a bullet to it, as well as add the proper text list object and replace the text in the modified selected range to include the new text. This is easier said than done, and took me a couple of days to figure out (I'm still in college, so go figure).
I wasn't able to find hardly anything about this on the internet, so hopefully this will spare future internet travelers the trouble I had to go through.
if let range = textView?.rangeForUserParagraphAttributeChange {
// So, the user has selected some text. Range may or may not be the user's exact selection...
// The start of range has possibly back-tracked from the start of the selection to the start of
// the nearest paragraph, and the end has possibly extended from the end of the selection to the
// end of the nearest paragraph at that point.
// Therefore, we are guarunteed to be selecting complete paragraphs.
//
// Now, for each paragraph, we want to insert a bullet.
// The trick is finding the range of each paragraph.
//
// Find the attributed string contents of the entire possibly multi-paragraph selection:
let entireRangeText = textView!.textStorage!.attributedSubstringFromRange(range)
// Find the string contents
let entireRangeString = entireRangeText.string as NSString
// Let's make a list to store all of the paragraph ranges inside range
var paragraphRanges: [NSRange] = []
// Find the range of the first paragraph (possibly the only paragraph)
var paraRange = entireRangeString.paragraphRangeForRange(NSMakeRange(0, 0))
// paraRange is relative to range, so let's keep track of it's actual position.
var actualRange = NSMakeRange(paraRange.location + range.location, paraRange.length)
// Add it to the list
paragraphRanges += [actualRange]
// Now find each paragraph inside the selection
while NSMaxRange(paraRange) < entireRangeString.length {
// Find the next range relative to the range
paraRange = entireRangeString.paragraphRangeForRange(NSMakeRange(NSMaxRange(paraRange), 0))
// Find it's actual range relative to the entire text
actualRange = NSMakeRange(paraRange.location + range.location, paraRange.length)
// Add it to our list of paragraph ranges
paragraphRanges += [actualRange]
}
// This is the attributed string that we will use to replace the entireRangeText
// with the bulleted version.
let newText = NSMutableAttributedString()
// Start counting our bullets at 1...
// Todo: Respect the starting number in the preferences
var bulletNumber = 1
// Make a list object to add to each paragraph
// Todo: Respect the list type specified
let list = NSTextList(markerFormat: "{decimal}", options: 0)
// Go through each paragraph range and add bullets to the text inside it
for paragraphRange in paragraphRanges {
// Construct the bullet header:
let bulletText = "\t" + list.markerForItemNumber(bulletNumber) + "\t"
// Find the text from the paragraph
let paragraphText = textView!.textStorage!.attributedSubstringFromRange(paragraphRange)
let mutableParagraphText = NSMutableAttributedString(attributedString: paragraphText)
// A range pointer we really don't need:
var effectiveRange = NSRange()
// Construct our new string appropriately
// Get the paragraph attribute and modify it to have the text list
if let paragraphStyle = paragraphText.attribute(NSParagraphStyleAttributeName,
atIndex: 0,
longestEffectiveRange: &effectiveRange,
inRange: NSMakeRange(0, paragraphText.length))
as? NSParagraphStyle {
let newParagraphStyle = paragraphStyle.mutableCopy() as! NSMutableParagraphStyle
// Attach the list object to the paragraph style of the paragraph text
// So that auto list continuation will work properly
newParagraphStyle.textLists = [list]
// Update the text's paragraph style:
mutableParagraphText.addAttributes([NSParagraphStyleAttributeName: newParagraphStyle],
range: NSMakeRange(0, paragraphText.length))
}
// Make the bullet's attributes match the attributes of the text it's near, just
// like TextEdit does.
newText.appendAttributedString(
NSAttributedString(string: bulletText,
attributes: mutableParagraphText.attributesAtIndex(0,
longestEffectiveRange: &effectiveRange,
inRange: NSMakeRange(0, paragraphText.length))))
newText.appendAttributedString(mutableParagraphText)
// Increase to the next bullet number:
bulletNumber += 1
}
// Finally, insert our string
textView?.shouldChangeTextInRange(range, replacementString: nil)
textView?.insertText(newText, replacementRange: range)
// Tell the undo manager what happened:
textView?.didChangeText()
undoManager?.setActionName("Paragraph List") // ToDo: Localize
// Change the selection for auto-insertion to work
// IMPORTANT: Note that you have to change the selection twice to get auto list insertion to work
// (at least on my system). Must be a bug.
textView?.setSelectedRange(NSMakeRange(range.location + newText.string.characters.count - 1, 0))
textView?.setSelectedRange(NSMakeRange(range.location + newText.string.characters.count, 0))
}
Upvotes: 4
Reputation: 852
Everything you are doing looks fine and ornately to me:) There is a problem with your input string Try this,
NSTextList *list1 = [[NSTextList alloc] initWithMarkerFormat:@"{decimal}" options:0]; // {Decimal} passed as marker format
NSMutableParagraphStyle *paragraph = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
[list1 setStartingItemNumber:1];
[paragraph setTextLists:[NSArray arrayWithObject:list1]];
NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:paragraph, NSParagraphStyleAttributeName, nil];
NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:@"\t%@ Suhas \n \t%@ Devarshi \n \t%@ Rohith\n", [list1 markerForItemNumber:1],[list1 markerForItemNumber:2],[list1 markerForItemNumber:3]] attributes:attributes] ;
[self.text setStringValue:attrString];//self.text is a NSTextField instance. kindly ignore the compiler warning:)
Upvotes: 7