/* HBLanguagesSelection.m $ This file is part of the HandBrake source code. Homepage: . It may be used under the terms of the GNU General Public License. */ #import "HBLanguagesSelection.h" #include "lang.h" @implementation HBLang - (instancetype)init { @throw nil; } - (instancetype)initWithLanguage:(NSString *)value iso639_2code:(NSString *)code { self = [super init]; if (self) { _language = value; _iso639_2 = code; } return self; } - (instancetype)copyWithZone:(NSZone *)zone { HBLang *lang = [[self class] allocWithZone:zone]; lang->_isSelected = self.isSelected; lang->_language = self.language; lang->_iso639_2 = self.iso639_2; return lang; } - (void)setIsSelected:(BOOL)isSelected { if (_isSelected != isSelected) { [[self.undo prepareWithInvocationTarget:self] setIsSelected:_isSelected]; } _isSelected = isSelected; } - (NSString *)description { return self.language; } @end @implementation HBLanguagesSelection - (instancetype)init { self = [self initWithLanguages:@[]]; return self; } - (instancetype)initWithLanguages:(NSArray *)languages { self = [super init]; if (self) { NSMutableArray *internal = [[NSMutableArray alloc] init]; NSMutableArray *selected = [[NSMutableArray alloc] init]; const iso639_lang_t *lang = lang_get_next(NULL); for (lang = lang_get_next(lang); lang != NULL; lang = lang_get_next(lang)) { NSString *nativeLanguage = strlen(lang->native_name) ? @(lang->native_name) : @(lang->eng_name); HBLang *item = [[HBLang alloc] initWithLanguage:nativeLanguage iso639_2code:@(lang->iso639_2)]; if ([languages containsObject:item.iso639_2]) { item.isSelected = YES; [selected addObject:item]; } else { [internal addObject:item]; } } // Add the (Any) item. HBLang *item = [[HBLang alloc] initWithLanguage:NSLocalizedString(@"(Any)", @"Language selection") iso639_2code:@"und"]; if ([languages containsObject:item.iso639_2]) { item.isSelected = YES; } [internal insertObject:item atIndex:0]; // Insert the selected items // in the original order. [selected sortUsingComparator:^NSComparisonResult(id obj1, id obj2) { return [languages indexOfObject:[obj1 iso639_2]] - [languages indexOfObject:[obj2 iso639_2]]; }]; [internal insertObjects:selected atIndexes:[NSIndexSet indexSetWithIndexesInRange:(NSMakeRange(1, selected.count))]]; _languagesArray = internal; } return self; } - (NSArray *)selectedLanguages { NSMutableArray *selected = [[NSMutableArray alloc] init]; for (HBLang *lang in self.languagesArray) { if (lang.isSelected) { [selected addObject:lang.iso639_2]; } } return [selected copy]; } - (void)setUndo:(NSUndoManager *)undo { _undo = undo; for (HBLang *lang in self.languagesArray) { lang.undo = undo; } } @end NSString *kHBLanguagesDragRowsType = @"kHBLanguagesDragRowsType"; @implementation HBLanguageArrayController - (void)setShowSelectedOnly:(BOOL)showSelectedOnly { _showSelectedOnly = showSelectedOnly; [self rearrangeObjects]; } - (NSArray *)arrangeObjects:(NSArray *)objects { if (!self.showSelectedOnly) { return [super arrangeObjects:objects]; } // Returns a filtered array with only the selected items NSMutableArray *filteredObjects = [NSMutableArray arrayWithCapacity:[objects count]]; for (id item in objects) { if ([[item valueForKeyPath:@"isSelected"] boolValue]) { [filteredObjects addObject:item]; } } return [super arrangeObjects:filteredObjects]; } - (void)awakeFromNib { [self.tableView registerForDraggedTypes:@[kHBLanguagesDragRowsType]]; self.isDragginEnabled = YES; } #pragma mark - NSTableView Delegate - (BOOL)tableView:(NSTableView *)tableView writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard *)pboard { if (self.isDragginEnabled) { NSData *data = nil; if (self.showSelectedOnly) { // If the show selected only filter // is enabled, we need to modify the rowIndexes // to match the position of the elements in the unfiltered array NSArray *filteredArray = [[self arrangedObjects] copy]; self.showSelectedOnly = NO; NSArray *unfilteredArray = [self arrangedObjects]; NSMutableIndexSet *unfilteredIndexes = [NSMutableIndexSet indexSet]; NSUInteger currentIndex = [rowIndexes firstIndex]; while (currentIndex != NSNotFound) { NSUInteger newIndex = [unfilteredArray indexOfObject:filteredArray[currentIndex]]; [unfilteredIndexes addIndex:newIndex]; currentIndex = [rowIndexes indexGreaterThanIndex:currentIndex]; } data = [NSKeyedArchiver archivedDataWithRootObject:unfilteredIndexes]; } else { data = [NSKeyedArchiver archivedDataWithRootObject:rowIndexes]; } [pboard declareTypes:@[kHBLanguagesDragRowsType] owner:self]; [pboard setData:data forType:kHBLanguagesDragRowsType]; } return self.isDragginEnabled; } - (NSDragOperation)tableView:(NSTableView *)view validateDrop:(id )info proposedRow:(NSInteger)row proposedDropOperation:(NSTableViewDropOperation)operation { NSDragOperation dragOp = NSDragOperationNone; // if drag source is our own table view, it's a move or a copy if ([info draggingSource] == view) { // At a minimum, allow move dragOp = NSDragOperationMove; } [view setDropRow:row dropOperation:NSTableViewDropAbove]; return dragOp; } - (BOOL)tableView:(NSTableView *)view acceptDrop:(id )info row:(NSInteger)row dropOperation:(NSTableViewDropOperation)operation { if (([info draggingSourceOperationMask] == NSDragOperationCopy)) { return NO; } if ([info draggingSource] == view) { // Get the index set from the pasteBoard. NSData *rowData = [[info draggingPasteboard] dataForType:kHBLanguagesDragRowsType]; NSIndexSet *indexSet = [NSKeyedUnarchiver unarchiveObjectWithData:rowData]; NSUInteger i = [indexSet countOfIndexesInRange:NSMakeRange(0, row)]; // Rearrage the objects. [self moveObjectsInArrangedObjectsFromIndexes:indexSet toIndex:row]; // Update the selection. row -= i; NSIndexSet *selectionSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(row, [indexSet count])]; [view selectRowIndexes:selectionSet byExtendingSelection:NO]; return YES; } return NO; } - (void)moveObjectsInArrangedObjectsFromIndexes:(NSIndexSet *)indexSet toIndex:(NSUInteger)insertIndex { NSArray *objects = [self arrangedObjects]; NSInteger aboveInsertIndexCount = 0; NSInteger removeIndex; NSUInteger thisIndex = [indexSet lastIndex]; while (thisIndex != NSNotFound) { if (thisIndex >= insertIndex) { removeIndex = thisIndex + aboveInsertIndexCount; aboveInsertIndexCount += 1; } else { removeIndex = thisIndex; insertIndex -= 1; } // Get the object we're moving id object = objects[removeIndex]; // Move the object [self removeObjectAtArrangedObjectIndex:removeIndex]; [self insertObject:object atArrangedObjectIndex:insertIndex]; thisIndex = [indexSet indexLessThanIndex:thisIndex]; } } @end