/* 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:@"(Any)"
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