diff --git a/AppDelegate.m b/AppDelegate.m index a89a93a..4df540e 100755 --- a/AppDelegate.m +++ b/AppDelegate.m @@ -1641,8 +1641,11 @@ bail: @synchronized(self.taskTableController.filteredItems) { + //remove prev + [self.taskTableController.filteredItems removeAllObjects]; + //normal filter - [self.filterObj filterTasks:search.string items:self.taskEnumerator.tasks results:self.taskTableController.filteredItems]; + [self.filterObj filterTasks:search.string items:self.taskEnumerator.tasks results:self.taskTableController.filteredItems pane:PANE_TOP]; } @@ -1679,6 +1682,9 @@ bail: //get segment tag segmentTag = [[self.bottomPaneBtn selectedCell] tagForSegment:[self.bottomPaneBtn selectedSegment]]; + //remove all filtered tasks + [self.bottomViewController.filteredItems removeAllObjects]; + //get item // ->will bail if item isn't in (current) view, etc switch(segmentTag) @@ -1687,7 +1693,7 @@ bail: case DYLIBS_VIEW: { //normal filter - [self.filterObj filterFiles:search.string items:self.currentTask.dylibs results:self.bottomViewController.filteredItems]; + [self.filterObj filterFiles:search.string items:self.currentTask.dylibs results:self.bottomViewController.filteredItems pane:PANE_BOTTOM]; break; } @@ -1696,7 +1702,7 @@ bail: case FILES_VIEW: { //filter - [self.filterObj filterFiles:search.string items:self.currentTask.files results:self.bottomViewController.filteredItems]; + [self.filterObj filterFiles:search.string items:self.currentTask.files results:self.bottomViewController.filteredItems pane:PANE_BOTTOM]; break; } @@ -1882,7 +1888,7 @@ bail: } } //bottom pane (dylibs, files, etc) - else + else if(PANE_BOTTOM == pane) { //always reload item (bottom) pane [self.bottomViewController.itemView reloadData]; @@ -2200,6 +2206,12 @@ bail: //filter string NSString* filterString = nil; + //tag + NSUInteger segmentTag = 0; + + //alert box + NSAlert* noKeywordsAlert = nil; + //extract filter filterString = textView.textStorage.string; @@ -2219,7 +2231,7 @@ bail: [self finalizeFiltration:PANE_TOP]; //filter - [self.filterObj filterTasks:filterString items:self.taskEnumerator.tasks results:self.taskTableController.filteredItems]; + [self.filterObj filterTasks:filterString items:self.taskEnumerator.tasks results:self.taskTableController.filteredItems pane:PANE_TOP]; } //refresh/update UI when not a keyword search @@ -2234,10 +2246,29 @@ bail: // ->just dylibs else if(textView == self.customItemsFilter) { + //get segment tag + segmentTag = [[self.bottomPaneBtn selectedCell] tagForSegment:[self.bottomPaneBtn selectedSegment]]; + + //check if keyword was entered for files/networks + if(segmentTag != DYLIBS_VIEW) + { + //alloc/init alert + noKeywordsAlert = [NSAlert alertWithMessageText:@"#keywords not (yet) supported for files/connections" defaultButton:@"Ok" alternateButton:nil otherButton:nil informativeTextWithFormat:@""]; + + //and show it + [noKeywordsAlert runModal]; + + //reset search + self.filterItemsBox.stringValue = @""; + + //bail + goto bail; + } + //set flag self.bottomViewController.isFiltered = YES; - @synchronized (self.bottomViewController.filteredItems) + @synchronized(self.bottomViewController.filteredItems) { //remove all filtered tasks [self.bottomViewController.filteredItems removeAllObjects]; @@ -2246,7 +2277,7 @@ bail: [self finalizeFiltration:PANE_BOTTOM]; //filter - [self.filterObj filterFiles:filterString items:self.currentTask.dylibs results:self.bottomViewController.filteredItems]; + [self.filterObj filterFiles:filterString items:self.currentTask.dylibs results:self.bottomViewController.filteredItems pane:PANE_BOTTOM]; } //refresh/update UI when not a keyword search diff --git a/Consts.h b/Consts.h index 7e68dfe..a42b3ca 100644 --- a/Consts.h +++ b/Consts.h @@ -179,6 +179,9 @@ //bottom pane #define PANE_BOTTOM 0x1 +//search pane +#define PANE_SEARCH 0x2 + //for prefs //#define PREF_FIRST_RUN @"isFirstRun" @@ -311,8 +314,9 @@ //delta for pid tag #define PID_TAG_DELTA 1000 +//TODO: change back //search wait time (from app's launch) -#define SEARCH_WAIT_TIME 45 +#define SEARCH_WAIT_TIME 10 //pls wait (search) message #define PLS_WAIT_MESSAGE @"completing (initial) task/dylib/file enumeration please wait" diff --git a/Filter.h b/Filter.h index 167483b..8f85122 100644 --- a/Filter.h +++ b/Filter.h @@ -46,11 +46,15 @@ //keyword filter '#notfound' -(BOOL)notFound:(Binary*)item; +//filter all for global search +// ->tasks, dylibs, files, & connections +-(void)filterAll:(NSString*)filterText items:(NSMutableDictionary*)items results:(NSMutableArray*)results; + //filter tasks --(void)filterTasks:(NSString*)filterText items:(NSMutableDictionary*)items results:(NSMutableArray*)results; +-(void)filterTasks:(NSString*)filterText items:(NSMutableDictionary*)items results:(NSMutableArray*)results pane:(NSUInteger)pane; //filter dylibs and files --(void)filterFiles:(NSString*)filterText items:(NSMutableArray*)items results:(NSMutableArray*)results; +-(void)filterFiles:(NSString*)filterText items:(NSMutableArray*)items results:(NSMutableArray*)results pane:(NSUInteger)pane; //filter network connections -(void)filterConnections:(NSString*)filterText items:(NSMutableArray*)items results:(NSMutableArray*)results; diff --git a/Filter.m b/Filter.m index 3a2ab04..bf14012 100644 --- a/Filter.m +++ b/Filter.m @@ -29,9 +29,6 @@ NSString * const BINARY_KEYWORDS[] = {@"#apple", @"#nonapple", @"#signed", @"#un self = [super init]; if(nil != self) { - //alloc file filter keywords - //fileFilters = [NSMutableArray array]; - //alloc binary filter keywords binaryFilters = [NSMutableArray array]; @@ -53,35 +50,121 @@ NSString * const BINARY_KEYWORDS[] = {@"#apple", @"#nonapple", @"#signed", @"#un return [self.binaryFilters containsObject:searchString]; } +//filter all for global search +// ->tasks, dylibs, files, & connections +-(void)filterAll:(NSString*)filterText items:(NSMutableDictionary*)items results:(NSMutableArray*)results +{ + //flag + BOOL isKeyword = NO; + + //matching tasks + NSMutableArray* matchingTasks = nil; + + //matching dylibs + NSMutableArray* matchingDylibs = nil; + + //matching files + NSMutableArray* matchingFiles = nil; + + //matching connections + NSMutableArray* matchingConnections = nil; + + //alloc array for matching tasks + matchingTasks = [NSMutableArray array]; + + //alloc array for matching dylibs + matchingDylibs = [NSMutableArray array]; + + //alloc dictionary for matching files + matchingFiles = [NSMutableArray array]; + + //alloc dictionary for matching connections + matchingConnections = [NSMutableArray array]; + + //set flag + isKeyword = [self isKeyword:filterText]; + + //filter all tasks + [self filterTasks:filterText items:items results:matchingTasks pane:PANE_SEARCH]; + + //iterate over all tasks + // ->for each, filter their dylib, files, etc + for(Task* task in items.allValues) + { + //filter dylibs + [self filterFiles:filterText items:task.dylibs results:matchingDylibs pane:PANE_SEARCH]; + + //when keyword search + // ->skip files/connections + if(YES == isKeyword) + { + //skip + continue; + } + + //filter files + [self filterFiles:filterText items:task.files results:matchingFiles pane:PANE_SEARCH]; + + //filter connections + [self filterFiles:filterText items:task.connections results:matchingConnections pane:PANE_SEARCH]; + } + + //remove duplicate dylibs + [results setArray:[[[NSSet setWithArray:results] allObjects] mutableCopy]]; + + //call back into search object to refresh it's UI and show results + dispatch_async(dispatch_get_main_queue(), ^{ + + //search done! + [((AppDelegate*)[[NSApplication sharedApplication] delegate]).searchWindowController completeSearch]; + + }); + + return; +} + //filter tasks // ->name, path, pid --(void)filterTasks:(NSString*)filterText items:(NSMutableDictionary*)items results:(NSMutableArray*)results +-(void)filterTasks:(NSString*)filterText items:(NSMutableDictionary*)items results:(NSMutableArray*)results pane:(NSUInteger)pane { //task Task* task = nil; - //first reset any previous filter'd items - [results removeAllObjects]; - //process #keyword filtering // ->note: already checked its a full/matching keyword if(YES == [filterText hasPrefix:@"#"]) { //prep UI for filtering // ->config/show overlay, etc - [((AppDelegate*)[[NSApplication sharedApplication] delegate]) prepUIForFiltering:PANE_TOP]; + if(PANE_SEARCH != pane) + { + //prep UI + [((AppDelegate*)[[NSApplication sharedApplication] delegate]) prepUIForFiltering:pane]; + } - //in background filter by keyword - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - - //nap a 1/2second to let message show up - [NSThread sleepForTimeInterval:0.5]; - + //when main thread + // ->do #keyword in background + if(YES == [NSThread isMainThread]) + { + //in background filter by keyword + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + + //nap a 1/2second to let message show up + [NSThread sleepForTimeInterval:0.5]; + + //filter + // ->calls back in UI to refresh when done! + [self filterByKeyword:(NSString*)filterText items:items.allValues results:(NSMutableArray*)results pane:pane]; + + }); + } + //already in background + // ->just run on current thread + else + { //filter - // ->calls back in UI to refresh when done! - [self filterByKeyword:(NSString*)filterText items:items.allValues results:(NSMutableArray*)results]; - - }); + [self filterByKeyword:(NSString*)filterText items:items.allValues results:(NSMutableArray*)results pane:pane]; + } } //not keyword @@ -142,11 +225,8 @@ NSString * const BINARY_KEYWORDS[] = {@"#apple", @"#nonapple", @"#signed", @"#un //filter tasks // ->name, path, pid --(void)filterByKeyword:(NSString*)filterText items:(NSArray*)items results:(NSMutableArray*)results +-(void)filterByKeyword:(NSString*)filterText items:(NSArray*)items results:(NSMutableArray*)results pane:(NSUInteger)pane { - //pane - NSUInteger pane = PANE_TOP; - //sync @synchronized(items) { @@ -160,9 +240,6 @@ NSString * const BINARY_KEYWORDS[] = {@"#apple", @"#nonapple", @"#signed", @"#un //handle tasks if(YES == [items.firstObject isKindOfClass:[Task class]]) { - //set pane - pane = PANE_TOP; - //iterate over all items for(id item in items) { @@ -177,9 +254,6 @@ NSString * const BINARY_KEYWORDS[] = {@"#apple", @"#nonapple", @"#signed", @"#un //handle dylibs else if(YES == [items.firstObject isKindOfClass:[Binary class]]) { - //set pane - pane = PANE_BOTTOM; - //iterate over all items for(id item in items) { @@ -195,12 +269,16 @@ NSString * const BINARY_KEYWORDS[] = {@"#apple", @"#nonapple", @"#signed", @"#un } //sync //tell the UI we are done - dispatch_async(dispatch_get_main_queue(), ^{ - - //finalize UI - [((AppDelegate*)[[NSApplication sharedApplication] delegate]) finalizeFiltration:pane]; - - }); + if(PANE_SEARCH != pane) + { + //on main thread, update UI + dispatch_async(dispatch_get_main_queue(), ^{ + + //finalize UI + [((AppDelegate*)[[NSApplication sharedApplication] delegate]) finalizeFiltration:pane]; + + }); + } //bail bail: @@ -210,11 +288,8 @@ bail: //filter dylibs and files // ->name and path --(void)filterFiles:(NSString*)filterText items:(NSMutableArray*)items results:(NSMutableArray*)results +-(void)filterFiles:(NSString*)filterText items:(NSMutableArray*)items results:(NSMutableArray*)results pane:(NSUInteger)pane { - //first reset filter'd items - [results removeAllObjects]; - //process #keyword filtering for dylibs // ->note: already checked its a full/matching keyword if( (YES == [filterText hasPrefix:@"#"]) && @@ -222,19 +297,35 @@ bail: { //prep UI for filtering // ->config/show overlay, etc - [((AppDelegate*)[[NSApplication sharedApplication] delegate]) prepUIForFiltering:PANE_BOTTOM]; + if(PANE_SEARCH != pane) + { + //prep UI + [((AppDelegate*)[[NSApplication sharedApplication] delegate]) prepUIForFiltering:pane]; + } - //in background filter by keyword - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - - //nap a 1/2second to let message show up - [NSThread sleepForTimeInterval:0.5]; - + //when main thread + // ->do #keyword search in background + if(YES == [NSThread isMainThread]) + { + //in background filter by keyword + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + + //nap a 1/2second to let message show up + [NSThread sleepForTimeInterval:0.5]; + + //filter + // ->calls back in UI to refresh when done! + [self filterByKeyword:(NSString*)filterText items:items results:(NSMutableArray*)results pane:pane]; + + }); + } + //already in background + // ->just run on current thread + else + { //filter - // ->calls back in UI to refresh when done! - [self filterByKeyword:(NSString*)filterText items:items results:(NSMutableArray*)results]; - - }); + [self filterByKeyword:(NSString*)filterText items:items results:(NSMutableArray*)results pane:pane]; + } } //not keyword @@ -283,9 +374,6 @@ bail: //filter network connections -(void)filterConnections:(NSString*)filterText items:(NSMutableArray*)items results:(NSMutableArray*)results { - //first reset filter'd items - [results removeAllObjects]; - //sync @synchronized(items) { diff --git a/RequestRootWindowController.m b/RequestRootWindowController.m index e0bc4a8..05577c9 100644 --- a/RequestRootWindowController.m +++ b/RequestRootWindowController.m @@ -117,6 +117,9 @@ //end w/ NULL installArgs[3] = NULL; + //reset text color + self.statusMsg.textColor = [NSColor blackColor]; + //create authorization ref osStatus = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authorizationRef); if(errAuthorizationSuccess != osStatus) diff --git a/SearchWindowController.h b/SearchWindowController.h index 08fa153..88b0860 100644 --- a/SearchWindowController.h +++ b/SearchWindowController.h @@ -69,5 +69,8 @@ //search -(void)search; +//callback for when searching is done +// ->update UI by removing overlay and reloading table +-(void)completeSearch; @end diff --git a/SearchWindowController.m b/SearchWindowController.m index 5ff6e35..3eb839b 100644 --- a/SearchWindowController.m +++ b/SearchWindowController.m @@ -145,36 +145,9 @@ //calc time remaining timeRemaining = SEARCH_WAIT_TIME - (currentTime - ((AppDelegate*)[[NSApplication sharedApplication] delegate]).startTime); - //pre-req - [self.overlayView setWantsLayer:YES]; - - //rounded corners - self.overlayView.layer.cornerRadius = 20.0; - - //maks - self.overlayView.layer.masksToBounds = YES; - - //set overlay's view color to black - self.overlayView.layer.backgroundColor = [NSColor blackColor].CGColor; - - //make it semi-transparent - self.overlayView.alphaValue = 0.20; - //show overlay - self.overlayView.hidden = NO; - - //disable search box - self.searchBox.enabled = NO; - - //show activity indicator - [self.activityIndicator setHidden:NO]; - - //show activity indicator label - self.activityIndicatorLabel.hidden = NO; - - //start spinner - [self.activityIndicator startAnimation:nil]; - + [self showOverlay]; + //update w/ time self.activityIndicatorLabel.stringValue = [NSString stringWithFormat:@"%@ (%d)", PLS_WAIT_MESSAGE, (int)timeRemaining]; @@ -185,6 +158,64 @@ return; } +//display the overlay +-(void)showOverlay +{ + //pre-req + [self.overlayView setWantsLayer:YES]; + + //rounded corners + self.overlayView.layer.cornerRadius = 20.0; + + //maks + self.overlayView.layer.masksToBounds = YES; + + //set overlay's view color to black + self.overlayView.layer.backgroundColor = [NSColor blackColor].CGColor; + + //make it semi-transparent + self.overlayView.alphaValue = 0.20; + + //show overlay + self.overlayView.hidden = NO; + + //disable search box + self.searchBox.enabled = NO; + + //show activity indicator + [self.activityIndicator setHidden:NO]; + + //show activity indicator label + self.activityIndicatorLabel.hidden = NO; + + //start spinner + [self.activityIndicator startAnimation:nil]; + + return; +} + +//callback for when searching is done +// ->update UI by removing overlay and reloading table +-(void)completeSearch +{ + //hide overlay + self.overlayView.hidden = YES; + + //hide activity indicator + self.activityIndicator.hidden = YES; + + //hide activity indicator label + self.activityIndicatorLabel.hidden = YES; + + //enable search box + self.searchBox.enabled = YES; + + //reload table + [self.searchTable reloadData]; + + return; +} + //thread function // ->wait until task enumeration is done, then allow user to search -(void)waitTillPau @@ -479,7 +510,7 @@ bail: return; } -//search +//do the search -(void)search { //search string @@ -488,39 +519,6 @@ bail: //all tasks OrderedDictionary* allTasks = nil; - //matching items - NSMutableArray* matchingItems = nil; - - //matching tasks - NSMutableArray* matchingTasks = nil; - - //matching dylibs - NSMutableDictionary* matchingDylibs = nil; - - //matching files - NSMutableDictionary* matchingFiles = nil; - - //matching connections - NSMutableDictionary* matchingConnections = nil; - - //task - Task* task = nil; - - //alloc array for matching items - matchingItems = [NSMutableArray array]; - - //alloc array for matching tasks - matchingTasks = [NSMutableArray array]; - - //alloc dictionary for matching dylibs - matchingDylibs = [NSMutableDictionary dictionary]; - - //alloc dictionary for matching files - matchingFiles = [NSMutableDictionary dictionary]; - - //alloc dictionary for matching connections - matchingConnections = [NSMutableDictionary dictionary]; - //reset search results [self.searchResults removeAllObjects]; @@ -531,166 +529,32 @@ bail: //grab search string searchString = [self.searchBox stringValue]; + //show overlay + [self showOverlay]; + + //config it + self.activityIndicatorLabel.stringValue = [NSString stringWithFormat:@"searching (%@)", searchString]; + + //disable search input box + self.searchBox.enabled = NO; + //grab all tasks - allTasks = ((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.tasks; + // ->make copy to avoid threading issues + allTasks = [((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.tasks copy]; - //'#' indicates a keyword search - // ->check for keyword match, then filter by keyword - if(YES == [searchString hasPrefix:@"#"]) - { - //ignore #search strings that don't match a keyword - if(YES != [filterObj isKeyword:searchString]) - { - //ignore - goto bail; - } - } - - //1st: search for all matching tasks - - //search for all matching tasks - [self.filterObj filterTasks:searchString items:allTasks results:matchingTasks]; - - //add all tasks - [self.searchResults addObjectsFromArray:matchingTasks]; - - //refresh table to display task matches - [self.searchTable reloadData]; - - //2nd: search for all matching dylibs - - //sync - @synchronized(allTasks) - { - //reset - [matchingItems removeAllObjects]; - - //walk all tasks - // ->scan each for dylib matches, only processing first match - for(NSNumber* taskPid in allTasks) - { - //extract task - task = allTasks[taskPid]; - - //filter - [self.filterObj filterFiles:searchString items:task.dylibs results:matchingItems]; - - //process all matching dylibs - // ->but first check if processed due to matching in another task already - for(Binary* dylib in matchingItems) - { - //ignore if already seen/processed - if(nil != matchingDylibs[dylib.path]) - { - //skip - continue; - } - - //process - [self.searchResults addObject:dylib]; - - //save - matchingDylibs[dylib.path] = dylib; - } - - }//all tasks + //kick off filtering in background + // ->will call back into to refresh UI when done + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - }//sync + //nap to allow message to show + [NSThread sleepForTimeInterval:0.5]; - //refresh table to display dylib - [self.searchTable reloadData]; - - //3rd: search for all matching files - - //sync - @synchronized(allTasks) - { - //reset - [matchingItems removeAllObjects]; + //filter + [self.filterObj filterAll:searchString items:allTasks results:self.searchResults]; - //walk all tasks - // ->scan each for file matches, only processing first match - for(NSNumber* taskPid in allTasks) - { - //extract task - task = allTasks[taskPid]; - - //filter - [self.filterObj filterFiles:searchString items:task.files results:matchingItems]; - - //process all matching dylibs - // ->but first check if processed due to matching in another task already - for(File* file in matchingItems) - { - //ignore if already seen/processed - if(nil != matchingFiles[file.path]) - { - //skip - continue; - } - - //process - [self.searchResults addObject:file]; - - //save - matchingFiles[file.path] = file; - } - - }//all tasks - - }//sync - - //refresh table to display dylib - [self.searchTable reloadData]; - - //4th: search for all matching network comms - - //sync - @synchronized(allTasks) - { - //reset - [matchingItems removeAllObjects]; - - //walk all tasks - // ->scan each for connections matches, only processing first match - for(NSNumber* taskPid in allTasks) - { - //extract task - task = allTasks[taskPid]; - - //filter - [self.filterObj filterConnections:searchString items:task.connections results:matchingItems]; - - //process all matching connections - // ->but first check if processed due to matching in another task already - for(Connection* connection in matchingItems) - { - //ignore if already seen/processed - if(nil != matchingConnections[connection.endpoints]) - { - //skip - continue; - } - - //process - [self.searchResults addObject:connection]; - - //save - matchingConnections[connection.endpoints] = connection; - } - - }//all tasks - - }//sync - - //refresh table to display dylib - [self.searchTable reloadData]; - -//bail -bail: + }); return; - } //invoked when the user clicks 'virus total' icon diff --git a/TaskEnumerator.m b/TaskEnumerator.m index c9c3832..0486ded 100644 --- a/TaskEnumerator.m +++ b/TaskEnumerator.m @@ -23,7 +23,6 @@ @implementation TaskEnumerator -//@synthesize files; @synthesize tasks; @synthesize dylibs; @synthesize binaryQueue; diff --git a/TaskExplorer.xcodeproj/project.xcworkspace/xcuserdata/patrickw.xcuserdatad/UserInterfaceState.xcuserstate b/TaskExplorer.xcodeproj/project.xcworkspace/xcuserdata/patrickw.xcuserdatad/UserInterfaceState.xcuserstate index ce4eb58..cdd30a6 100644 Binary files a/TaskExplorer.xcodeproj/project.xcworkspace/xcuserdata/patrickw.xcuserdatad/UserInterfaceState.xcuserstate and b/TaskExplorer.xcodeproj/project.xcworkspace/xcuserdata/patrickw.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/UI/FlatView.xib b/UI/FlatView.xib index 71f9556..a731cf1 100755 --- a/UI/FlatView.xib +++ b/UI/FlatView.xib @@ -1,10 +1,11 @@ - + - + + @@ -66,10 +67,7 @@ - - - - + @@ -103,7 +101,7 @@ - + @@ -175,7 +173,7 @@ - + @@ -184,7 +182,7 @@ - + @@ -204,9 +202,8 @@ - - - + + @@ -303,6 +300,9 @@ + + + @@ -328,10 +328,7 @@ - - - - + @@ -417,6 +414,7 @@ + @@ -435,7 +433,7 @@ - + @@ -446,10 +444,7 @@ - - - - + @@ -486,12 +481,13 @@ + - + @@ -512,7 +508,7 @@ - + @@ -528,7 +524,7 @@ - + diff --git a/UI/SearchWindow.xib b/UI/SearchWindow.xib index b661ad8..dcbc83a 100644 --- a/UI/SearchWindow.xib +++ b/UI/SearchWindow.xib @@ -1,10 +1,11 @@ - + - + + @@ -415,6 +416,10 @@ + + + + @@ -442,7 +447,6 @@ - diff --git a/en.lproj/MainMenu.xib b/en.lproj/MainMenu.xib index f823768..413931e 100755 --- a/en.lproj/MainMenu.xib +++ b/en.lproj/MainMenu.xib @@ -77,7 +77,7 @@