1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#import "chrome/browser/ui/cocoa/view_id_util.h"
6
7#import <Cocoa/Cocoa.h>
8
9#include <map>
10#include <utility>
11
12#include "base/lazy_instance.h"
13#include "base/logging.h"
14#import "chrome/browser/ui/cocoa/browser_window_controller.h"
15#import "chrome/browser/ui/cocoa/tabs/tab_strip_controller.h"
16
17namespace {
18
19// TODO(suzhe): After migrating to Mac OS X 10.6, we may use Objective-C's new
20// "Associative References" feature to attach the ViewID to the view directly
21// rather than using a separated map.
22typedef std::map<NSView*, ViewID> ViewIDMap;
23
24static base::LazyInstance<ViewIDMap> g_view_id_map = LAZY_INSTANCE_INITIALIZER;
25
26// Returns the view's nearest descendant (including itself) with a specific
27// ViewID, or nil if no subview has that ViewID.
28NSView* FindViewWithID(NSView* view, ViewID viewID) {
29  if ([view viewID] == viewID)
30    return view;
31
32  for (NSView* subview in [view subviews]) {
33    NSView* result = FindViewWithID(subview, viewID);
34    if (result != nil)
35      return result;
36  }
37  return nil;
38}
39
40}  // anonymous namespace
41
42namespace view_id_util {
43
44void SetID(NSView* view, ViewID viewID) {
45  DCHECK(view);
46  DCHECK(viewID != VIEW_ID_NONE);
47  // We handle VIEW_ID_TAB_0 to VIEW_ID_TAB_LAST in GetView() function directly.
48  DCHECK(!((viewID >= VIEW_ID_TAB_0) && (viewID <= VIEW_ID_TAB_LAST)));
49  g_view_id_map.Get()[view] = viewID;
50}
51
52void UnsetID(NSView* view) {
53  DCHECK(view);
54  g_view_id_map.Get().erase(view);
55}
56
57NSView* GetView(NSWindow* window, ViewID viewID) {
58  DCHECK(viewID != VIEW_ID_NONE);
59  DCHECK(window);
60
61  // As tabs can be created, destroyed or rearranged dynamically, we handle them
62  // here specially.
63  if (viewID >= VIEW_ID_TAB_0 && viewID <= VIEW_ID_TAB_LAST) {
64    BrowserWindowController* windowController = [window windowController];
65    DCHECK([windowController isKindOfClass:[BrowserWindowController class]]);
66    TabStripController* tabStripController =
67        [windowController tabStripController];
68    DCHECK(tabStripController);
69    NSUInteger count = [tabStripController viewsCount];
70    DCHECK(count);
71    NSUInteger index =
72        (viewID == VIEW_ID_TAB_LAST ? count - 1 : viewID - VIEW_ID_TAB_0);
73    return index < count ? [tabStripController viewAtIndex:index] : nil;
74  }
75
76  return FindViewWithID([[window contentView] superview], viewID);
77}
78
79}  // namespace view_id_util
80
81@implementation NSView (ViewID)
82
83- (ViewID)viewID {
84  ViewIDMap* map = g_view_id_map.Pointer();
85  ViewIDMap::const_iterator iter = map->find(self);
86  return iter != map->end() ? iter->second : VIEW_ID_NONE;
87}
88
89- (NSView*)ancestorWithViewID:(ViewID)viewID {
90  NSView* ancestor = self;
91  while (ancestor && [ancestor viewID] != viewID)
92    ancestor = [ancestor superview];
93  return ancestor;
94}
95
96@end
97