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