Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100755 792 lines (632 sloc) 31.061 kB
7aef368 (UNINTELLIGIBLE)
Zachary Schneirov authored
1 //
2 // NSString_NV.m
3 // Notation
4 //
5 // Created by Zachary Schneirov on 1/13/06.
c0a429b use a license header
Zachary Schneirov authored
6
7 /*Copyright (c) 2010, Zachary Schneirov. All rights reserved.
7af2491 @scrod Changed license to GPLv3. See http://scrodlog.notational.net/44603382…
authored
8 This file is part of Notational Velocity.
9
10 Notational Velocity is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 3 of the License, or
13 (at your option) any later version.
14
15 Notational Velocity is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with Notational Velocity. If not, see <http://www.gnu.org/licenses/>. */
7aef368 (UNINTELLIGIBLE)
Zachary Schneirov authored
22
23 #import "NSString_NV.h"
24 #import "NSData_transformations.h"
2ed9ce8 @scrod move file-related methods into an NSFileManager category
authored
25 #import "NSFileManager_NV.h"
7aef368 (UNINTELLIGIBLE)
Zachary Schneirov authored
26 #import "NoteObject.h"
27 #import "GlobalPrefs.h"
28 #import "LabelObject.h"
29
30 @implementation NSString (NV)
31
32 static int dayFromAbsoluteTime(CFAbsoluteTime absTime);
33
34 enum {NoSpecialDay = -1, ThisDay = 0, NextDay = 1, PriorDay = 2};
35
36 static const double dayInSeconds = 86400.0;
37 static CFTimeInterval secondsAfterGMT = 0.0;
38 static int currentDay = 0;
39 static CFMutableDictionaryRef dateStringsCache = NULL;
e2694bf @scrod use an alternate formatter for date-string previews in horizontal mode
authored
40 static CFDateFormatterRef dateAndTimeFormatter = NULL;
7aef368 (UNINTELLIGIBLE)
Zachary Schneirov authored
41
42 unsigned int hoursFromAbsoluteTime(CFAbsoluteTime absTime) {
43 return (unsigned int)floor(absTime / 3600.0);
44 }
45
46 //should be called after midnight, and then all the notes should have their date-strings recomputed
47 void resetCurrentDayTime() {
48 CFAbsoluteTime current = CFAbsoluteTimeGetCurrent();
769e318 leak
Zachary Schneirov authored
49
50 CFTimeZoneRef timeZone = CFTimeZoneCopyDefault();
51 secondsAfterGMT = CFTimeZoneGetSecondsFromGMT(timeZone, current);
52
7aef368 (UNINTELLIGIBLE)
Zachary Schneirov authored
53 currentDay = (int)floor((current + secondsAfterGMT) / dayInSeconds); // * dayInSeconds - secondsAfterGMT;
54
55 if (dateStringsCache)
56 CFDictionaryRemoveAllValues(dateStringsCache);
e2694bf @scrod use an alternate formatter for date-string previews in horizontal mode
authored
57
58 if (dateAndTimeFormatter) {
59 CFRelease(dateAndTimeFormatter);
60 dateAndTimeFormatter = NULL;
61 }
769e318 leak
Zachary Schneirov authored
62
63 CFRelease(timeZone);
7aef368 (UNINTELLIGIBLE)
Zachary Schneirov authored
64 }
65 //the epoch is defined at midnight GMT, so we have to convert from GMT to find the days
66
67 static int dayFromAbsoluteTime(CFAbsoluteTime absTime) {
68 if (currentDay == 0)
69 resetCurrentDayTime();
70
71 int timeDay = (int)floor((absTime + secondsAfterGMT) / dayInSeconds); // * dayInSeconds - secondsAfterGMT;
72 if (timeDay == currentDay) {
73 return ThisDay;
74 } else if (timeDay == currentDay + 1 /*dayInSeconds*/) {
75 return NextDay;
76 } else if (timeDay == currentDay - 1 /*dayInSeconds*/) {
77 return PriorDay;
78 }
79
80 return NoSpecialDay;
81 }
82
83 + (NSString*)relativeTimeStringWithDate:(CFDateRef)date relativeDay:(int)day {
84 static CFDateFormatterRef timeOnlyFormatter = nil;
85 static NSString *days[3] = { NULL };
86
87 if (!timeOnlyFormatter) {
e2694bf @scrod use an alternate formatter for date-string previews in horizontal mode
authored
88 timeOnlyFormatter = CFDateFormatterCreate(kCFAllocatorDefault, CFLocaleCopyCurrent(), kCFDateFormatterNoStyle, kCFDateFormatterShortStyle);
7aef368 (UNINTELLIGIBLE)
Zachary Schneirov authored
89 }
90
91 if (!days[ThisDay]) {
71bd9cf explicitly localize today/tomorrow/yesterday strings following apple'…
Zachary Schneirov authored
92 days[ThisDay] = [NSLocalizedString(@"Today", nil) retain];
93 days[NextDay] = [NSLocalizedString(@"Tomorrow", nil) retain];
94 days[PriorDay] = [NSLocalizedString(@"Yesterday", nil) retain];
7aef368 (UNINTELLIGIBLE)
Zachary Schneirov authored
95 }
96
97 CFStringRef dateString = CFDateFormatterCreateStringWithDate(kCFAllocatorDefault, timeOnlyFormatter, date);
e2694bf @scrod use an alternate formatter for date-string previews in horizontal mode
authored
98
99 if ([[GlobalPrefs defaultPrefs] horizontalLayout]) {
100 //if today, return the time only; otherwise say "Yesterday", etc.; and this method shouldn't be called unless day != NoSpecialDay
101 if (day == PriorDay || day == NextDay)
102 return days[day];
103 return [(id)dateString autorelease];
104 }
7aef368 (UNINTELLIGIBLE)
Zachary Schneirov authored
105
106 NSString *relativeTimeString = [days[day] stringByAppendingFormat:@" %@", dateString];
107 CFRelease(dateString);
108
109 return relativeTimeString;
110 }
111
112 //take into account yesterday/today thing
113 //this method _will_ affect application launch time
114 + (NSString*)relativeDateStringWithAbsoluteTime:(CFAbsoluteTime)absTime {
115 if (!dateStringsCache) {
116 CFDictionaryKeyCallBacks keyCallbacks = { kCFTypeDictionaryKeyCallBacks.version, (CFDictionaryRetainCallBack)NULL, (CFDictionaryReleaseCallBack)NULL,
117 (CFDictionaryCopyDescriptionCallBack)NULL, (CFDictionaryEqualCallBack)NULL, (CFDictionaryHashCallBack)NULL };
118 dateStringsCache = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &keyCallbacks, &kCFTypeDictionaryValueCallBacks);
119 }
238f532 Changes for 64-bit compatibility by Brian Bergstrand: Keyed archiving…
Zachary Schneirov authored
120 NSInteger minutesCount = (NSInteger)((NSInteger)absTime / 60);
7aef368 (UNINTELLIGIBLE)
Zachary Schneirov authored
121
122 NSString *dateString = (NSString*)CFDictionaryGetValue(dateStringsCache, (const void *)minutesCount);
123
124 if (!dateString) {
e2694bf @scrod use an alternate formatter for date-string previews in horizontal mode
authored
125 int day = dayFromAbsoluteTime(absTime);
126
127 if (!dateAndTimeFormatter) {
128 BOOL horiz = [[GlobalPrefs defaultPrefs] horizontalLayout];
129 dateAndTimeFormatter = CFDateFormatterCreate(kCFAllocatorDefault, CFLocaleCopyCurrent(),
130 horiz ? kCFDateFormatterShortStyle : kCFDateFormatterMediumStyle,
131 horiz ? kCFDateFormatterNoStyle : kCFDateFormatterShortStyle);
7aef368 (UNINTELLIGIBLE)
Zachary Schneirov authored
132 }
133
134 CFDateRef date = CFDateCreate(kCFAllocatorDefault, absTime);
135
136 if (day == NoSpecialDay) {
e2694bf @scrod use an alternate formatter for date-string previews in horizontal mode
authored
137 dateString = [(NSString*)CFDateFormatterCreateStringWithDate(kCFAllocatorDefault, dateAndTimeFormatter, date) autorelease];
7aef368 (UNINTELLIGIBLE)
Zachary Schneirov authored
138 } else {
139 dateString = [NSString relativeTimeStringWithDate:date relativeDay:day];
140 }
141
142 CFRelease(date);
143
144 //ints as pointers ints as pointers ints as pointers
145 CFDictionarySetValue(dateStringsCache, (const void *)minutesCount, (const void *)dateString);
146 }
147
148 return dateString;
149 }
150
0c0af95 @darrylhthomas Flagged possibly obsolete code.
darrylhthomas authored
151 // TODO: possibly obsolete? SN api2 formats dates as doubles from start of unix epoch
aa5d95a date/time parsing methods for simplenote dates
Zachary Schneirov authored
152 CFDateFormatterRef simplenoteDateFormatter(int lowPrecision) {
153 //CFStringRef dateStr = CFSTR("2010-01-02 23:23:31.876229");
154 static CFDateFormatterRef dateFormatter = NULL;
155 static CFDateFormatterRef lowPrecisionDateFormatter = NULL;
156 static CFLocaleRef locale = NULL;
157 static CFTimeZoneRef zone = NULL;
158 if (!dateFormatter) {
159 locale = CFLocaleCreate(NULL,CFSTR("en"));
160 zone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, 0.0);
161 dateFormatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterNoStyle, kCFDateFormatterNoStyle);
162 lowPrecisionDateFormatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterNoStyle, kCFDateFormatterNoStyle);
163 CFDateFormatterSetFormat(dateFormatter, CFSTR("yyyy-MM-dd HH:mm:ss.SSSSSS"));
164 CFDateFormatterSetFormat(lowPrecisionDateFormatter, CFSTR("yyyy-MM-dd HH:mm:ss"));
165 CFDateFormatterSetProperty(dateFormatter, kCFDateFormatterTimeZone, zone);
166 CFDateFormatterSetProperty(lowPrecisionDateFormatter, kCFDateFormatterTimeZone, zone);
167 }
168 return lowPrecision ? lowPrecisionDateFormatter : dateFormatter;
169 }
170
0c0af95 @darrylhthomas Flagged possibly obsolete code.
darrylhthomas authored
171 // TODO: possibly obsolete? SN api2 formats dates as doubles from start of unix epoch
aa5d95a date/time parsing methods for simplenote dates
Zachary Schneirov authored
172 + (NSString*)simplenoteDateWithAbsoluteTime:(CFAbsoluteTime)absTime {
173 CFStringRef str = CFDateFormatterCreateStringWithAbsoluteTime(NULL, simplenoteDateFormatter(0), absTime);
174 return [(id)str autorelease];
175 }
176
0c0af95 @darrylhthomas Flagged possibly obsolete code.
darrylhthomas authored
177 // TODO: possibly obsolete? SN api2 formats dates as doubles from start of unix epoch
aa5d95a date/time parsing methods for simplenote dates
Zachary Schneirov authored
178 - (CFAbsoluteTime)absoluteTimeFromSimplenoteDate {
179
180 CFAbsoluteTime absTime = 0;
181 if (!CFDateFormatterGetAbsoluteTimeFromString(simplenoteDateFormatter(0), (CFStringRef)self, NULL, &absTime)) {
182 if (!CFDateFormatterGetAbsoluteTimeFromString(simplenoteDateFormatter(1), (CFStringRef)self, NULL, &absTime)) {
183 NSLog(@"can't get date from %@; returning current time instead", self);
184 return 0;
185 }
186 }
187 return absTime;
188 }
189
f2632de @scrod autocompletion of labels in tags field should filter existing tags; s…
authored
190 - (NSArray*)labelCompatibleWords {
191 NSArray *array = nil;
192 if (IsLeopardOrLater) {
6e58cc8 @scrod suggest tags only if the suggestion range borders a non-tag separatin…
authored
193 array = [self componentsSeparatedByCharactersInSet:[NSCharacterSet labelSeparatorCharacterSet]];
f2632de @scrod autocompletion of labels in tags field should filter existing tags; s…
authored
194 } else {
195 BOOL lacksSpace = [self rangeOfString:@" " options:NSLiteralSearch].location == NSNotFound;
196 array = [self componentsSeparatedByString: lacksSpace ? @"," : @" "];
197 }
198 NSMutableArray *titles = [NSMutableArray arrayWithCapacity:[array count]];
199
200 NSUInteger i;
201 for (i=0; i<[array count]; i++) {
202 NSString *aWord = [array objectAtIndex:i];
4545c31 @scrod auto-formatting plain-text lists, using GENUINE INDENTATION instead o…
authored
203 if ([aWord length] > 0) {
f2632de @scrod autocompletion of labels in tags field should filter existing tags; s…
authored
204 [titles addObject:aWord];
205 }
206 }
207 return titles;
208 }
7aef368 (UNINTELLIGIBLE)
Zachary Schneirov authored
209
210 - (CFArrayRef)copyRangesOfWordsInString:(NSString*)findString inRange:(NSRange)limitRange {
211 CFStringRef quoteStr = CFSTR("\"");
212 CFRange quoteRange = CFStringFind((CFStringRef)findString, quoteStr, 0);
213 CFArrayRef terms = CFStringCreateArrayBySeparatingStrings(NULL, (CFStringRef)findString,
214 quoteRange.location == kCFNotFound ? CFSTR(" ") : quoteStr);
215 if (terms) {
216 CFIndex termIndex;
217 CFMutableArrayRef allRanges = NULL;
218
219 for (termIndex = 0; termIndex < CFArrayGetCount(terms); termIndex++) {
220 CFStringRef term = CFArrayGetValueAtIndex(terms, termIndex);
221 if (CFStringGetLength(term) > 0) {
222 CFArrayRef ranges = CFStringCreateArrayWithFindResults(NULL, (CFStringRef)self, term, CFRangeMake(limitRange.location,limitRange.length), kCFCompareCaseInsensitive);
223
224 if (ranges) {
225 if (!allRanges) {
226 //to make sure we get the right cfrange callbacks
227 allRanges = CFArrayCreateMutableCopy(NULL, 0, ranges);
228 } else {
229 CFArrayAppendArray(allRanges, ranges, CFRangeMake(0, CFArrayGetCount(ranges)));
230 }
231 CFRelease(ranges);
232 }
233 }
234 }
235 //should sort them all now by location
236 //CFArraySortValues(allRanges, CFRangeMake(0, CFArrayGetCount(allRanges)), <#CFComparatorFunction comparator#>,<#void * context#>);
4216a4e renamed to satisfy the create rule for memory management: getShortLiv…
Zachary Schneirov authored
237 CFRelease(terms);
7aef368 (UNINTELLIGIBLE)
Zachary Schneirov authored
238 return allRanges;
239 }
240
241 return NULL;
242 }
243
244 + (NSString*)customPasteboardTypeOfCode:(int)code {
245 //returns something like CorePasteboardFlavorType 0x4D5A0003
246 return [NSString stringWithFormat:@"CorePasteboardFlavorType 0x%X", code];
247 }
248
249 - (NSString*)stringAsSafePathExtension {
250 return [self stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"./*: \t\n\r"]];
251 }
252
253 - (NSString*)filenameExpectingAdditionalCharCount:(int)charCount {
254 NSString *newfilename = self;
255 if ([self length] + charCount > 255)
256 newfilename = [self substringToIndex: 255 - charCount];
257
258 return newfilename;
259 }
260
affb0a5 @scrod slightly smarter determination of titles from imported files; and don…
authored
261 - (BOOL)isAMachineDirective {
262 return [self hasPrefix:@"#!"] || [self hasPrefix:@"#import "] || [self hasPrefix:@"#include "] ||
263 [self hasPrefix:@"<!DOCTYPE "] || [self hasPrefix:@"<?xml "] || [self hasPrefix:@"<html "] ||
65e077b @scrod recognize PHP-file headers
authored
264 [self hasPrefix:@"@import "] || [self hasPrefix:@"<?php"] || [self hasPrefix:@"bplist0"];
affb0a5 @scrod slightly smarter determination of titles from imported files; and don…
authored
265
266 }
267
da0db0c when deployment target is 10.5 or higher, use the new-in-leopard stri…
Zachary Schneirov authored
268 #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
269 - (NSString*)stringByReplacingOccurrencesOfString:(NSString*)stringToReplace withString:(NSString*)replacementString {
270 //NSLog(@"NSString_NV: %s", _cmd);
271 NSMutableString *sanitizedName = [[self mutableCopy] autorelease];
7aef368 (UNINTELLIGIBLE)
Zachary Schneirov authored
272 [sanitizedName replaceOccurrencesOfString:stringToReplace withString:replacementString options:NSLiteralSearch range:NSMakeRange(0, [sanitizedName length])];
273
274 return sanitizedName;
275 }
da0db0c when deployment target is 10.5 or higher, use the new-in-leopard stri…
Zachary Schneirov authored
276 #endif
277
7aef368 (UNINTELLIGIBLE)
Zachary Schneirov authored
278 - (NSString*)fourCharTypeString {
279 if ([[self dataUsingEncoding:NSMacOSRomanStringEncoding allowLossyConversion:YES] length] >= 4) {
280 //only truncate; don't return a string containing null characters for the last few bytes
281 OSType type = UTGetOSTypeFromString((CFStringRef)self);
282 return [(id)UTCreateStringForOSType(type) autorelease];
283 }
284 return self;
285 }
286
1731016 @scrod fix importing URLs when the pasteboard contains only plaintext (e.g.,…
authored
287 - (BOOL)superficiallyResemblesAnHTTPURL {
288 //has the right protocol and contains no whitespace or line breaks
289
290 return ([self rangeOfString:@"http" options:NSCaseInsensitiveSearch | NSAnchoredSearch].location != NSNotFound ||
291 [self rangeOfString:@"https" options:NSCaseInsensitiveSearch | NSAnchoredSearch].location != NSNotFound) &&
292 [self rangeOfCharacterFromSet:[NSCharacterSet whitespaceAndNewlineCharacterSet] options:NSLiteralSearch].location == NSNotFound;
293 }
294
7aef368 (UNINTELLIGIBLE)
Zachary Schneirov authored
295 - (void)copyItemToPasteboard:(id)sender {
296
0dc9839 @scrod add a "Copy URL" item to the contextual note menu; change the NV URL …
authored
297 NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
298 [pasteboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
299 [pasteboard setString:[sender isKindOfClass:[NSMenuItem class]] ? [sender representedObject] : self
300 forType:NSStringPboardType];
7aef368 (UNINTELLIGIBLE)
Zachary Schneirov authored
301 }
302
303
5afdecc @scrod use shorter synthetic titles for imported data (36) than for known no…
authored
304 - (NSString*)syntheticTitleAndSeparatorWithContext:(NSString**)sepStr bodyLoc:(NSUInteger*)bodyLoc maxTitleLen:(NSUInteger)maxTitleLen {
305 return [self syntheticTitleAndSeparatorWithContext:sepStr bodyLoc:bodyLoc oldTitle:nil maxTitleLen:maxTitleLen];
7aef368 (UNINTELLIGIBLE)
Zachary Schneirov authored
306 }
307
5afdecc @scrod use shorter synthetic titles for imported data (36) than for known no…
authored
308 - (NSString*)syntheticTitleAndSeparatorWithContext:(NSString**)sepStr bodyLoc:(NSUInteger*)bodyLoc
309 oldTitle:(NSString*)oldTitle maxTitleLen:(NSUInteger)maxTitleLen {
091cba3 refactor title-separator methods, allow increasing title length for n…
Zachary Schneirov authored
310
d296045 mostly working method to differentiate title/body while tracking the …
Zachary Schneirov authored
311 //break string into pieces for turning into a note
312 //find the first line, whitespace or no whitespace
313
48c2fc7 @scrod slightly improved file-title importing
authored
314 NSCharacterSet *titleDelimiters = [NSCharacterSet characterSetWithCharactersInString:
315 [NSString stringWithFormat:@"\n\r\t%C%C", NSLineSeparatorCharacter, NSParagraphSeparatorCharacter]];
316
d296045 mostly working method to differentiate title/body while tracking the …
Zachary Schneirov authored
317 NSScanner *scanner = [NSScanner scannerWithString:self];
318 [scanner setCharactersToBeSkipped:[[[NSMutableCharacterSet alloc] init] autorelease]];
319
320 //skip any blank space before the title; this will not be preserved for round-tripped syncing
091cba3 refactor title-separator methods, allow increasing title length for n…
Zachary Schneirov authored
321 BOOL didSkipInitialWS = [scanner scanCharactersFromSet:[NSCharacterSet whitespaceAndNewlineCharacterSet] intoString:NULL];
322
5afdecc @scrod use shorter synthetic titles for imported data (36) than for known no…
authored
323 if ([oldTitle length] > maxTitleLen) {
091cba3 refactor title-separator methods, allow increasing title length for n…
Zachary Schneirov authored
324 //break apart the string based on an existing title (if it still matches) that would have been longer than our default truncation limit
325
326 NSString *contentStartStr = didSkipInitialWS && [scanner scanLocation] < [self length] ? [self substringFromIndex:[scanner scanLocation]] : self;
327 if ([contentStartStr length] >= [oldTitle length] && [contentStartStr hasPrefix:oldTitle]) {
328
329 [scanner setScanLocation:[oldTitle length] + (didSkipInitialWS ? [scanner scanLocation] : 0)];
330 [scanner scanContextualSeparator:sepStr withPrecedingString:oldTitle];
331 if (bodyLoc) *bodyLoc = [scanner scanLocation];
332 return oldTitle;
333 }
334 }
d296045 mostly working method to differentiate title/body while tracking the …
Zachary Schneirov authored
335
336 //grab the title
337 NSString *firstLine = nil;
338 [scanner scanUpToCharactersFromSet:titleDelimiters intoString:&firstLine];
339
5afdecc @scrod use shorter synthetic titles for imported data (36) than for known no…
authored
340 if ([firstLine length] > maxTitleLen) {
d296045 mostly working method to differentiate title/body while tracking the …
Zachary Schneirov authored
341 //what if this title is too long? then we need to break it up and start the body after that
342 NSRange lastSpaceInFirstLine = [firstLine rangeOfString:@" " options: NSBackwardsSearch | NSLiteralSearch
5afdecc @scrod use shorter synthetic titles for imported data (36) than for known no…
authored
343 range:NSMakeRange(maxTitleLen - 10, 10)];
d296045 mostly working method to differentiate title/body while tracking the …
Zachary Schneirov authored
344 if (lastSpaceInFirstLine.location == NSNotFound) {
5afdecc @scrod use shorter synthetic titles for imported data (36) than for known no…
authored
345 lastSpaceInFirstLine.location = maxTitleLen;
d296045 mostly working method to differentiate title/body while tracking the …
Zachary Schneirov authored
346 }
091cba3 refactor title-separator methods, allow increasing title length for n…
Zachary Schneirov authored
347 [scanner setScanLocation:[scanner scanLocation] - ([firstLine length] - lastSpaceInFirstLine.location)];
d296045 mostly working method to differentiate title/body while tracking the …
Zachary Schneirov authored
348 firstLine = [firstLine substringToIndex:lastSpaceInFirstLine.location];
349
091cba3 refactor title-separator methods, allow increasing title length for n…
Zachary Schneirov authored
350 [scanner scanContextualSeparator:sepStr withPrecedingString:firstLine];
d296045 mostly working method to differentiate title/body while tracking the …
Zachary Schneirov authored
351 if (bodyLoc) *bodyLoc = [scanner scanLocation];
091cba3 refactor title-separator methods, allow increasing title length for n…
Zachary Schneirov authored
352 return firstLine;
d296045 mostly working method to differentiate title/body while tracking the …
Zachary Schneirov authored
353 }
354
091cba3 refactor title-separator methods, allow increasing title length for n…
Zachary Schneirov authored
355 //grab blank space between the title and the body; common case:
356 [scanner scanContextualSeparator:sepStr withPrecedingString:firstLine];
357 if (bodyLoc) *bodyLoc = [scanner scanLocation];
d296045 mostly working method to differentiate title/body while tracking the …
Zachary Schneirov authored
358
091cba3 refactor title-separator methods, allow increasing title length for n…
Zachary Schneirov authored
359 return [firstLine length] ? firstLine : NSLocalizedString(@"Untitled Note", @"Title of a nameless note");
d296045 mostly working method to differentiate title/body while tracking the …
Zachary Schneirov authored
360 }
361
41295dc use a better title-deriving implementation in which it can be strippe…
Zachary Schneirov authored
362 - (NSString*)syntheticTitleAndTrimmedBody:(NSString**)newBody {
363 NSUInteger bodyLoc = 0;
5afdecc @scrod use shorter synthetic titles for imported data (36) than for known no…
authored
364 NSString *title = [self syntheticTitleAndSeparatorWithContext:NULL bodyLoc:&bodyLoc maxTitleLen:60];
41295dc use a better title-deriving implementation in which it can be strippe…
Zachary Schneirov authored
365 if (newBody) *newBody = [self substringFromIndex:bodyLoc];
366 return title;
7aef368 (UNINTELLIGIBLE)
Zachary Schneirov authored
367 }
368
369 //the following three methods + function come courtesy of Mike Ferris' TextExtras
370 + (NSString *)tabbifiedStringWithNumberOfSpaces:(unsigned)origNumSpaces tabWidth:(unsigned)tabWidth usesTabs:(BOOL)usesTabs {
371 static NSMutableString *sharedString = nil;
372 static unsigned numTabs = 0;
373 static unsigned numSpaces = 0;
374
375 int diffInTabs;
376 int diffInSpaces;
377
378 // TabWidth of 0 means don't use tabs!
379 if (!usesTabs || (tabWidth == 0)) {
380 diffInTabs = 0 - numTabs;
381 diffInSpaces = origNumSpaces - numSpaces;
382 } else {
383 diffInTabs = (origNumSpaces / tabWidth) - numTabs;
384 diffInSpaces = (origNumSpaces % tabWidth) - numSpaces;
385 }
386
387 if (!sharedString) {
388 sharedString = [[NSMutableString alloc] init];
389 }
390
391 if (diffInTabs < 0) {
392 [sharedString deleteCharactersInRange:NSMakeRange(0, -diffInTabs)];
393 } else {
394 unsigned numToInsert = diffInTabs;
395 while (numToInsert > 0) {
396 [sharedString replaceCharactersInRange:NSMakeRange(0, 0) withString:@"\t"];
397 numToInsert--;
398 }
399 }
400 numTabs += diffInTabs;
401
402 if (diffInSpaces < 0) {
403 [sharedString deleteCharactersInRange:NSMakeRange(numTabs, -diffInSpaces)];
404 } else {
405 unsigned numToInsert = diffInSpaces;
406 while (numToInsert > 0) {
407 [sharedString replaceCharactersInRange:NSMakeRange(numTabs, 0) withString:@" "];
408 numToInsert--;
409 }
410 }
411 numSpaces += diffInSpaces;
412
091cba3 refactor title-separator methods, allow increasing title length for n…
Zachary Schneirov authored
413
7aef368 (UNINTELLIGIBLE)
Zachary Schneirov authored
414 return sharedString;
415 }
416
417 - (unsigned)numberOfLeadingSpacesFromRange:(NSRange*)range tabWidth:(unsigned)tabWidth {
418 // Returns number of spaces, accounting for expanding tabs.
419 NSRange searchRange = (range ? *range : NSMakeRange(0, [self length]));
420 unichar buff[100];
421 unsigned i = 0;
422 unsigned spaceCount = 0;
423 BOOL done = NO;
424 unsigned tabW = tabWidth;
238f532 Changes for 64-bit compatibility by Brian Bergstrand: Keyed archiving…
Zachary Schneirov authored
425 NSUInteger endOfWhiteSpaceIndex = NSNotFound;
7aef368 (UNINTELLIGIBLE)
Zachary Schneirov authored
426
4216a4e renamed to satisfy the create rule for memory management: getShortLiv…
Zachary Schneirov authored
427 if (!range || range->length == 0) {
7aef368 (UNINTELLIGIBLE)
Zachary Schneirov authored
428 return 0;
429 }
430
431 while ((searchRange.length > 0) && !done) {
432 [self getCharacters:buff range:NSMakeRange(searchRange.location, ((searchRange.length > 100) ? 100 : searchRange.length))];
433 for (i=0; i < ((searchRange.length > 100) ? 100 : searchRange.length); i++) {
434 if (buff[i] == (unichar)' ') {
435 spaceCount++;
436 } else if (buff[i] == (unichar)'\t') {
437 // MF:!!! Perhaps this should account for the case of 2 spaces follwed by a tab really being visually equivalent to 8 spaces (for 8 space tabs) and not 10 spaces.
438 spaceCount += tabW;
439 } else {
440 done = YES;
441 endOfWhiteSpaceIndex = searchRange.location + i;
442 break;
443 }
444 }
445 searchRange.location += ((searchRange.length > 100) ? 100 : searchRange.length);
446 searchRange.length -= ((searchRange.length > 100) ? 100 : searchRange.length);
447 }
448 if (range && (endOfWhiteSpaceIndex != NSNotFound)) {
449 range->length = endOfWhiteSpaceIndex - range->location;
450 }
451 return spaceCount;
452 }
453
454 BOOL IsHardLineBreakUnichar(unichar uchar, NSString *str, unsigned charIndex) {
455 // This function redundantly takes both the character and the string and index. This is because often we only have to look at that one character and usually we already have it when this is called (usually from a source cheaper than characterAtIndex: too.)
456 // Returns yes if the unichar given is a hard line break, that is it will always cause a new line fragment to begin.
457 // MF:??? Is this test complete?
458 if ((uchar == (unichar)'\n') || (uchar == NSParagraphSeparatorCharacter) || (uchar == NSLineSeparatorCharacter)) {
459 return YES;
460 } else if ((uchar == (unichar)'\r') && ((charIndex + 1 >= [str length]) || ([str characterAtIndex:charIndex + 1] != (unichar)'\n'))) {
461 return YES;
462 }
463 return NO;
464 }
465
466 - (char*)copyLowercaseASCIIString {
467
468 const char *cstringPtr = NULL;
469
b8e85ab upon initializing a database that was created under the right circums…
Zachary Schneirov authored
470 //here we are making assumptions (based on observations and CFString.c) about the implementation of CFStringGetCStringPtr:
471 //with a non-western language preference, kCFStringEncodingASCII or another Latin variant must be used instead of kCFStringEncodingMacRoman
472 if ((cstringPtr = CFStringGetCStringPtr((CFStringRef)self, kCFStringEncodingMacRoman)) ||
473 (cstringPtr = CFStringGetCStringPtr((CFStringRef)self, kCFStringEncodingASCII))) {
7aef368 (UNINTELLIGIBLE)
Zachary Schneirov authored
474
60b88ca use 64-bit variant of lowercase technique from stringencoders project
Zachary Schneirov authored
475 size_t length = [self length];
476 char *cstringBuffer = (char*)malloc(length + 1);
477 //modp will add the NULL terminator
478 modp_tolower_copy(cstringBuffer, cstringPtr, length);
7aef368 (UNINTELLIGIBLE)
Zachary Schneirov authored
479
480 return cstringBuffer;
b8e85ab upon initializing a database that was created under the right circums…
Zachary Schneirov authored
481 } else {
5ab8d25 extra logging defeats the point of certain optimizations
Zachary Schneirov authored
482 //will be true on Snow Leopard for empty strings
483 //NSLog(@"found string that should have been 7 bit, but (apparently) is not.");
7aef368 (UNINTELLIGIBLE)
Zachary Schneirov authored
484 }
485
486 return NULL;
487 }
488
489 - (const char*)lowercaseUTF8String {
490
491 CFMutableStringRef str2 = CFStringCreateMutableCopy(NULL, 0, (CFStringRef)self);
492 CFStringLowercase(str2, NULL);
493
494 const char *utf8String = [(NSString*)str2 UTF8String];
495
8eea777 @scrod if UTF8String fails while generating cstring caches, then try other m…
authored
496 if (!utf8String) {
497
498 CFStringEncoding encoding = CFStringGetFastestEncoding(str2);
499 const char *cStrPtr = CFStringGetCStringPtr(str2, encoding);
500
501 if (cStrPtr && (kCFStringEncodingUTF8 == encoding || (encoding & 0x0100) == 0)) {
502 //-UTF8String failed, but the native cstringptr worked and was not a UTF16+ encoding
503 //so return that instead, forfeiting upper-ascii searchability for now
504 //(for whatever reason, native string encoding is almost never kCFStringEncodingUTF8, so CFStringGetFastestEncoding is called only if necessary)
505 utf8String = cStrPtr;
506
507 } else {
508 //-UTF8String failed and CFStringGetCStringPtr was not a good fallback; try lossy MacOSRoman and pray
509 NSMutableData *nullTerminatedData = [[[(NSString*)str2 dataUsingEncoding:NSMacOSRomanStringEncoding allowLossyConversion:YES] mutableCopy] autorelease];
510 [nullTerminatedData appendBytes:"\0" length:1];
511 utf8String = [nullTerminatedData bytes];
512 }
513 }
e09ad6e @scrod some additional safety for UTF8String method; autorelease parent CFSt…
authored
514 [(id)str2 autorelease];
7aef368 (UNINTELLIGIBLE)
Zachary Schneirov authored
515 return utf8String;
516 }
517
6b2d198 URL-encoding methods
Zachary Schneirov authored
518 - (NSString *)stringByReplacingPercentEscapes {
519 return [(NSString*) CFURLCreateStringByReplacingPercentEscapes(NULL, (CFStringRef) self, CFSTR("")) autorelease];
520 }
521
522 - (NSString*)stringWithPercentEscapes {
523 return [(NSString *) CFURLCreateStringByAddingPercentEscapes(NULL, (CFStringRef)[[self mutableCopy] autorelease], NULL, CFSTR("=,!$&'()*+;@?\n\"<>#\t :/"),kCFStringEncodingUTF8) autorelease];
524 }
525
7aef368 (UNINTELLIGIBLE)
Zachary Schneirov authored
526 + (NSString*)reasonStringFromCarbonFSError:(OSStatus)err {
527 static NSDictionary *reasons = nil;
528 if (!reasons) {
529 NSString *path = [[NSBundle mainBundle] pathForResource:@"CarbonErrorStrings" ofType:@"plist"];
530 reasons = [[NSDictionary dictionaryWithContentsOfFile:path] retain];
531 }
532
a8da2fe default error string fallback in the case of missing errors
Zachary Schneirov authored
533 NSString *reason = [reasons objectForKey:[[NSNumber numberWithInt:(int)err] stringValue]];
534 if (!reason)
535 return [NSString stringWithFormat:NSLocalizedString(@"an error of type %d occurred", @"string of last resort for errors not found in CarbonErrorStrings"), (int)err];
536 return reason;
7aef368 (UNINTELLIGIBLE)
Zachary Schneirov authored
537 }
538
256fcb5 Allow importing other plain-text file types based on the computed UTI…
Zachary Schneirov authored
539 - (BOOL)UTIOfFileConformsToType:(NSString*)type {
540
541 CFStringRef fileUTI = NULL;
542 FSRef fileRef;
543 if (FSPathMakeRef((const UInt8 *)[self fileSystemRepresentation], &fileRef, NULL) == noErr) {
544 if (LSCopyItemAttribute(&fileRef, kLSRolesAll, kLSItemContentType, (CFTypeRef*)&fileUTI) == noErr) {
545 if (fileUTI) {
546 BOOL conforms = UTTypeConformsTo(fileUTI, (CFStringRef)type);
547 CFRelease(fileUTI);
548 return conforms;
549 }
550 }
551 }
552 return NO;
553 }
554
7aef368 (UNINTELLIGIBLE)
Zachary Schneirov authored
555 - (CFUUIDBytes)uuidBytes {
556 CFUUIDBytes bytes = {0};
557 CFUUIDRef uuidRef = CFUUIDCreateFromString(NULL, (CFStringRef)self);
558 if (uuidRef) {
559 bytes = CFUUIDGetUUIDBytes(uuidRef);
560 CFRelease(uuidRef);
561 }
562
563 return bytes;
564 }
565
566 + (NSString*)uuidStringWithBytes:(CFUUIDBytes)bytes {
567 CFUUIDRef uuidRef = CFUUIDCreateFromUUIDBytes(NULL, bytes);
568 CFStringRef uuidString = NULL;
569
570 if (uuidRef) {
571 uuidString = CFUUIDCreateString(NULL, uuidRef);
572 CFRelease(uuidRef);
573 }
574
575 return [(NSString*)uuidString autorelease];
576 }
577
0dc9839 @scrod add a "Copy URL" item to the contextual note menu; change the NV URL …
authored
578
579 - (NSData *)decodeBase64 {
580 return [self decodeBase64WithNewlines:YES];
581 }
582
583 - (NSData *)decodeBase64WithNewlines:(BOOL)encodedWithNewlines {
584 // Create a memory buffer containing Base64 encoded string data
e09ad6e @scrod some additional safety for UTF8String method; autorelease parent CFSt…
authored
585 const char *utf8String = [self UTF8String];
586 if (!utf8String)
587 return nil;
588
589 BIO * mem = BIO_new_mem_buf((void *)utf8String, strlen(utf8String));
0dc9839 @scrod add a "Copy URL" item to the contextual note menu; change the NV URL …
authored
590
591 // Push a Base64 filter so that reading from the buffer decodes it
592 BIO * b64 = BIO_new(BIO_f_base64());
593 if (!encodedWithNewlines)
594 BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
595 mem = BIO_push(b64, mem);
596
597 // Decode into an NSMutableData
598 NSMutableData * data = [NSMutableData data];
599 char inbuf[512];
600 int inlen;
601 while ((inlen = BIO_read(mem, inbuf, sizeof(inbuf))) > 0)
602 [data appendBytes: inbuf length: inlen];
603
604 // Clean up and go home
605 BIO_free_all(mem);
606 return data;
607 }
608
7aef368 (UNINTELLIGIBLE)
Zachary Schneirov authored
609 @end
610
611
612 @implementation NSMutableString (NV)
613
1c3581c a "detab" method for simplenote
Zachary Schneirov authored
614 - (void)replaceTabsWithSpacesOfWidth:(int)tabWidth {
615 NSAssert(tabWidth < 50 && tabWidth > 0, @"that's a ridiculous tab width");
616
617 @try {
618 NSRange tabRange, nextRange = NSMakeRange(0, [self length]);
619 while ((tabRange = [self rangeOfString:@"\t" options:NSLiteralSearch range:nextRange]).location != NSNotFound) {
620
621 int numberOfSpacesPerTab = tabWidth;
622 int locationOnLine = tabRange.location - [self lineRangeForRange:tabRange].location;
623 if (numberOfSpacesPerTab != 0) {
624 int numberOfSpacesLess = locationOnLine % numberOfSpacesPerTab;
625 numberOfSpacesPerTab = numberOfSpacesPerTab - numberOfSpacesLess;
626 }
627 //NSLog(@"loc on line: %d, numberOfSpacesPerTab: %d", locationOnLine, numberOfSpacesPerTab);
628
629 NSMutableString *spacesString = [[NSMutableString alloc] initWithCapacity:numberOfSpacesPerTab];
630 while (numberOfSpacesPerTab-- > 0) {
631 [spacesString appendString:@" "];
632 }
633
634 [self replaceCharactersInRange:tabRange withString:spacesString];
635 [spacesString release];
636
637 NSUInteger rangeLoc = MIN((tabRange.location + numberOfSpacesPerTab), [self length]);
638 nextRange = NSMakeRange(rangeLoc, [self length] - rangeLoc);
639 }
640 } @catch (NSException *e) {
641 NSLog(@"%s got an exception: %@", _cmd, [e reason]);
642 }
643 }
644
c9f24be accessory view for choosing whether to import creation dates from the…
Zachary Schneirov authored
645 + (NSMutableString*)newShortLivedStringFromFile:(NSString*)filename {
646 NSStringEncoding anEncoding = NSMacOSRomanStringEncoding; //won't use this, doesn't matter
7aef368 (UNINTELLIGIBLE)
Zachary Schneirov authored
647
695b861 when importing files, inform the OS that it should not add their cont…
Zachary Schneirov authored
648 return [self newShortLivedStringFromData:[NSMutableData dataWithContentsOfFile:filename options:NSUncachedRead error:NULL]
c9f24be accessory view for choosing whether to import creation dates from the…
Zachary Schneirov authored
649 ofGuessedEncoding:&anEncoding withPath:[filename fileSystemRepresentation] orWithFSRef:NULL];
650 }
651
652 + (NSMutableString*)newShortLivedStringFromData:(NSMutableData*)data ofGuessedEncoding:(NSStringEncoding*)encoding withPath:(const char*)aPath orWithFSRef:(const FSRef*)fsRef{
653 //this will fail if data lacks a BOM, but try it first as it's the fastest check
7aef368 (UNINTELLIGIBLE)
Zachary Schneirov authored
654 NSMutableString* stringFromData = [data newStringUsingBOMReturningEncoding:encoding];
655 if (stringFromData) {
656 return stringFromData;
657 }
658
256fcb5 Allow importing other plain-text file types based on the computed UTI…
Zachary Schneirov authored
659 //TODO: there are some false positives for UTF-8 detection; e.g., the MacOSRoman-encoded copyright symbol
660
b8e85ab upon initializing a database that was created under the right circums…
Zachary Schneirov authored
661 //if it's just 7-bit ASCII, jump straight to the fastest encoding; don't even try UTF-8 (but report UTF-8, anyway)
662 BOOL hasHighASCII = ContainsHighAscii([data bytes], [data length]);
663 CFStringEncoding cfasciiEncoding = CFStringGetSystemEncoding() == kCFStringEncodingMacRoman ? kCFStringEncodingMacRoman : kCFStringEncodingASCII;
664 NSStringEncoding firstEncodingToTry = hasHighASCII ? NSUTF8StringEncoding : CFStringConvertEncodingToNSStringEncoding(cfasciiEncoding);
7aef368 (UNINTELLIGIBLE)
Zachary Schneirov authored
665
c9f24be accessory view for choosing whether to import creation dates from the…
Zachary Schneirov authored
666 #define AddIfUnique(enc) if (!ContainsUInteger(encodingsToTry, encodingIndex, (enc))) encodingsToTry[encodingIndex++] = (enc)
7aef368 (UNINTELLIGIBLE)
Zachary Schneirov authored
667
c9f24be accessory view for choosing whether to import creation dates from the…
Zachary Schneirov authored
668 NSStringEncoding encodingsToTry[5];
669 NSUInteger encodingIndex = 0;
7aef368 (UNINTELLIGIBLE)
Zachary Schneirov authored
670
c9f24be accessory view for choosing whether to import creation dates from the…
Zachary Schneirov authored
671 AddIfUnique(firstEncodingToTry);
7aef368 (UNINTELLIGIBLE)
Zachary Schneirov authored
672
82a1d92 Remove runtime and compile-time checks for Panther
Zachary Schneirov authored
673 if (hasHighASCII) {
c9f24be accessory view for choosing whether to import creation dates from the…
Zachary Schneirov authored
674 //check the file on disk for extended attributes only if absolutely necessary
675 NSStringEncoding extendedAttrsEncoding = 0;
676 if (!aPath && fsRef && !IsZeros(fsRef, sizeof(FSRef))) {
677 NSMutableData *pathData = [NSMutableData dataWithLength:4 * 1024];
678 if (FSRefMakePath(fsRef, [pathData mutableBytes], [pathData length]) == noErr)
2ed9ce8 @scrod move file-related methods into an NSFileManager category
authored
679 extendedAttrsEncoding = [[NSFileManager defaultManager] textEncodingAttributeOfFSPath:[pathData bytes]];
c9f24be accessory view for choosing whether to import creation dates from the…
Zachary Schneirov authored
680 } else if (aPath) {
2ed9ce8 @scrod move file-related methods into an NSFileManager category
authored
681 extendedAttrsEncoding = [[NSFileManager defaultManager] textEncodingAttributeOfFSPath:aPath];
c9f24be accessory view for choosing whether to import creation dates from the…
Zachary Schneirov authored
682 }
683 if (extendedAttrsEncoding) AddIfUnique(extendedAttrsEncoding);
684 }
685 AddIfUnique(*encoding);
b8e85ab upon initializing a database that was created under the right circums…
Zachary Schneirov authored
686 NSStringEncoding systemEncoding = CFStringConvertEncodingToNSStringEncoding(CFStringGetSystemEncoding());
c9f24be accessory view for choosing whether to import creation dates from the…
Zachary Schneirov authored
687 AddIfUnique(systemEncoding);
688 AddIfUnique(NSMacOSRomanStringEncoding);
689
690 encodingIndex = 0;
691 do {
82a1d92 Remove runtime and compile-time checks for Panther
Zachary Schneirov authored
692 stringFromData = [[NSMutableString alloc] initWithBytesNoCopy:[data mutableBytes] length:[data length]
693 encoding:encodingsToTry[encodingIndex] freeWhenDone:NO];
c9f24be accessory view for choosing whether to import creation dates from the…
Zachary Schneirov authored
694 } while (!stringFromData && ++encodingIndex < 5);
695
696 if (stringFromData) {
697 NSAssert(encodingIndex < 5, @"got valid string from data, but encodingIndex is too high!");
698 //report ASCII files as UTF-8 data in case this encoding will be used for future writes of a note
699 *encoding = hasHighASCII ? encodingsToTry[encodingIndex] : NSUTF8StringEncoding;
700 return stringFromData;
701 }
702
703 return nil;
7aef368 (UNINTELLIGIBLE)
Zachary Schneirov authored
704 }
705
706 @end
0ac3566 use firstCharacterIgnoringModifiers/firstCharacter methods that retur…
Zachary Schneirov authored
707
091cba3 refactor title-separator methods, allow increasing title length for n…
Zachary Schneirov authored
708 @implementation NSScanner (NV)
709
710 //useful for -syntheticTitleAndSeparatorWithContext:bodyLoc:oldTitle:
711 - (void)scanContextualSeparator:(NSString**)sepStr withPrecedingString:(NSString*)firstLine {
712
713 if (![firstLine length]) {
714 //no initial preceding string, so context won't make sense
715 if (sepStr) *sepStr = @"";
716 return;
717 }
718 NSUInteger len = [[self string] length];
719 if ([self scanCharactersFromSet:[NSCharacterSet whitespaceAndNewlineCharacterSet] intoString:sepStr]) {
720 if (sepStr && *sepStr) {
721 if ([self scanLocation] >= len) goto noBody;
722 //typical case
723 *sepStr = [NSString stringWithFormat:@"%C%@%C", [firstLine characterAtIndex:[firstLine length] - 1], *sepStr,
724 [[self string] characterAtIndex:[self scanLocation]]];
725 }
726 } else if (sepStr) {
727 //is this the end of the string, or was the scanner's location previously somewhere in the middle?
728 if ([self scanLocation] >= len) {
729 noBody: //all one line
730 *sepStr = @"";
731 } else {
732 //middle of the "title", probably because it is too long; grab the two surrounding characters
733 *sepStr = [NSString stringWithFormat:@"%C%C", [firstLine characterAtIndex:[firstLine length] - 1],
734 [[self string] characterAtIndex:[self scanLocation]]];
735 }
736 }
737
738 //location of _following_ string (usually the body of a note) will now be [self scanLocation]
739 }
740
741
742 @end
743
7c0f47e @scrod finish automatic [[double-bracketed]] linking and auto-completion; nv…
authored
744 @implementation NSCharacterSet (NV)
745
6e58cc8 @scrod suggest tags only if the suggestion range borders a non-tag separatin…
authored
746 + (NSCharacterSet*)labelSeparatorCharacterSet {
747 static NSMutableCharacterSet *charSet = nil;
748 if (!charSet) {
749 charSet = [[NSMutableCharacterSet whitespaceCharacterSet] retain];
943842b @scrod also use semicolon to separate tags
authored
750 [charSet formUnionWithCharacterSet:[NSCharacterSet characterSetWithCharactersInString:@",;"]];
6e58cc8 @scrod suggest tags only if the suggestion range borders a non-tag separatin…
authored
751 }
752
753 return charSet;
754 }
755
4545c31 @scrod auto-formatting plain-text lists, using GENUINE INDENTATION instead o…
authored
756 + (NSCharacterSet*)listBulletsCharacterSet {
757 static NSCharacterSet *charSet = nil;
758 if (!charSet) {
73da1a3 @scrod recognize additional characters in automatic list-bullet-formatting
authored
759 charSet = [[NSCharacterSet characterSetWithCharactersInString:[NSString stringWithFormat:@"-+*!#%C%C%C%C%C%C%C",
760 0x2022, 0x2014, 0x2013, 0x2043, 0x2713, 0x25AA, 0x25C6]] retain];
4545c31 @scrod auto-formatting plain-text lists, using GENUINE INDENTATION instead o…
authored
761 }
762
763 return charSet;
764
765 }
766
7c0f47e @scrod finish automatic [[double-bracketed]] linking and auto-completion; nv…
authored
767 #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
768 + (id)newlineCharacterSet {
769 return [NSCharacterSet characterSetWithCharactersInString:[NSString stringWithFormat:@"%C%C%C",0x000A,0x000D,0x0085]];
770 }
771 #endif
772
773 @end
774
091cba3 refactor title-separator methods, allow increasing title length for n…
Zachary Schneirov authored
775
0ac3566 use firstCharacterIgnoringModifiers/firstCharacter methods that retur…
Zachary Schneirov authored
776
777 @implementation NSEvent (NV)
778
779 - (unichar)firstCharacter {
780 NSString *chars = [self characters];
781 if ([chars length]) return [chars characterAtIndex:0];
782 return USHRT_MAX;
783 }
784
785 - (unichar)firstCharacterIgnoringModifiers {
786 NSString *chars = [self charactersIgnoringModifiers];
787 if ([chars length]) return [chars characterAtIndex:0];
788 return USHRT_MAX;
789 }
790
791 @end
Something went wrong with that request. Please try again.