// // $Id$ // // SPEncodingPopupAccessory.m // sequel-pro // // Created by Hans-Jörg Bibiko on August 22, 2009 // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // More info at <http://code.google.com/p/sequel-pro/> #import "SPEncodingPopupAccessory.h" @implementation SPEncodingPopupAccessory + (NSView *)encodingAccessory:(NSUInteger)encoding includeDefaultEntry:(BOOL)includeDefaultItem encodingPopUp:(NSPopUpButton **)popup { SPEncodingPopupAccessory *owner = [[[SPEncodingPopupAccessory alloc] init] autorelease]; // Rather than caching, load the accessory view everytime, as it might appear in multiple panels simultaneously. if (![NSBundle loadNibNamed:@"EncodingPopupView" owner:owner]) { NSLog(@"Failed to load EncodingPopupView.nib"); return nil; } if (popup) *popup = owner->encodingPopUp; [[self class] setupPopUp:owner->encodingPopUp selectedEncoding:encoding withDefaultEntry:includeDefaultItem]; return [owner->encodingAccessoryView autorelease]; } /** * Sort using the equivalent Mac encoding as the major key. * Secondary key is the actual encoding value, which works well enough. * We treat Unicode encodings as special case, putting them at top of the list. */ static int encodingCompare(const void *firstPtr, const void *secondPtr) { CFStringEncoding first = *(CFStringEncoding *)firstPtr; CFStringEncoding second = *(CFStringEncoding *)secondPtr; CFStringEncoding macEncodingForFirst = CFStringGetMostCompatibleMacStringEncoding(first); CFStringEncoding macEncodingForSecond = CFStringGetMostCompatibleMacStringEncoding(second); // Should really never happen if (first == second) return 0; if (macEncodingForFirst == kCFStringEncodingUnicode || macEncodingForSecond == kCFStringEncodingUnicode) { if (macEncodingForSecond == macEncodingForFirst) // Both Unicode; compare second order return (first > second) ? 1 : -1; // First is Unicode return (macEncodingForFirst == kCFStringEncodingUnicode) ? -1 : 1; } if ((macEncodingForFirst > macEncodingForSecond) || ((macEncodingForFirst == macEncodingForSecond) && (first > second))) return 1; return -1; } /** * Returns the actual enabled list of encodings for open/save files. */ + (NSArray *)enabledEncodings { static NSMutableArray *allEncodings = nil; // Build list of encodings, sorted, and including only those with human readable names if (!allEncodings) { const CFStringEncoding *cfEncodings = CFStringGetListOfAvailableEncodings(); CFStringEncoding *tmp; NSInteger cnt, num = 0; while (cfEncodings[num] != kCFStringEncodingInvalidId) num++; tmp = malloc(sizeof(CFStringEncoding) * num); memcpy(tmp, cfEncodings, sizeof(CFStringEncoding) * num); qsort(tmp, num, sizeof(CFStringEncoding), encodingCompare); allEncodings = [[NSMutableArray alloc] init]; for (cnt = 0; cnt < num; cnt++) { NSStringEncoding nsEncoding = CFStringConvertEncodingToNSStringEncoding(tmp[cnt]); if (nsEncoding && [NSString localizedNameOfStringEncoding:nsEncoding]) [allEncodings addObject:[NSNumber numberWithUnsignedInteger:nsEncoding]]; } free(tmp); } return allEncodings; } /** * This method initializes the provided popup with list of encodings; * it also sets up the selected encoding as indicated and if includeDefaultItem is YES. * Otherwise the tags are set to the NSStringEncoding value for the encoding. */ + (void)setupPopUp:(NSPopUpButton *)popup selectedEncoding:(NSUInteger)selectedEncoding withDefaultEntry:(BOOL)includeDefaultItem { NSArray *encs = [self enabledEncodings]; NSUInteger cnt, numEncodings, itemToSelect = 0; // Put the encodings in the popup [popup removeAllItems]; // Make sure the initial selected encoding appears in the list if (!includeDefaultItem && (selectedEncoding != NoStringEncoding) && ![encs containsObject:[NSNumber numberWithUnsignedInteger:selectedEncoding]]) encs = [encs arrayByAddingObject:[NSNumber numberWithUnsignedInteger:selectedEncoding]]; numEncodings = [encs count]; // Fill with encodings for (cnt = 0; cnt < numEncodings; cnt++) { NSStringEncoding enc = [[encs objectAtIndex:cnt] unsignedIntegerValue]; [popup addItemWithTitle:[NSString localizedNameOfStringEncoding:enc]]; [[popup lastItem] setTag:enc]; [[popup lastItem] setEnabled:YES]; if (enc == selectedEncoding) itemToSelect = [popup numberOfItems] - 1; } [popup selectItemAtIndex:itemToSelect]; } @end