PageRenderTime 44ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/System/Applications/TextEdit/TextFinder.m

#
Objective C | 339 lines | 242 code | 61 blank | 36 comment | 52 complexity | 9a5cdf348845c5850f940f45619a22ba MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1
  1. /*
  2. TextFinder.m
  3. Copyright (c) 1995-1996, NeXT Software, Inc.
  4. All rights reserved.
  5. Author: Ali Ozer
  6. You may freely copy, distribute and reuse the code in this example.
  7. NeXT disclaims any warranty of any kind, expressed or implied,
  8. as to its fitness for any particular use.
  9. Find and replace functionality with a minimal panel...
  10. In addition to including this class and FindPanel.nib in your app, you
  11. probably need to hook up the following action methods in your document
  12. object (or whatever object is first responder) to call the appropriate
  13. methods in [TextFinder sharedInstance]:
  14. orderFrontFindPanel:
  15. findNext:
  16. findPrevious:
  17. enterSelection: (calls setFindString:)
  18. */
  19. #import <AppKit/AppKit.h>
  20. #import "TextFinder.h"
  21. @implementation TextFinder
  22. - (id) init
  23. {
  24. if (!(self = [super init]))
  25. return nil;
  26. [[NSNotificationCenter defaultCenter] addObserver: self
  27. selector: @selector (appDidActivate:)
  28. name: NSApplicationDidBecomeActiveNotification
  29. object: [NSApplication sharedApplication]];
  30. [[NSNotificationCenter defaultCenter] addObserver: self
  31. selector: @selector (addWillDeactivate:)
  32. name: NSApplicationWillResignActiveNotification
  33. object: [NSApplication sharedApplication]];
  34. [self setFindString: @""];
  35. [self loadFindStringFromPasteboard];
  36. return self;
  37. }
  38. - (void) appDidActivate: (NSNotification *)notification
  39. {
  40. [self loadFindStringFromPasteboard];
  41. }
  42. - (void)addWillDeactivate: (NSNotification *)notification
  43. {
  44. [self loadFindStringToPasteboard];
  45. }
  46. - (void)loadFindStringFromPasteboard
  47. {
  48. NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:NSFindPboard];
  49. if ([[pasteboard types] containsObject:NSStringPboardType]) {
  50. NSString *string = [pasteboard stringForType:NSStringPboardType];
  51. if (string && [string length]) {
  52. [self setFindString: string];
  53. findStringChangedSinceLastPasteboardUpdate = NO;
  54. }
  55. }
  56. }
  57. - (void) loadFindStringToPasteboard
  58. {
  59. NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName: NSFindPboard];
  60. if (findStringChangedSinceLastPasteboardUpdate) {
  61. [pasteboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
  62. [pasteboard setString:[self findString] forType:NSStringPboardType];
  63. findStringChangedSinceLastPasteboardUpdate = NO;
  64. }
  65. }
  66. static id sharedFindObject = nil;
  67. + (id)sharedInstance
  68. {
  69. if (!sharedFindObject) {
  70. sharedFindObject = [[self alloc] init];
  71. }
  72. return sharedFindObject;
  73. }
  74. - (void) loadUI
  75. {
  76. if (!findTextField) {
  77. if (![NSBundle loadNibNamed: @"FindPanel" owner: self]) {
  78. NSLog (@"Failed to load FindPanel.nib");
  79. NSBeep ();
  80. }
  81. if (self == sharedFindObject)
  82. [[findTextField window] setFrameAutosaveName: @"Find"];
  83. }
  84. [findTextField setStringValue: [self findString]];
  85. }
  86. - (void) dealloc
  87. {
  88. if (self != sharedFindObject) {
  89. [findString release];
  90. [super dealloc];
  91. }
  92. }
  93. - (NSString *) findString
  94. {
  95. return findString;
  96. }
  97. - (void) setFindString: (NSString *)string
  98. {
  99. if ([string isEqualToString: findString])
  100. return;
  101. [findString autorelease];
  102. findString = [string copy];
  103. if (findTextField) {
  104. [findTextField setStringValue: string];
  105. [findTextField selectText: nil];
  106. }
  107. findStringChangedSinceLastPasteboardUpdate = YES;
  108. }
  109. - (NSTextView *) textObjectToSearchIn
  110. {
  111. id obj = [[NSApp mainWindow] firstResponder];
  112. return (obj && [obj isKindOfClass: [NSTextView class]]) ? obj : nil;
  113. }
  114. - (NSPanel *) findPanel
  115. {
  116. if (!findTextField)
  117. [self loadUI];
  118. return (NSPanel *) [findTextField window];
  119. }
  120. /*
  121. The primitive for finding; this ends up setting the status field (and
  122. beeping if necessary)...
  123. */
  124. - (BOOL) find: (BOOL)direction
  125. {
  126. NSTextView *text = [self textObjectToSearchIn];
  127. lastFindWasSuccessful = NO;
  128. if (text) {
  129. NSString *textContents = [text string];
  130. unsigned int textLength;
  131. if (textContents && (textLength = [textContents length])) {
  132. NSRange range;
  133. unsigned int options = 0;
  134. if (direction == Backward)
  135. options |= NSBackwardsSearch;
  136. if ([ignoreCaseButton state])
  137. options |= NSCaseInsensitiveSearch;
  138. range = [textContents findString: [self findString] selectedRange: [text selectedRange] options: options wrap: YES];
  139. if (range.length) {
  140. [text setSelectedRange: range];
  141. [text scrollRangeToVisible: range];
  142. lastFindWasSuccessful = YES;
  143. }
  144. }
  145. }
  146. if (!lastFindWasSuccessful) {
  147. NSBeep ();
  148. [statusField setStringValue: NSLocalizedStringFromTable (@"Not found", @"FindPanel", @"Status displayed in find panel when the find string is not found.")];
  149. } else {
  150. [statusField setStringValue: @""];
  151. }
  152. return lastFindWasSuccessful;
  153. }
  154. - (void) orderFrontFindPanel: (id)sender
  155. {
  156. NSPanel *panel = [self findPanel];
  157. [findTextField selectText: nil];
  158. [panel makeKeyAndOrderFront: nil];
  159. }
  160. /**** Action methods for gadgets in the find panel; these should all end up setting or clearing the status field ****/
  161. - (void) findNextAndOrderFindPanelOut: (id)sender
  162. {
  163. [findNextButton performClick: nil];
  164. if (lastFindWasSuccessful) {
  165. [[self findPanel] orderOut: sender];
  166. } else {
  167. [findTextField selectText: nil];
  168. }
  169. }
  170. - (void) findNext: (id)sender
  171. {
  172. if (findTextField)
  173. [self setFindString: [findTextField stringValue]]; /* findTextField should be set */
  174. [self find: Forward];
  175. }
  176. - (void) findPrevious: (id)sender
  177. {
  178. if (findTextField)
  179. [self setFindString:[findTextField stringValue]]; /* findTextField should be set */
  180. [self find: Backward];
  181. }
  182. - (void) replace: (id)sender
  183. {
  184. NSTextView *text = [self textObjectToSearchIn];
  185. if (!text) {
  186. NSBeep ();
  187. } else {
  188. [[text textStorage] replaceCharactersInRange: [text selectedRange] withString: [replaceTextField stringValue]];
  189. [text didChangeText];
  190. }
  191. [statusField setStringValue: @""];
  192. }
  193. - (void) replaceAndFind: (id)sender
  194. {
  195. [self replace: sender];
  196. [self findNext: sender];
  197. }
  198. #define ReplaceAllScopeEntireFile 42
  199. #define ReplaceAllScopeSelection 43
  200. - (void) replaceAll: (id)sender
  201. {
  202. NSTextView *text = [self textObjectToSearchIn];
  203. if (!text) {
  204. NSBeep();
  205. } else {
  206. NSTextStorage *textStorage = [text textStorage];
  207. NSString *textContents = [text string];
  208. NSString *replaceString = [replaceTextField stringValue];
  209. BOOL entireFile = replaceAllScopeMatrix ? ([replaceAllScopeMatrix selectedTag] == ReplaceAllScopeEntireFile) : YES;
  210. NSRange replaceRange = entireFile ? NSMakeRange (0, [textStorage length]) : [text selectedRange];
  211. unsigned int options = NSBackwardsSearch | ([ignoreCaseButton state] ? NSCaseInsensitiveSearch : 0);
  212. unsigned int replaced = 0;
  213. if (findTextField)
  214. [self setFindString:[findTextField stringValue]];
  215. while (1) {
  216. NSRange foundRange = [textContents rangeOfString: [self findString] options: options range: replaceRange];
  217. if (foundRange.length == 0)
  218. break;
  219. if ([text shouldChangeTextInRange: foundRange replacementString: replaceString]) {
  220. if (replaced == 0)
  221. [textStorage beginEditing];
  222. replaced++;
  223. [textStorage replaceCharactersInRange: foundRange withString: replaceString];
  224. replaceRange.length = foundRange.location - replaceRange.location;
  225. }
  226. }
  227. if (replaced > 0) { /* There was at least one replacement */
  228. [textStorage endEditing]; /* We need this to bracket the beginEditing */
  229. [text didChangeText]; /* We need one of these to terminate the shouldChange... methods we sent */
  230. [statusField setStringValue: [NSString localizedStringWithFormat: NSLocalizedStringFromTable (@"%d replaced", @"FindPanel", @"Status displayed in find panel when indicated number of matches are replaced."), replaced]];
  231. } else { /* No replacements were done... */
  232. NSBeep();
  233. [statusField setStringValue:NSLocalizedStringFromTable(@"Not found", @"FindPanel", @"Status displayed in find panel when the find string is not found.")];
  234. }
  235. }
  236. }
  237. @end
  238. @implementation NSString (NSStringTextFinding)
  239. - (NSRange) findString: (NSString *)string selectedRange: (NSRange)selectedRange options: (unsigned)options wrap: (BOOL)wrap
  240. {
  241. BOOL forwards = (options & NSBackwardsSearch) == 0;
  242. unsigned int length = [self length];
  243. NSRange searchRange, range;
  244. if (forwards) {
  245. searchRange.location = NSMaxRange(selectedRange);
  246. searchRange.length = length - searchRange.location;
  247. range = [self rangeOfString:string options:options range:searchRange];
  248. if ((range.length == 0) && wrap) { /* If not found look at the first part of the string */
  249. searchRange.location = 0;
  250. searchRange.length = selectedRange.location;
  251. range = [self rangeOfString:string options:options range:searchRange];
  252. }
  253. } else {
  254. searchRange.location = 0;
  255. searchRange.length = selectedRange.location;
  256. range = [self rangeOfString:string options:options range:searchRange];
  257. if ((range.length == 0) && wrap) {
  258. searchRange.location = NSMaxRange(selectedRange);
  259. searchRange.length = length - searchRange.location;
  260. range = [self rangeOfString:string options:options range:searchRange];
  261. }
  262. }
  263. return range;
  264. }
  265. @end
  266. /*
  267. 2/21/95 aozer Created for Edit II.
  268. 2/24/95 aozer Find pasteboard support
  269. 8/16/95 aozer Replace functionality
  270. 10/4/95 aozer Status field update
  271. 11/12/96 aozer Correctly send shouldChange... to the textview while doing a replace
  272. */