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/dev_tools_controller.h"
6
7#include <algorithm>
8
9#include <Cocoa/Cocoa.h>
10
11#include "chrome/browser/browser_process.h"
12#include "chrome/browser/debugger/devtools_window.h"
13#include "chrome/browser/prefs/pref_service.h"
14#include "chrome/browser/profiles/profile.h"
15#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
16#import "chrome/browser/ui/cocoa/view_id_util.h"
17#include "chrome/common/pref_names.h"
18#include "content/browser/tab_contents/tab_contents.h"
19
20namespace {
21
22// Default offset of the contents splitter in pixels.
23const int kDefaultContentsSplitOffset = 400;
24
25// Never make the web part of the tab contents smaller than this (needed if the
26// window is only a few pixels high).
27const int kMinWebHeight = 50;
28
29}  // end namespace
30
31
32@interface DevToolsController (Private)
33- (void)showDevToolsContents:(TabContents*)devToolsContents
34                 withProfile:(Profile*)profile;
35- (void)resizeDevToolsToNewHeight:(CGFloat)height;
36@end
37
38
39@implementation DevToolsController
40
41- (id)initWithDelegate:(id<TabContentsControllerDelegate>)delegate {
42  if ((self = [super init])) {
43    splitView_.reset([[NSSplitView alloc] initWithFrame:NSZeroRect]);
44    [splitView_ setDividerStyle:NSSplitViewDividerStyleThin];
45    [splitView_ setVertical:NO];
46    [splitView_ setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
47    [splitView_ setDelegate:self];
48
49    contentsController_.reset(
50        [[TabContentsController alloc] initWithContents:NULL
51                                               delegate:delegate]);
52  }
53  return self;
54}
55
56- (void)dealloc {
57  [splitView_ setDelegate:nil];
58  [super dealloc];
59}
60
61- (NSView*)view {
62  return splitView_.get();
63}
64
65- (NSSplitView*)splitView {
66  return splitView_.get();
67}
68
69- (void)updateDevToolsForTabContents:(TabContents*)contents
70                         withProfile:(Profile*)profile {
71  // Get current devtools content.
72  TabContentsWrapper* devToolsTab = contents ?
73      DevToolsWindow::GetDevToolsContents(contents) : NULL;
74  TabContents* devToolsContents = devToolsTab ?
75      devToolsTab->tab_contents() : NULL;
76
77  [self showDevToolsContents:devToolsContents withProfile:profile];
78}
79
80- (void)ensureContentsVisible {
81  [contentsController_ ensureContentsVisible];
82}
83
84- (void)showDevToolsContents:(TabContents*)devToolsContents
85                 withProfile:(Profile*)profile {
86  [contentsController_ ensureContentsSizeDoesNotChange];
87
88  NSArray* subviews = [splitView_ subviews];
89  if (devToolsContents) {
90    DCHECK_GE([subviews count], 1u);
91
92    // |devToolsView| is a TabContentsViewCocoa object, whose ViewID was
93    // set to VIEW_ID_TAB_CONTAINER initially, so we need to change it to
94    // VIEW_ID_DEV_TOOLS_DOCKED here.
95    view_id_util::SetID(
96        devToolsContents->GetNativeView(), VIEW_ID_DEV_TOOLS_DOCKED);
97
98    CGFloat splitOffset = 0;
99    if ([subviews count] == 1) {
100      // Load the default split offset.
101      splitOffset = profile->GetPrefs()->
102          GetInteger(prefs::kDevToolsSplitLocation);
103      if (splitOffset < 0) {
104        // Initial load, set to default value.
105        splitOffset = kDefaultContentsSplitOffset;
106      }
107      [splitView_ addSubview:[contentsController_ view]];
108    } else {
109      DCHECK_EQ([subviews count], 2u);
110      // If devtools are already visible, keep the current size.
111      splitOffset = NSHeight([[subviews objectAtIndex:1] frame]);
112    }
113
114    // Make sure |splitOffset| isn't too large or too small.
115    splitOffset = std::max(static_cast<CGFloat>(kMinWebHeight), splitOffset);
116    splitOffset =
117        std::min(splitOffset, NSHeight([splitView_ frame]) - kMinWebHeight);
118    DCHECK_GE(splitOffset, 0) << "kMinWebHeight needs to be smaller than "
119                              << "smallest available tab contents space.";
120
121    [self resizeDevToolsToNewHeight:splitOffset];
122  } else {
123    if ([subviews count] > 1) {
124      NSView* oldDevToolsContentsView = [subviews objectAtIndex:1];
125      // Store split offset when hiding devtools window only.
126      int splitOffset = NSHeight([oldDevToolsContentsView frame]);
127
128      profile->GetPrefs()->SetInteger(
129          prefs::kDevToolsSplitLocation, splitOffset);
130      [oldDevToolsContentsView removeFromSuperview];
131      [splitView_ adjustSubviews];
132    }
133  }
134
135  [contentsController_ changeTabContents:devToolsContents];
136}
137
138- (void)resizeDevToolsToNewHeight:(CGFloat)height {
139  NSArray* subviews = [splitView_ subviews];
140
141  // It seems as if |-setPosition:ofDividerAtIndex:| should do what's needed,
142  // but I can't figure out how to use it. Manually resize web and devtools.
143  // TODO(alekseys): either make setPosition:ofDividerAtIndex: work or to add a
144  // category on NSSplitView to handle manual resizing.
145  NSView* devToolsView = [subviews objectAtIndex:1];
146  NSRect devToolsFrame = [devToolsView frame];
147  devToolsFrame.size.height = height;
148  [devToolsView setFrame:devToolsFrame];
149
150  NSView* webView = [subviews objectAtIndex:0];
151  NSRect webFrame = [webView frame];
152  webFrame.size.height =
153      NSHeight([splitView_ frame]) - ([splitView_ dividerThickness] + height);
154  [webView setFrame:webFrame];
155
156  [splitView_ adjustSubviews];
157}
158
159// NSSplitViewDelegate protocol.
160- (BOOL)splitView:(NSSplitView *)splitView
161    shouldAdjustSizeOfSubview:(NSView *)subview {
162  // Return NO for the devTools view to indicate that it should not be resized
163  // automatically. It preserves the height set by the user and also keeps
164  // view height the same while changing tabs when one of the tabs shows infobar
165  // and others are not.
166  if ([[splitView_ subviews] indexOfObject:subview] == 1)
167    return NO;
168  return YES;
169}
170
171@end
172