// // SKInfoWindowController.m // Skim // // Created by Christiaan Hofman on 12/17/06. /* This software is Copyright (c) 2006-2018 Christiaan Hofman. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Christiaan Hofman nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import "SKInfoWindowController.h" //#import "SKMainDocument.h" //#import "NSDocument_SKExtensions.h" #import "PDFPage_SKExtensions.h" #import //#import "SKMainWindowController.h" #if VERSION_DMG #import #else #import #endif #define SKInfoWindowFrameAutosaveName @"SKInfoWindow" #define SKInfoVersionKey @"Version" #define SKInfoPageCountKey @"PageCount" #define SKInfoPageSizeKey @"PageSize" #define SKInfoPageWidthKey @"PageWidth" #define SKInfoPageHeightKey @"PageHeight" #define SKInfoKeywordsStringKey @"KeywordsString" #define SKInfoEncryptedKey @"Encrypted" #define SKInfoAllowsPrintingKey @"AllowsPrinting" #define SKInfoAllowsCopyingKey @"AllowsCopying" #define SKInfoAllowsDocumentAssemblyKey @"AllowsDocumentAssembly" #define SKInfoAllowsContentAccessibilityKey @"AllowsContentAccessibility" #define SKInfoAllowsCommentingKey @"AllowsCommenting" #define SKInfoAllowsFormFieldEntryKey @"AllowsFormFieldEntry" #define SKInfoFileNameKey @"FileName" #define SKInfoFileSizeKey @"FileSize" #define SKInfoPhysicalSizeKey @"PhysicalSize" #define SKInfoLogicalSizeKey @"LogicalSize" #define SKInfoTagsKey @"Tags" #define SKInfoRatingKey @"Rating" #define LABEL_COLUMN_ID @"label" #define VALUE_COLUMN_ID @"value" NSString * const kKMWindowDidBecomeMainNotificationName = @"KMWindowDidBecomeMainNotificationName"; @interface SKInfoWindowController (SKPrivate) - (void)handleViewFrameDidChangeNotification:(NSNotification *)notification; - (void)handleWindowDidBecomeMainNotification:(NSNotification *)notification; - (void)handleWindowDidResignMainNotification:(NSNotification *)notification; - (void)handlePDFDocumentInfoDidChangeNotification:(NSNotification *)notification; - (void)handleDocumentFileURLDidChangeNotification:(NSNotification *)notification; @end @implementation SKInfoWindowController @synthesize summaryTableView, attributesTableView, tabView, info, editDictionary; @dynamic keys; static SKInfoWindowController *sharedInstance = nil; + (id)sharedInstance { @synchronized (self) { if (sharedInstance == nil) { sharedInstance = [[super allocWithZone:NULL] init]; } } return sharedInstance; } + (instancetype)allocWithZone:(struct _NSZone *)zone { return [self sharedInstance]; } - (id)init { if (sharedInstance) NSLog(@"Attempt to allocate second instance of %@", [self class]); self = [super initWithWindowNibName:@"InfoWindow"]; if (self){ info = nil; if (@available(macOS 10.13, *)) { summaryKeys = [[NSArray alloc] initWithObjects: SKInfoFileNameKey, SKInfoFileSizeKey, SKInfoPageSizeKey, SKInfoPageCountKey, SKInfoVersionKey, @"", SKInfoEncryptedKey, SKInfoAllowsPrintingKey, SKInfoAllowsCopyingKey, SKInfoAllowsDocumentAssemblyKey, SKInfoAllowsContentAccessibilityKey, SKInfoAllowsCommentingKey, SKInfoAllowsFormFieldEntryKey,nil]; } else { summaryKeys = [[NSArray alloc] initWithObjects: SKInfoFileNameKey, SKInfoFileSizeKey, SKInfoPageSizeKey, SKInfoPageCountKey, SKInfoVersionKey, @"", SKInfoEncryptedKey, SKInfoAllowsPrintingKey, SKInfoAllowsCopyingKey, nil]; } attributesKeys = [[NSArray alloc] initWithObjects: PDFDocumentTitleAttribute, PDFDocumentAuthorAttribute, PDFDocumentSubjectAttribute, PDFDocumentCreatorAttribute, PDFDocumentProducerAttribute, PDFDocumentCreationDateAttribute, PDFDocumentModificationDateAttribute, SKInfoKeywordsStringKey, nil]; labels = [[NSDictionary alloc] initWithObjectsAndKeys: NSLocalizedString(@"File name:", @"Info label"), SKInfoFileNameKey, NSLocalizedString(@"File size:", @"Info label"), SKInfoFileSizeKey, NSLocalizedString(@"Page size:", @"Info label"), SKInfoPageSizeKey, NSLocalizedString(@"Page count:", @"Info label"), SKInfoPageCountKey, NSLocalizedString(@"PDF Version:", @"Info label"), SKInfoVersionKey, NSLocalizedString(@"Encrypted:", @"Info label"), SKInfoEncryptedKey, NSLocalizedString(@"Printing:", @"Info label"), SKInfoAllowsPrintingKey, NSLocalizedString(@"Content Copying:", @"Info label"), SKInfoAllowsCopyingKey, NSLocalizedString(@"Document Assembly:", @"Info label"), SKInfoAllowsDocumentAssemblyKey, NSLocalizedString(@"Content Copying for Accessibility:", @"Info label"), SKInfoAllowsContentAccessibilityKey, NSLocalizedString(@"Commenting:", @"Info label"), SKInfoAllowsCommentingKey, NSLocalizedString(@"Filling of form fields:", @"Info label"), SKInfoAllowsFormFieldEntryKey, NSLocalizedString(@"Title:", @"Info label"), PDFDocumentTitleAttribute, NSLocalizedString(@"Author:", @"Info label"), PDFDocumentAuthorAttribute, NSLocalizedString(@"Subject:", @"Info label"), PDFDocumentSubjectAttribute, NSLocalizedString(@"Content Creator:", @"Info label"), PDFDocumentCreatorAttribute, NSLocalizedString(@"PDF Producer:", @"Info label"), PDFDocumentProducerAttribute, NSLocalizedString(@"Creation date:", @"Info label"), PDFDocumentCreationDateAttribute, NSLocalizedString(@"Modification date:", @"Info label"), PDFDocumentModificationDateAttribute, NSLocalizedString(@"Keywords:", @"Info label"), SKInfoKeywordsStringKey, nil]; } return self; } - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; } - (void)updateForDocument:(NSDocument *)doc { NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithDictionary:[self infoForDocument:doc]]; for (NSString *key in self.editDictionary.allKeys) { [dic setObject:self.editDictionary[key] forKey:key]; } [self setInfo:dic]; [summaryTableView reloadData]; [attributesTableView reloadData]; } - (void)windowDidLoad { [self updateForDocument:[[[NSApp mainWindow] windowController] document]]; if (@available(macOS 10.13, *)) { } else { [self.window setFrame:CGRectMake(self.window.frame.origin.x, self.window.frame.origin.y, 460, 310) display:YES animate:YES]; } [summaryTableView setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleNone]; [attributesTableView setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleNone]; self.editDictionary = [NSMutableDictionary dictionary]; [self.tabView selectTabViewItemAtIndex:1]; [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(handleViewFrameDidChangeNotification:) name: NSViewFrameDidChangeNotification object: [attributesTableView enclosingScrollView]]; [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(handleWindowDidBecomeMainNotification:) name: NSWindowDidBecomeMainNotification object: nil]; [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(handleWindowDidResignMainNotification:) name: NSWindowDidResignMainNotification object: nil]; [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(handlePDFDocumentInfoDidChangeNotification:) name: PDFDocumentDidUnlockNotification object: nil]; // [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(handlePDFDocumentInfoDidChangeNotification:) // name: SKPDFPageBoundsDidChangeNotification object: nil]; // [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(handleDocumentFileURLDidChangeNotification:) // name: SKDocumentFileURLDidChangeNotification object: nil]; [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(handleWindowWillCloseNotification:) name: NSWindowWillCloseNotification object: nil]; [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(km_handleWindowDidBecomeMainNotification:) name: kKMWindowDidBecomeMainNotificationName object: nil]; } - (void)showWindow:(id)sender { [super showWindow:nil]; [self.editDictionary removeAllObjects]; [self.attributesTableView reloadData]; } #define BYTE_FACTOR 1024 #define BYTE_FACTOR_F 1024.0f #define BYTE_SHIFT 10 static NSString *SKFileSizeStringForFileURL(NSURL *fileURL, unsigned long long *physicalSizePtr, unsigned long long *logicalSizePtr) { if (fileURL == nil) return @""; FSRef fileRef; FSCatalogInfo catalogInfo; unsigned long long size, logicalSize = 0; BOOL gotSize = NO, isDir = NO; NSMutableString *string = [NSMutableString string]; Boolean gotRef = CFURLGetFSRef((CFURLRef)fileURL, &fileRef); if (gotRef && noErr == FSGetCatalogInfo(&fileRef, kFSCatInfoDataSizes | kFSCatInfoRsrcSizes | kFSCatInfoNodeFlags, &catalogInfo, NULL, NULL, NULL)) { size = catalogInfo.dataPhysicalSize + catalogInfo.rsrcPhysicalSize; logicalSize = catalogInfo.dataLogicalSize + catalogInfo.rsrcLogicalSize; isDir = (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) != 0; gotSize = YES; } if (gotSize == NO) { // this seems to give the logical size NSDictionary *fileAttrs = [[NSFileManager defaultManager] attributesOfItemAtPath:[fileURL path] error:NULL]; logicalSize = size = [[fileAttrs objectForKey:NSFileSize] unsignedLongLongValue]; isDir = [[fileAttrs fileType] isEqualToString:NSFileTypeDirectory]; } if (isDir) { NSString *path = [fileURL path]; unsigned long long componentSize; unsigned long long logicalComponentSize; for (NSString *file in [[NSFileManager defaultManager] subpathsOfDirectoryAtPath:path error:NULL]) { SKFileSizeStringForFileURL([NSURL fileURLWithPath:[path stringByAppendingPathComponent:file]], &componentSize, &logicalComponentSize); size += componentSize; logicalSize += logicalComponentSize; } } if (physicalSizePtr) *physicalSizePtr = size; if (logicalSizePtr) *logicalSizePtr = logicalSize; if (size < BYTE_FACTOR) { [string appendFormat:@"%qu %@", size, NSLocalizedString(@"bytes", @"size unit")]; } else { #define numUnits 6 NSString *units[numUnits] = {NSLocalizedString(@"kB", @"size unit"), NSLocalizedString(@"MB", @"size unit"), NSLocalizedString(@"GB", @"size unit"), NSLocalizedString(@"TB", @"size unit"), NSLocalizedString(@"PB", @"size unit"), NSLocalizedString(@"EB", @"size unit")}; NSUInteger i; for (i = 0; i < numUnits; i++, size >>= BYTE_SHIFT) { if ((size >> BYTE_SHIFT) < BYTE_FACTOR || i == numUnits - 1) { [string appendFormat:@"%.1f %@", size / BYTE_FACTOR_F, units[i]]; break; } } } NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; [formatter setFormatterBehavior:NSNumberFormatterBehavior10_4]; [formatter setNumberStyle:NSNumberFormatterDecimalStyle]; [string appendFormat:@" (%@ %@)", [formatter stringFromNumber:[NSNumber numberWithUnsignedLongLong:logicalSize]], NSLocalizedString(@"bytes", @"size unit")]; return string; } #define CM_PER_POINT 0.035277778 #define INCH_PER_POINT 0.013888889 static inline NSString *SKSizeString(NSSize size, NSSize altSize) { BOOL useMetric = [[[NSLocale currentLocale] objectForKey:NSLocaleUsesMetricSystem] boolValue]; NSString *units = useMetric ? NSLocalizedString(@"cm", @"size unit") : NSLocalizedString(@"in", @"size unit"); CGFloat factor = useMetric ? CM_PER_POINT : INCH_PER_POINT; if (NSEqualSizes(size, altSize)) return [NSString stringWithFormat:@"%.2f x %.2f %@", size.width * factor, size.height * factor, units]; else return [NSString stringWithFormat:@"%.2f x %.2f %@ (%.2f x %.2f %@)", size.width * factor, size.height * factor, units, altSize.width * factor, altSize.height * factor, units]; } - (NSDictionary *)infoForDocument:(NSDocument *)doc { NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; unsigned long long logicalSize = 0, physicalSize = 0; if ([doc isKindOfClass:KMMainDocument.class]) { CPDFDocument *pdfDoc = ((KMMainDocument *)doc).mainViewController.document; if (pdfDoc != nil) { [dictionary addEntriesFromDictionary:[pdfDoc documentAttributes]]; [dictionary setValue:[NSString stringWithFormat: @"%ld.%ld", (long)[pdfDoc majorVersion], (long)[pdfDoc minorVersion]] forKey:SKInfoVersionKey]; [dictionary setValue:[NSNumber numberWithInteger:[pdfDoc pageCount]] forKey:SKInfoPageCountKey]; if ([pdfDoc pageCount]) { NSSize cropSize = [[pdfDoc pageAtIndex:0] boundsForBox:CPDFDisplayCropBox].size; NSSize mediaSize = [[pdfDoc pageAtIndex:0] boundsForBox:CPDFDisplayMediaBox].size; [dictionary setValue:SKSizeString(cropSize, mediaSize) forKey:SKInfoPageSizeKey]; [dictionary setValue:[NSNumber numberWithDouble:cropSize.width] forKey:SKInfoPageWidthKey]; [dictionary setValue:[NSNumber numberWithDouble:cropSize.height] forKey:SKInfoPageHeightKey]; } NSArray *keyworks = [dictionary valueForKey:CPDFDocumentKeywordsAttribute]; if ([keyworks isKindOfClass:[NSArray class]]) { [dictionary setValue:[[dictionary valueForKey:CPDFDocumentKeywordsAttribute] componentsJoinedByString:@"\n"] forKey:SKInfoKeywordsStringKey]; } [dictionary setValue:[NSNumber numberWithBool:[pdfDoc isEncrypted]] forKey:SKInfoEncryptedKey]; [dictionary setValue:[NSNumber numberWithBool:[pdfDoc allowsPrinting]] forKey:SKInfoAllowsPrintingKey]; [dictionary setValue:[NSNumber numberWithBool:[pdfDoc allowsCopying]] forKey:SKInfoAllowsCopyingKey]; if (@available(macOS 10.13, *)) { // [dictionary setValue:[NSNumber numberWithBool:[pdfDoc allowsContentAccessibility]] forKey:SKInfoAllowsContentAccessibilityKey]; [dictionary setValue:[NSNumber numberWithBool:[pdfDoc allowsCommenting]] forKey:SKInfoAllowsCommentingKey]; [dictionary setValue:[NSNumber numberWithBool:[pdfDoc allowsDocumentAssembly]] forKey:SKInfoAllowsDocumentAssemblyKey]; [dictionary setValue:[NSNumber numberWithBool:[pdfDoc allowsFormFieldEntry]] forKey:SKInfoAllowsFormFieldEntryKey]; } } } [dictionary setValue:[[[doc fileURL] path] lastPathComponent] forKey:SKInfoFileNameKey]; [dictionary setValue:SKFileSizeStringForFileURL([doc fileURL], &physicalSize, &logicalSize) forKey:SKInfoFileSizeKey]; [dictionary setValue:[NSNumber numberWithUnsignedLongLong:physicalSize] forKey:SKInfoPhysicalSizeKey]; [dictionary setValue:[NSNumber numberWithUnsignedLongLong:logicalSize] forKey:SKInfoLogicalSizeKey]; // if ([doc respondsToSelector:@selector(tags)]) // [dictionary setValue:[(KMMainDocument *)doc tags] ?: [NSArray array] forKey:SKInfoTagsKey]; // if ([doc respondsToSelector:@selector(rating)]) // [dictionary setValue:[NSNumber numberWithDouble:[(KMMainDocument *)doc rating]] forKey:SKInfoRatingKey]; return dictionary; } - (NSArray *)keys { return [attributesKeys arrayByAddingObjectsFromArray:summaryKeys]; } - (void)handleWindowWillCloseNotification:(NSNotification *)notification { id object = [notification object]; if ([object isEqual:self.window]) { if (self.editDictionary.count > 0) { NSWindowController *windowController = [[NSApp mainWindow] windowController]; if ([windowController isKindOfClass:[KMBrowserWindowController class]]) { KMMainDocument *document = (KMMainDocument *)[(KMBrowserWindowController *)windowController document]; NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithDictionary:[document.mainViewController.document documentAttributes]]; BOOL isSave = NO; for (NSString *key in self.editDictionary) { if ([key isEqualToString:SKInfoKeywordsStringKey]) { NSString *string = self.editDictionary[key]; NSArray *array = [string componentsSeparatedByString:@"\n"]; if (array && ![dic[PDFDocumentKeywordsAttribute] isEqualToArray:array]) { isSave = YES; [dic setObject:array forKey:PDFDocumentKeywordsAttribute]; } } else { if (![dic[key] isEqualToString:self.editDictionary[key]]) { isSave = YES; [dic setObject:self.editDictionary[key] forKey:key]; } } } if (isSave) { [document.mainViewController.document setDocumentAttributes:dic]; [document saveDocument:nil]; } } } [self.editDictionary removeAllObjects]; } } - (void)handleViewFrameDidChangeNotification:(NSNotification *)notification { [attributesTableView noteHeightOfRowsWithIndexesChanged:[NSIndexSet indexSetWithIndex:[attributesKeys count] - 1]]; } - (void)handleWindowDidBecomeMainNotification:(NSNotification *)notification { [self.editDictionary removeAllObjects]; [self updateForDocument:[[[notification object] windowController] document]]; } - (void)km_handleWindowDidBecomeMainNotification:(NSNotification *)notification { [self.editDictionary removeAllObjects]; [self updateForDocument:notification.object]; } - (void)handleWindowDidResignMainNotification:(NSNotification *)notification { [self.editDictionary removeAllObjects]; [self updateForDocument:nil]; } - (void)handlePDFDocumentInfoDidChangeNotification:(NSNotification *)notification { [self.editDictionary removeAllObjects]; NSDocument *doc = [[[NSApp mainWindow] windowController] document]; if ([doc isKindOfClass:[KMMainDocument class]]) { CPDFDocument *pdfDocument = ((KMMainDocument *)doc).mainViewController.document; if ([pdfDocument isEqual:notification.object]) { [self updateForDocument:doc]; } } } - (void)handleDocumentFileURLDidChangeNotification:(NSNotification *)notification { [self.editDictionary removeAllObjects]; NSDocument *doc = [[[NSApp mainWindow] windowController] document]; if ([doc isEqual:[notification object]]) [self updateForDocument:doc]; } - (NSInteger)numberOfRowsInTableView:(NSTableView *)tv { NSArray *keys = nil; if ([tv isEqual:summaryTableView]) keys = summaryKeys; else if ([tv isEqual:attributesTableView]) keys = attributesKeys; return [keys count]; } - (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row { static NSDateFormatter *shortDateFormatter = nil; if(shortDateFormatter == nil) { shortDateFormatter = [[NSDateFormatter alloc] init]; [shortDateFormatter setDateStyle:NSDateFormatterShortStyle]; [shortDateFormatter setTimeStyle:NSDateFormatterNoStyle]; } NSArray *keys = nil; if ([tableView isEqual:summaryTableView]) { keys = summaryKeys; } else if ([tableView isEqual:attributesTableView]) { keys = attributesKeys; } NSString *key = [keys objectAtIndex:row]; NSString *tcID = [tableColumn identifier]; id value = nil; if ([key length]) { if ([tcID isEqualToString:LABEL_COLUMN_ID]) { value = [labels objectForKey:key] ?: [key stringByAppendingString:@":"]; } else if ([tcID isEqualToString:VALUE_COLUMN_ID]) { value = [info objectForKey:key]; if (value == nil) value = @"-"; else if ([value isKindOfClass:[NSDate class]]) value = [shortDateFormatter stringFromDate:value]; else if ([value isKindOfClass:[NSNumber class]]){ if ([key isEqualToString:SKInfoEncryptedKey]) { value = [value boolValue] ? NSLocalizedString(@"Yes", @"") : NSLocalizedString(@"No", @""); } else { value = ([key isEqualToString:SKInfoPageCountKey] ? [value stringValue] : ([value boolValue] ? NSLocalizedString(@"Allowed", @"") : NSLocalizedString(@"Not Allowed", @""))); } } } } else { value = @""; } NSTableCellView *cellView = [tableView makeViewWithIdentifier:tcID owner:self]; cellView.textField.stringValue = value; if ([tableView isEqual:attributesTableView] && [[tableColumn identifier] isEqualToString:VALUE_COLUMN_ID]) { if (row == 0 || row == 1 || row == 2 || row == 3 || row == 7) { cellView.textField.bezelStyle = NSTextFieldSquareBezel; cellView.textField.editable = YES; cellView.textField.selectable = YES; cellView.textField.bordered = YES; cellView.textField.bezeled = YES; cellView.textField.drawsBackground = YES; cellView.textField.delegate = self; if (row == 1) { NSTableCellView *cell = [tableView viewAtColumn:1 row:0 makeIfNecessary:YES]; cell.textField.nextKeyView = cellView.textField; } else if (row == 2) { NSTableCellView *cell = [tableView viewAtColumn:1 row:1 makeIfNecessary:NO]; cell.textField.nextKeyView = cellView.textField; } else if (row == 3) { NSTableCellView *cell = [tableView viewAtColumn:1 row:2 makeIfNecessary:NO]; cell.textField.nextKeyView = cellView.textField; } else if (row == 7) { NSTableCellView *cell = [tableView viewAtColumn:1 row:3 makeIfNecessary:NO]; cell.textField.nextKeyView = cellView.textField; } } else { cellView.textField.editable = NO; cellView.textField.selectable = NO; cellView.textField.bordered = NO; cellView.textField.bezeled = NO; cellView.textField.drawsBackground = NO; cellView.textField.delegate = nil; } cellView.textField.tag = row; } return cellView; } - (id)tableView:(NSTableView *)tv objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row { static NSDateFormatter *shortDateFormatter = nil; if(shortDateFormatter == nil) { shortDateFormatter = [[NSDateFormatter alloc] init]; [shortDateFormatter setDateStyle:NSDateFormatterShortStyle]; [shortDateFormatter setTimeStyle:NSDateFormatterNoStyle]; } NSArray *keys = nil; if ([tv isEqual:summaryTableView]) keys = summaryKeys; else if ([tv isEqual:attributesTableView]) keys = attributesKeys; NSString *key = [keys objectAtIndex:row]; NSString *tcID = [tableColumn identifier]; id value = nil; if ([key length]) { if ([tcID isEqualToString:LABEL_COLUMN_ID]) { value = [labels objectForKey:key] ?: [key stringByAppendingString:@":"]; } else if ([tcID isEqualToString:VALUE_COLUMN_ID]) { value = [info objectForKey:key]; if (value == nil) value = @"-"; else if ([value isKindOfClass:[NSDate class]]) value = [shortDateFormatter stringFromDate:value]; else if ([value isKindOfClass:[NSNumber class]]){ if ([key isEqualToString:SKInfoEncryptedKey]) { value = [value boolValue] ? NSLocalizedString(@"Yes", @"") : NSLocalizedString(@"No", @""); } else { value = ([key isEqualToString:SKInfoPageCountKey] ? [value stringValue] : ([value boolValue] ? NSLocalizedString(@"Allowed", @"") : NSLocalizedString(@"Not Allowed", @""))); } } } } return value; } - (void)tableView:(NSTableView *)tv willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row { if ([tv isEqual:attributesTableView] && [[tableColumn identifier] isEqualToString:LABEL_COLUMN_ID]) [cell setLineBreakMode:row == [tv numberOfRows] - 1 ? NSLineBreakByWordWrapping : NSLineBreakByTruncatingTail]; } - (CGFloat)tableView:(NSTableView *)tv heightOfRow:(NSInteger)row { CGFloat rowHeight = [tv rowHeight]; if ([tv isEqual:attributesTableView] && row == [tv numberOfRows] - 1) rowHeight = fmax(rowHeight, NSHeight([[tv enclosingScrollView] bounds]) - [tv numberOfRows] * (rowHeight + [tv intercellSpacing].height) + rowHeight); return rowHeight; } - (BOOL)tableView:(NSTableView *)tv shouldSelectRow:(NSInteger)row { return YES; } - (void)tabView:(NSTabView *)tabView willSelectTabViewItem:(nullable NSTabViewItem *)tabViewItem { if ([tabView indexOfTabViewItem:tabViewItem] == 1) { [self.attributesTableView reloadData]; } } #pragma mark - NSTextFieldDelegate - (void)controlTextDidChange:(NSNotification *)obj { NSTextField *textField = (NSTextField*)[obj object]; if (textField.tag == 0) { [self.editDictionary setObject:textField.stringValue forKey:PDFDocumentTitleAttribute]; } else if (textField.tag == 1) { [self.editDictionary setObject:textField.stringValue forKey:PDFDocumentAuthorAttribute]; } else if (textField.tag == 2) { [self.editDictionary setObject:textField.stringValue forKey:PDFDocumentSubjectAttribute]; } else if (textField.tag == 3) { [self.editDictionary setObject:textField.stringValue forKey:PDFDocumentCreatorAttribute]; } else if (textField.tag == 7) { [self.editDictionary setObject:textField.stringValue forKey:SKInfoKeywordsStringKey]; } } @end