PageRenderTime 89ms CodeModel.GetById 17ms app.highlight 67ms RepoModel.GetById 2ms app.codeStats 0ms

/core/externals/google-toolbox-for-mac/AppKit/GTMUILocalizer.m

http://macfuse.googlecode.com/
Objective C | 428 lines | 354 code | 30 blank | 44 comment | 89 complexity | 90aa971175db6adfe5722919b86aa7ff MD5 | raw file
  1//
  2//  GTMUILocalizer.m
  3//
  4//  Copyright 2009 Google Inc.
  5//
  6//  Licensed under the Apache License, Version 2.0 (the "License"); you may not
  7//  use this file except in compliance with the License.  You may obtain a copy
  8//  of the License at
  9//
 10//  http://www.apache.org/licenses/LICENSE-2.0
 11//
 12//  Unless required by applicable law or agreed to in writing, software
 13//  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 14//  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 15//  License for the specific language governing permissions and limitations under
 16//  the License.
 17//
 18
 19#import "GTMDefines.h"
 20#import "GTMUILocalizer.h"
 21
 22@interface GTMUILocalizer (GTMUILocalizerPrivate)
 23- (void)localizeAccessibility:(id)object;
 24- (void)localizeBindings:(id)object;
 25
 26// Never recursively call any of these methods. Always call
 27// -[self localizeObject:recursively:] otherwise bindings will not be
 28// localized properly.
 29- (void)localizeWindow:(NSWindow *)window recursively:(BOOL)recursive;
 30- (void)localizeToolbar:(NSToolbar *)toolbar;
 31- (void)localizeView:(NSView *)view recursively:(BOOL)recursive;
 32- (void)localizeMenu:(NSMenu *)menu recursively:(BOOL)recursive;
 33- (void)localizeCell:(NSCell *)cell recursively:(BOOL)recursive;
 34
 35@end
 36
 37@implementation GTMUILocalizer
 38- (id)initWithBundle:(NSBundle *)bundle {
 39  if ((self = [super init])) {
 40    bundle_ = [bundle retain];
 41  }
 42  return self;
 43}
 44
 45- (void)dealloc {
 46  [bundle_ release];
 47  [super dealloc];
 48}
 49
 50- (void)awakeFromNib {
 51  if (owner_) {
 52    NSBundle *newBundle = [[self class] bundleForOwner:owner_];
 53    bundle_ = [newBundle retain];
 54    [self localizeObject:owner_ recursively:YES];
 55    [self localizeObject:otherObjectToLocalize_ recursively:YES];
 56    [self localizeObject:yetAnotherObjectToLocalize_ recursively:YES];
 57  } else {
 58    _GTMDevLog(@"Expected an owner_ set for %@", self);
 59  }
 60  // Won't need these again, clear them.
 61  owner_ = nil;
 62  otherObjectToLocalize_ = nil;
 63  yetAnotherObjectToLocalize_ = nil;
 64}
 65
 66+ (NSBundle *)bundleForOwner:(id)owner {
 67  NSBundle *newBundle = nil;
 68  if (owner) {
 69    Class class = [NSWindowController class];
 70    if ([owner isKindOfClass:class] && ![owner isMemberOfClass:class]) {
 71      newBundle = [NSBundle bundleForClass:[owner class]];
 72#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
 73    } else if ([owner isKindOfClass:[NSViewController class]]) {
 74      newBundle = [(NSViewController *)owner nibBundle];
 75#endif
 76    }
 77    if (!newBundle) {
 78      newBundle = [NSBundle mainBundle];
 79    }
 80  }
 81  return newBundle;
 82}
 83
 84- (NSString *)localizedStringForString:(NSString *)string {
 85  NSString *localized = nil;
 86  if (bundle_ && [string hasPrefix:@"^"]) {
 87    NSString *notFoundValue = @"__GTM_NOT_FOUND__";
 88    NSString *key = [string substringFromIndex:1];
 89    localized = [bundle_ localizedStringForKey:key
 90                                         value:notFoundValue
 91                                         table:nil];
 92    if ([localized isEqualToString:notFoundValue]) {
 93      localized = nil;
 94    }
 95  }
 96  return localized;
 97}
 98
 99- (void)localizeObject:(id)object recursively:(BOOL)recursive {
100  if (object) {
101    if ([object isKindOfClass:[NSWindowController class]]) {
102      NSWindow *window = [object window];
103      [self localizeWindow:window recursively:recursive];
104#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
105    } else if ([object isKindOfClass:[NSViewController class]]) {
106        NSView *view = [object view];
107        [self localizeView:view recursively:recursive];
108#endif
109    } else if ([object isKindOfClass:[NSMenu class]]) {
110      [self localizeMenu:(NSMenu *)object recursively:recursive];
111    } else if ([object isKindOfClass:[NSWindow class]]) {
112      [self localizeWindow:(NSWindow *)object recursively:recursive];
113    } else if ([object isKindOfClass:[NSView class]]) {
114      [self localizeView:(NSView *)object recursively:recursive];
115    } else if ([object isKindOfClass:[NSApplication class]]) {
116      // Do the main menu
117      NSMenu *menu = [object mainMenu];
118      [self localizeMenu:menu recursively:recursive];
119    } else if ([object isKindOfClass:[NSCell class]]) {
120      [self localizeCell:(NSCell *)object recursively:recursive];
121    } else if ([object isKindOfClass:[NSToolbar class]]) {
122      [self localizeToolbar:(NSToolbar*)object];
123    }
124    [self localizeBindings:object];
125  }
126}
127
128- (void)localizeWindow:(NSWindow *)window recursively:(BOOL)recursive {
129  NSString *title = [window title];
130  NSString *localizedTitle = [self localizedStringForString:title];
131  if (localizedTitle) {
132    [window setTitle:localizedTitle];
133  }
134  if (recursive) {
135    [self localizeObject:[window contentView] recursively:recursive];
136    [self localizeObject:[window toolbar] recursively:recursive];
137  }
138}
139
140- (void)localizeToolbar:(NSToolbar *)toolbar {
141  // NOTE: Like the header says, -items only gives us what is in the toolbar
142  // which is usually the default items, if the toolbar supports customization
143  // there is no way to fetch those possible items to tweak their contents.
144  NSToolbarItem *item;
145  GTM_FOREACH_OBJECT(item, [toolbar items]) {
146    NSString *label = [item label];
147    if (label) {
148      label = [self localizedStringForString:label];
149      if (label) {
150        [item setLabel:label];
151      }
152    }
153
154    NSString *paletteLabel = [item paletteLabel];
155    if (paletteLabel) {
156      paletteLabel = [self localizedStringForString:paletteLabel];
157      if (paletteLabel) {
158        [item setPaletteLabel:paletteLabel];
159      }
160    }
161
162    NSString *toolTip = [item toolTip];
163    if (toolTip) {
164      toolTip = [self localizedStringForString:toolTip];
165      if (toolTip) {
166        [item setToolTip:toolTip];
167      }
168    }
169  }
170}
171
172- (void)localizeView:(NSView *)view recursively:(BOOL)recursive {
173  if (view) {
174    // First do tooltips
175    NSString *toolTip = [view toolTip];
176    if (toolTip) {
177      NSString *localizedToolTip = [self localizedStringForString:toolTip];
178      if (localizedToolTip) {
179        [view setToolTip:localizedToolTip];
180      }
181    }
182
183    // Then do accessibility both on views directly...
184    [self localizeAccessibility:view];
185
186    // ...and on control cells (which implement accessibility for the controls
187    // that contain them)
188    if ([view isKindOfClass:[NSControl class]]) {
189      [self localizeAccessibility:[(NSControl *)view cell]];
190    }
191
192    // Must do the menu before the titles, or else this will screw up
193    // popup menus on us.
194    [self localizeObject:[view menu] recursively:recursive];
195    if (recursive) {
196      NSArray *subviews = [view subviews];
197      NSView *subview = nil;
198      GTM_FOREACH_OBJECT(subview, subviews) {
199        [self localizeObject:subview recursively:recursive];
200      }
201    }
202
203    // Then do titles
204    if ([view isKindOfClass:[NSTextField class]]) {
205      NSString *title = [(NSTextField *)view stringValue];
206      NSString *localizedTitle = [self localizedStringForString:title];
207      if (localizedTitle) {
208        [(NSTextField *)view setStringValue:localizedTitle];
209      }
210    } else if ([view respondsToSelector:@selector(title)]
211        && [view respondsToSelector:@selector(setTitle:)]) {
212      NSString *title = [view performSelector:@selector(title)];
213      if (title) {
214        NSString *localizedTitle = [self localizedStringForString:title];
215        if (localizedTitle) {
216          [view performSelector:@selector(setTitle:) withObject:localizedTitle];
217        }
218      }
219      if ([view respondsToSelector:@selector(alternateTitle)]
220          && [view respondsToSelector:@selector(setAlternateTitle:)]) {
221        title = [view performSelector:@selector(alternateTitle)];
222        if (title) {
223          NSString *localizedTitle = [self localizedStringForString:title];
224          if (localizedTitle) {
225            [view performSelector:@selector(setAlternateTitle:)
226                       withObject:localizedTitle];
227          }
228        }
229      }
230    } else if ([view respondsToSelector:@selector(tabViewItems)]) {
231      NSArray *items = [view performSelector:@selector(tabViewItems)];
232      NSEnumerator *itemEnum = [items objectEnumerator];
233      NSTabViewItem *item = nil;
234      while ((item = [itemEnum nextObject])) {
235        NSString *label = [item label];
236        NSString *localizedLabel = [self localizedStringForString:label];
237        if (localizedLabel) {
238          [item setLabel:localizedLabel];
239        }
240        if (recursive) {
241          [self localizeObject:[item view] recursively:recursive];
242        }
243      }
244    }
245  }
246
247  // Do NSTextField placeholders
248  if ([view isKindOfClass:[NSTextField class]]) {
249    NSString *placeholder = [[(NSTextField *)view cell] placeholderString];
250    NSString *localizedPlaceholer = [self localizedStringForString:placeholder];
251    if (localizedPlaceholer) {
252      [[(NSTextField *)view cell] setPlaceholderString:localizedPlaceholer];
253    }
254  }
255
256  // Do any NSMatrix placeholders
257  if ([view isKindOfClass:[NSMatrix class]]) {
258    NSMatrix *matrix = (NSMatrix *)view;
259    // Process the prototype
260    id cell = [matrix prototype];
261    [self localizeObject:cell recursively:recursive];
262    // Process the cells
263    GTM_FOREACH_OBJECT(cell, [matrix cells]) {
264      [self localizeObject:cell recursively:recursive];
265      // The tooltip isn't on a cell, so we do it via the matrix.
266      NSString *toolTip = [matrix toolTipForCell:cell];
267      NSString *localizedToolTip = [self localizedStringForString:toolTip];
268      if (localizedToolTip) {
269        [matrix setToolTip:localizedToolTip forCell:cell];
270      }
271    }
272  }
273
274  // Do NSTableView column headers.
275  if ([view isKindOfClass:[NSTableView class]]) {
276    NSTableView *tableView = (NSTableView *)view;
277    NSArray *columns = [tableView tableColumns];
278    NSTableColumn *column = nil;
279    GTM_FOREACH_OBJECT(column, columns) {
280      [self localizeObject:[column headerCell] recursively:recursive];
281    }
282  }
283
284  // Do NSSegmentedControl segments.
285  if ([view isKindOfClass:[NSSegmentedControl class]]) {
286    NSSegmentedControl *segmentedControl = (NSSegmentedControl *)view;
287    for (NSInteger i = 0; i < [segmentedControl segmentCount]; ++i) {
288      NSString *label = [segmentedControl labelForSegment:i];
289      NSString *localizedLabel = [self localizedStringForString:label];
290      if (localizedLabel) {
291        [segmentedControl setLabel:localizedLabel forSegment:i];
292      }
293    }
294  }
295
296  // Do NSComboBox items.
297  if ([view isKindOfClass:[NSComboBox class]]) {
298    NSComboBox *combobox = (NSComboBox*)view;
299    // Make sure it doesn't use a DataSource.
300    if (![combobox usesDataSource]) {
301      NSMutableArray *localizedValues = [NSMutableArray array];
302      BOOL replaceValues = NO;
303      NSString *value;
304      GTM_FOREACH_OBJECT(value, [combobox objectValues]) {
305        NSString *localizedValue = nil;
306        if ([value isKindOfClass:[NSString class]]) {
307          localizedValue = [self localizedStringForString:value];
308        }
309        if (localizedValue) {
310          replaceValues = YES;
311          [localizedValues addObject:localizedValue];
312        } else {
313          [localizedValues addObject:value];
314        }
315      }
316      if (replaceValues) {
317        [combobox removeAllItems];
318        [combobox addItemsWithObjectValues:localizedValues];
319      }
320    }
321  }
322}
323
324- (void)localizeMenu:(NSMenu *)menu recursively:(BOOL)recursive {
325  if (menu) {
326    NSString *title = [menu title];
327    NSString *localizedTitle = [self localizedStringForString:title];
328    if (localizedTitle) {
329      [menu setTitle:localizedTitle];
330    }
331    NSArray *menuItems = [menu itemArray];
332    NSMenuItem *menuItem = nil;
333    GTM_FOREACH_OBJECT(menuItem, menuItems) {
334      title = [menuItem title];
335      localizedTitle = [self localizedStringForString:title];
336      if (localizedTitle) {
337        [menuItem setTitle:localizedTitle];
338      }
339      if (recursive) {
340        [self localizeObject:[menuItem submenu] recursively:recursive];
341      }
342    }
343  }
344}
345
346- (void)localizeCell:(NSCell *)cell recursively:(BOOL)recursive {
347  if (cell) {
348    NSString *title = [cell title];
349    NSString *localizedTitle = [self localizedStringForString:title];
350    if (localizedTitle) {
351      [cell setTitle:localizedTitle];
352    }
353    [self localizeObject:[cell menu] recursively:recursive];
354    id obj = [cell representedObject];
355    [self localizeObject:obj recursively:recursive];
356  }
357}
358
359- (void)localizeBindings:(id)object {
360  NSArray *exposedBindings = [object exposedBindings];
361  if (exposedBindings) {
362    NSString *optionsToLocalize[] = {
363      NSDisplayNameBindingOption,
364      NSDisplayPatternBindingOption,
365      NSMultipleValuesPlaceholderBindingOption,
366      NSNoSelectionPlaceholderBindingOption,
367      NSNotApplicablePlaceholderBindingOption,
368      NSNullPlaceholderBindingOption,
369    };
370    Class stringClass = [NSString class];
371    NSString *exposedBinding;
372    GTM_FOREACH_OBJECT(exposedBinding, exposedBindings) {
373      NSDictionary *bindingInfo = [object infoForBinding:exposedBinding];
374      if (bindingInfo) {
375        id observedObject = [bindingInfo objectForKey:NSObservedObjectKey];
376        NSString *path = [bindingInfo objectForKey:NSObservedKeyPathKey];
377        NSDictionary *options = [bindingInfo objectForKey:NSOptionsKey];
378        if (observedObject && path && options) {
379          NSMutableDictionary *newOptions
380            = [NSMutableDictionary dictionaryWithDictionary:options];
381          BOOL valueChanged = NO;
382          for (size_t i = 0;
383               i < sizeof(optionsToLocalize) / sizeof(optionsToLocalize[0]);
384               ++i) {
385            NSString *key = optionsToLocalize[i];
386            NSString *value = [newOptions objectForKey:key];
387            if ([value isKindOfClass:stringClass]) {
388              NSString *localizedValue = [self localizedStringForString:value];
389              if (localizedValue) {
390                valueChanged = YES;
391                [newOptions setObject:localizedValue forKey:key];
392              }
393            }
394          }
395          if (valueChanged) {
396            // Only unbind and rebind if there is a change.
397            [object unbind:exposedBinding];
398            [object bind:exposedBinding
399                toObject:observedObject
400             withKeyPath:path
401                 options:newOptions];
402          }
403        }
404      }
405    }
406  }
407}
408
409- (void)localizeAccessibility:(id)object {
410  NSArray *supportedAttrs = [object accessibilityAttributeNames];
411  if ([supportedAttrs containsObject:NSAccessibilityHelpAttribute]) {
412    NSString *accessibilityHelp
413      = [object accessibilityAttributeValue:NSAccessibilityHelpAttribute];
414    if (accessibilityHelp) {
415      NSString *localizedAccessibilityHelp
416        = [self localizedStringForString:accessibilityHelp];
417      if (localizedAccessibilityHelp) {
418        [object accessibilitySetValue:localizedAccessibilityHelp
419                         forAttribute:NSAccessibilityHelpAttribute];
420      }
421    }
422  }
423
424  // We cannot do the same thing with NSAccessibilityDescriptionAttribute; see
425  // the links in the header file for more details.
426}
427
428@end