Put NSAutoreleasePool usage around other distributed notification observer methods
[adiumx.git] / Source / AIMDLogViewerWindowController.m
blob8e0d6bde188df925cb59eda581ac21cfcec4286c
1 //
2 //  AIMDLogViewerWindowController.m
3 //  Adium
4 //
5 //  Created by Evan Schoenberg on 3/1/06.
6 //
8 #import "AIMDLogViewerWindowController.h"
9 #import <AIUtilities/AIArrayAdditions.h>
10 #import <AIUtilities/AIStringAdditions.h>
11 #import "AILoggerPlugin.h"
12 #import "AILogToGroup.h"
13 #import "AILogFromGroup.h"
14 #import "AIChatLog.h"
15 #import <Adium/AIContactControllerProtocol.h>
17 @implementation AIMDLogViewerWindowController
19 - (id)initWithWindowNibName:(NSString *)windowNibName
21         if ((self = [super initWithWindowNibName:windowNibName])) {
22                 currentSearchLock = [[NSLock alloc] init];
23         }
24         return self;
27 - (void)windowDidLoad
29         [super windowDidLoad];
30         
31         [tableView_results setAutosaveName:@"LogViewerResults"];
32         [tableView_results setAutosaveTableColumns:YES];
35 /*!
36  * @brief Perform a content search of the indexed logs
37  *
38  * This uses the 10.4+ asynchronous search functions.
39  * Google-like search syntax (phrase, prefix/suffix, boolean, etc. searching) is automatically supported.
40  */
41 - (void)_logContentFilter:(NSString *)searchString searchID:(int)searchID onSearchIndex:(SKIndexRef)logSearchIndex
43         float                   largestRankingValue = 0;
44         SKSearchRef             thisSearch;
45     Boolean                     more = true;
46     UInt32                      totalCount = 0;
48         [currentSearchLock lock];
49         if (currentSearch) {
50                 SKSearchCancel(currentSearch);
51                 CFRelease(currentSearch); currentSearch = NULL;
52         }
54         thisSearch = SKSearchCreate(logSearchIndex,
55                                                                 (CFStringRef)searchString,
56                                                                 kSKSearchOptionDefault);
57         currentSearch = (thisSearch ? (SKSearchRef)CFRetain(thisSearch) : NULL);
58         [currentSearchLock unlock];
60         //Retrieve matches as long as more are pending
61     while (more && currentSearch) {
62 #define BATCH_NUMBER 100
63         SKDocumentID    foundDocIDs[BATCH_NUMBER];
64         float                   foundScores[BATCH_NUMBER];
65         SKDocumentRef   foundDocRefs[BATCH_NUMBER];
67         CFIndex foundCount = 0;
68         CFIndex i;
69                 
70         more = SKSearchFindMatches (
71                                                                         thisSearch,
72                                                                         BATCH_NUMBER,
73                                                                         foundDocIDs,
74                                                                         foundScores,
75                                                                         0.5, // maximum time before func returns, in seconds
76                                                                         &foundCount
77                                                                         );
78                 
79         totalCount += foundCount;
80                 
81         SKIndexCopyDocumentRefsForDocumentIDs (
82                                                                                            logSearchIndex,
83                                                                                            foundCount,
84                                                                                            foundDocIDs,
85                                                                                            foundDocRefs
86                                                                                            );
87         for (i = 0; ((i < foundCount) && (searchID == activeSearchID)) ; i++) {
88                         SKDocumentRef   document = foundDocRefs[i];
89                         CFURLRef                url = SKDocumentCopyURL(document);
90                         /*
91                          * Nasty implementation note: As of 10.4.7 and all previous versions, a path longer than 1024 bytes (PATH_MAX)
92                          * will cause CFURLCopyFileSystemPath() to crash [ultimately in CFGetAllocator()].  This is the case for all
93                          * Cocoa applications...
94                          */
95                         CFStringRef             logPath = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
96                         NSArray                 *pathComponents = [(NSString *)logPath pathComponents];
97                         
98                         //Don't test for the date now; we'll test once we've found the AIChatLog if we make it that far
99                         if ([self searchShouldDisplayDocument:document pathComponents:pathComponents testDate:NO]) {
100                                 unsigned int    numPathComponents = [pathComponents count];
101                                 NSString                *toPath = [NSString stringWithFormat:@"%@/%@",
102                                         [pathComponents objectAtIndex:numPathComponents-3],
103                                         [pathComponents objectAtIndex:numPathComponents-2]];
104                                 NSString                *path = [NSString stringWithFormat:@"%@/%@",toPath,[pathComponents objectAtIndex:numPathComponents-1]];
105                                 AIChatLog               *theLog;
106                                 
107                                 /* Add the log - if our index is currently out of date (for example, a log was just deleted) 
108                                  * we may get a null log, so be careful.
109                                  */
110                                 theLog = [[logToGroupDict objectForKey:toPath] logAtPath:path];
111                                 if (!theLog) {
112                                         AILog(@"_logContentFilter: %x's key %@ yields %@; logAtPath:%@ gives %@",logToGroupDict,toPath,[logToGroupDict objectForKey:toPath],path,theLog);
113                                 }
114                                 [resultsLock lock];
115                                 if ((theLog != nil) &&
116                                         (![currentSearchResults containsObjectIdenticalTo:theLog]) &&
117                                         [self chatLogMatchesDateFilter:theLog] &&
118                                         (searchID == activeSearchID)) {
119                                         [theLog setRankingValueOnArbitraryScale:foundScores[i]];
120                                         
121                                         //SearchKit does not normalize ranking scores, so we track the largest we've found and use it as 1.0
122                                         if (foundScores[i] > largestRankingValue) largestRankingValue = foundScores[i];
124                                         [currentSearchResults addObject:theLog];
125                                 } else {
126                                         //Didn't get a valid log, so decrement our totalCount which is tracking how many logs we found
127                                         totalCount--;
128                                 }
129                                 [resultsLock unlock];                                   
130                                 
131                         } else {
132                                 //Didn't add this log, so decrement our totalCount which is tracking how many logs we found
133                                 totalCount--;
134                         }
135                         
136                         if (logPath) CFRelease(logPath);
137                         if (url) CFRelease(url);
138                         if (document) CFRelease(document);
139         }
140                 
141                 //Scale all logs' ranking values to the largest ranking value we've seen thus far
142                 [resultsLock lock];
143                 for (i = 0; ((i < totalCount) && (searchID == activeSearchID)); i++) {
144                         AIChatLog       *theLog = [currentSearchResults objectAtIndex:i];
145                         [theLog setRankingPercentage:([theLog rankingValueOnArbitraryScale] / largestRankingValue)];
146                 }
147                 [resultsLock unlock];
149                 [self performSelectorOnMainThread:@selector(updateProgressDisplay)
150                                                            withObject:nil
151                                                         waitUntilDone:NO];
152                 
153                 if (searchID != activeSearchID) {
154                         more = FALSE;
155                 }
156     }
157         
158         //Ensure current search isn't released in two places simultaneously
159         [currentSearchLock lock];
160         if (currentSearch) {
161                 CFRelease(currentSearch);
162                 currentSearch = NULL;
163         }
164         [currentSearchLock unlock];
166         if (thisSearch) CFRelease(thisSearch);
169 - (void)stopSearching
170 {       
171         [currentSearchLock lock];
172         if (currentSearch) {
173                 SKSearchCancel(currentSearch);
174                 CFRelease(currentSearch); currentSearch = nil;
175         }
176         [currentSearchLock unlock];
178         [super stopSearching];
181 #pragma mark Date type menu
183 - (void)configureDateFilter
185         [super configureDateFilter];
186         
187         [datePicker setDateValue:[NSDate date]];
190 - (IBAction)selectDate:(id)sender
192         [filterDate release];
193         filterDate = [[[datePicker dateValue] dateWithCalendarFormat:nil timeZone:nil] retain];
195         [self startSearchingClearingCurrentResults:YES];
198 - (NSMenu *)dateTypeMenu
200         NSMenu          *dateTypeMenu = [super dateTypeMenu];
201         AIDateType      dateType;
202         NSDictionary *dateTypeTitleDict = [NSDictionary dictionaryWithObjectsAndKeys:
203                 AILocalizedString(@"Exactly", nil), [NSNumber numberWithInt:AIDateTypeExactly],
204                 AILocalizedString(@"Before", nil), [NSNumber numberWithInt:AIDateTypeBefore],
205                 AILocalizedString(@"After", nil), [NSNumber numberWithInt:AIDateTypeAfter],
206                 nil];
208         [dateTypeMenu addItem:[NSMenuItem separatorItem]];              
210         for (dateType = AIDateTypeExactly; dateType <= AIDateTypeAfter; dateType++) {
211                 [dateTypeMenu addItem:[self _menuItemForDateType:dateType dict:dateTypeTitleDict]];
212         }
214         return dateTypeMenu;
218  * @brief A new date type was selected
220  * The date picker will be hidden/revealed as appropriate.
221  * This does not start a search
222  */ 
223 - (void)selectedDateType:(AIDateType)dateType
225         BOOL                    showDatePicker = NO;
227         [super selectedDateType:dateType];
229         switch (dateType) {
230                 case AIDateTypeExactly:
231                         filterDateType = AIDateTypeExactly;
232                         filterDate = [[[datePicker dateValue] dateWithCalendarFormat:nil timeZone:nil] retain];
233                         showDatePicker = YES;
234                         break;
235                         
236                 case AIDateTypeBefore:
237                         filterDateType = AIDateTypeBefore;
238                         filterDate = [[[datePicker dateValue] dateWithCalendarFormat:nil timeZone:nil] retain];
239                         showDatePicker = YES;
240                         break;
241                         
242                 case AIDateTypeAfter:
243                         filterDateType = AIDateTypeAfter;
244                         filterDate = [[[datePicker dateValue] dateWithCalendarFormat:nil timeZone:nil] retain];
245                         showDatePicker = YES;
246                         break;
247                         
248                 default:
249                         showDatePicker = NO;
250                         break;
251         }
252         
253         BOOL updateSize = NO;
254         if (showDatePicker && [datePicker isHidden]) {
255                 [datePicker setHidden:NO];              
256                 updateSize = YES;
257                 
258         } else if (!showDatePicker && ![datePicker isHidden]) {
259                 [datePicker setHidden:YES];
260                 updateSize = YES;
261         }
262         
263         if (updateSize) {
264                 NSEnumerator *enumerator = [[[[self window] toolbar] items] objectEnumerator];
265                 NSToolbarItem *toolbarItem;
266                 while ((toolbarItem = [enumerator nextObject])) {
267                         if ([[toolbarItem itemIdentifier] isEqualToString:DATE_ITEM_IDENTIFIER]) {
268                                 NSSize newSize = NSMakeSize(([datePicker isHidden] ? 180 : 290), NSHeight([view_DatePicker frame]));
269                                 [toolbarItem setMinSize:newSize];
270                                 [toolbarItem setMaxSize:newSize];
271                                 break;
272                         }
273                 }               
274         }
277 - (NSString *)dateItemNibName
279         return @"LogViewerDateFilter";
282 - (void)dealloc
284         [filterDate release]; filterDate = nil;
285         [currentSearchLock release]; currentSearchLock = nil;
287         [super dealloc];
290 @end