| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2009 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 "extension_shelf_controller.h" |
| 6 |
| 7 #include "base/mac_util.h" |
| 8 #include "chrome/browser/browser.h" |
| 9 #include "chrome/browser/extensions/extension_shelf_model.h" |
| 10 #include "skia/ext/skia_utils_mac.h" |
| 11 |
| 12 namespace { |
| 13 |
| 14 const int kExtensionShelfPaddingTop = 1; |
| 15 const int kToolstripPadding = 2; |
| 16 |
| 17 } |
| 18 |
| 19 // This class manages the extensions ("toolstrips") on the shelf. It listens to |
| 20 // events sent by the extension system, and acts as a bridge between that and |
| 21 // the cocoa world. |
| 22 class ExtensionShelfMac : public ExtensionShelfModelObserver { |
| 23 public: |
| 24 ExtensionShelfMac(Browser* browser, ExtensionShelfController* controller); |
| 25 virtual ~ExtensionShelfMac(); |
| 26 |
| 27 // ExtensionShelfModelObserver |
| 28 virtual void ToolstripInsertedAt(ExtensionHost* toolstrip, int index); |
| 29 virtual void ToolstripRemovingAt(ExtensionHost* toolstrip, int index); |
| 30 virtual void ToolstripMoved(ExtensionHost* toolstrip, |
| 31 int from_index, |
| 32 int to_index); |
| 33 virtual void ToolstripChangedAt(ExtensionHost* toolstrip, int index); |
| 34 virtual void ExtensionShelfEmpty(); |
| 35 virtual void ShelfModelReloaded(); |
| 36 virtual void ShelfModelDeleting(); |
| 37 |
| 38 // Determines what is our target height and sets it. |
| 39 void AdjustHeight(); |
| 40 |
| 41 private: |
| 42 class Toolstrip; |
| 43 |
| 44 void Show(); |
| 45 void Hide(); |
| 46 |
| 47 // Create the contents of the extension shelf. |
| 48 void Init(Profile* profile); |
| 49 |
| 50 // Loads the background image into memory, or does nothing if already loaded. |
| 51 void InitBackground(); |
| 52 |
| 53 // Re-inserts all toolstrips from the model. Must be called when the shelf |
| 54 // contains no toolstrips. |
| 55 void LoadFromModel(); |
| 56 |
| 57 void DeleteToolstrips(); |
| 58 |
| 59 Toolstrip* ToolstripAtIndex(int index); |
| 60 |
| 61 ExtensionShelfController* controller_; // weak, owns us |
| 62 |
| 63 Browser* browser_; // weak |
| 64 |
| 65 // Lazily-initialized background for toolstrips. |
| 66 scoped_ptr<SkBitmap> background_; |
| 67 |
| 68 // The model representing the toolstrips on the shelf. |
| 69 ExtensionShelfModel* model_; // weak |
| 70 |
| 71 // Set of toolstrip views which are really on the shelf. |
| 72 std::set<Toolstrip*> toolstrips_; |
| 73 |
| 74 // Stores if we are currently layouting items. |
| 75 bool is_adjusting_height_; |
| 76 |
| 77 DISALLOW_COPY_AND_ASSIGN(ExtensionShelfMac); |
| 78 }; |
| 79 |
| 80 // This class represents a single extension ("toolstrip") on the extension |
| 81 // shelf. |
| 82 class ExtensionShelfMac::Toolstrip { |
| 83 public: |
| 84 explicit Toolstrip(ExtensionHost* host) |
| 85 : host_(host) { |
| 86 DCHECK(host_->view()); |
| 87 Init(); |
| 88 } |
| 89 |
| 90 // Inserts the native NSView belonging to this extension into the view that |
| 91 // belongs to |controller|. Makes sure the controller is notified when the |
| 92 // extension's |frame| changes. |
| 93 void AddToolstripToController(ExtensionShelfController* controller); |
| 94 |
| 95 // Removes the native NSView belonging to this extension from the view that |
| 96 // belongs to |controller|. Removes |controller| as a frame size observer. |
| 97 void RemoveToolstripFromController(ExtensionShelfController* controller); |
| 98 |
| 99 // Sets the image that is used by the extension. |
| 100 void SetBackground(const SkBitmap& background) { |
| 101 host_->view()->SetBackground(background); |
| 102 } |
| 103 |
| 104 // Returns the native NSView belonging to this extension. |
| 105 gfx::NativeView native_view() { |
| 106 return host_->view()->native_view(); |
| 107 } |
| 108 |
| 109 private: |
| 110 void Init(); |
| 111 |
| 112 ExtensionHost* host_; // weak |
| 113 |
| 114 const std::string extension_name_; |
| 115 |
| 116 private: |
| 117 DISALLOW_COPY_AND_ASSIGN(Toolstrip); |
| 118 }; |
| 119 |
| 120 void ExtensionShelfMac::Toolstrip::AddToolstripToController( |
| 121 ExtensionShelfController* controller) { |
| 122 NSView* toolstrip_view = host_->view()->native_view(); |
| 123 [[controller view] addSubview:toolstrip_view]; |
| 124 |
| 125 [[NSNotificationCenter defaultCenter] |
| 126 addObserver:controller |
| 127 selector:@selector(updateVisibility:) |
| 128 name:NSViewFrameDidChangeNotification |
| 129 object:toolstrip_view]; |
| 130 } |
| 131 |
| 132 void ExtensionShelfMac::Toolstrip::RemoveToolstripFromController( |
| 133 ExtensionShelfController* controller) { |
| 134 [host_->view()->native_view() removeFromSuperview]; |
| 135 |
| 136 [[NSNotificationCenter defaultCenter] |
| 137 removeObserver:controller |
| 138 name:NSViewFrameDidChangeNotification |
| 139 object:host_->view()->native_view()]; |
| 140 } |
| 141 |
| 142 void ExtensionShelfMac::Toolstrip::Init() { |
| 143 host_->view()->set_is_toolstrip(true); |
| 144 } |
| 145 |
| 146 ExtensionShelfMac::ExtensionShelfMac(Browser* browser, |
| 147 ExtensionShelfController* controller) |
| 148 : controller_(controller), |
| 149 browser_(browser), |
| 150 model_(browser->extension_shelf_model()), |
| 151 is_adjusting_height_(false) { |
| 152 if (model_) // Can be NULL in tests. |
| 153 Init(browser_->profile()); |
| 154 } |
| 155 |
| 156 ExtensionShelfMac::~ExtensionShelfMac() { |
| 157 DeleteToolstrips(); |
| 158 if (model_) |
| 159 model_->RemoveObserver(this); |
| 160 } |
| 161 |
| 162 void ExtensionShelfMac::Show() { |
| 163 [controller_ show:nil]; |
| 164 } |
| 165 |
| 166 void ExtensionShelfMac::Hide() { |
| 167 [controller_ hide:nil]; |
| 168 } |
| 169 |
| 170 void ExtensionShelfMac::ToolstripInsertedAt(ExtensionHost* host, |
| 171 int index) { |
| 172 InitBackground(); |
| 173 Toolstrip* toolstrip = new Toolstrip(host); |
| 174 toolstrip->SetBackground(*background_.get()); |
| 175 toolstrip->AddToolstripToController(controller_); |
| 176 toolstrips_.insert(toolstrip); |
| 177 model_->SetToolstripDataAt(index, toolstrip); |
| 178 |
| 179 AdjustHeight(); |
| 180 } |
| 181 |
| 182 void ExtensionShelfMac::ToolstripRemovingAt(ExtensionHost* host, |
| 183 int index) { |
| 184 Toolstrip* toolstrip = ToolstripAtIndex(index); |
| 185 toolstrip->RemoveToolstripFromController(controller_); |
| 186 toolstrips_.erase(toolstrip); |
| 187 model_->SetToolstripDataAt(index, NULL); |
| 188 delete toolstrip; |
| 189 |
| 190 AdjustHeight(); |
| 191 } |
| 192 |
| 193 void ExtensionShelfMac::ToolstripMoved(ExtensionHost* host, |
| 194 int from_index, |
| 195 int to_index) { |
| 196 // TODO(thakis): Implement reordering toolstrips. |
| 197 AdjustHeight(); |
| 198 } |
| 199 |
| 200 void ExtensionShelfMac::ToolstripChangedAt( |
| 201 ExtensionHost* toolstrip, int index) { |
| 202 // TODO(thakis): Implement changing toolstrips. |
| 203 AdjustHeight(); |
| 204 } |
| 205 |
| 206 void ExtensionShelfMac::ExtensionShelfEmpty() { |
| 207 AdjustHeight(); |
| 208 } |
| 209 |
| 210 void ExtensionShelfMac::ShelfModelReloaded() { |
| 211 DeleteToolstrips(); |
| 212 LoadFromModel(); |
| 213 } |
| 214 |
| 215 void ExtensionShelfMac::ShelfModelDeleting() { |
| 216 DeleteToolstrips(); |
| 217 model_->RemoveObserver(this); |
| 218 model_ = NULL; |
| 219 } |
| 220 |
| 221 void ExtensionShelfMac::Init(Profile* profile) { |
| 222 LoadFromModel(); |
| 223 model_->AddObserver(this); |
| 224 } |
| 225 |
| 226 void ExtensionShelfMac::InitBackground() { |
| 227 if (background_.get()) |
| 228 return; |
| 229 |
| 230 // If this is called while the shelf is invisible, shortly resize the shelf so |
| 231 // that it can paint itself. |
| 232 NSRect current_frame = [[controller_ view] frame]; |
| 233 if (current_frame.size.height < [controller_ height]) { |
| 234 NSRect new_frame = current_frame; |
| 235 new_frame.size.height = [controller_ height]; |
| 236 [[controller_ view] setFrame:new_frame]; |
| 237 } |
| 238 |
| 239 // The background is tiled horizontally in the toolstrip. Hence, its width |
| 240 // should not be too small so that tiling is fast, and not too large, so that |
| 241 // not too much memory is needed -- but the exact width doesn't really matter. |
| 242 const CGFloat kBackgroundTileWidth = 100; |
| 243 |
| 244 // Paint shelf background into an SkBitmap. If we decide to keep the shelf, we |
| 245 // need to do this for both the "main window" and "not main window" shadings. |
| 246 NSRect background_rect = NSMakeRect( |
| 247 0, 0, |
| 248 kBackgroundTileWidth, [controller_ height] - kExtensionShelfPaddingTop); |
| 249 NSBitmapImageRep* bitmap_rep = [[controller_ view] |
| 250 bitmapImageRepForCachingDisplayInRect:background_rect]; |
| 251 |
| 252 [[controller_ view] cacheDisplayInRect:background_rect |
| 253 toBitmapImageRep:bitmap_rep]; |
| 254 background_.reset(new SkBitmap(gfx::CGImageToSkBitmap([bitmap_rep CGImage]))); |
| 255 |
| 256 // Restore old frame. |
| 257 [[controller_ view] setFrame:current_frame]; |
| 258 } |
| 259 |
| 260 void ExtensionShelfMac::AdjustHeight() { |
| 261 if (model_->empty() || toolstrips_.empty()) { |
| 262 // It's possible that |model_| is not empty, but |toolstrips_| are empty |
| 263 // when removing the last toolstrip. |
| 264 DCHECK(toolstrips_.empty()); |
| 265 Hide(); |
| 266 return; |
| 267 } |
| 268 |
| 269 if (is_adjusting_height_) |
| 270 return; |
| 271 is_adjusting_height_ = true; |
| 272 |
| 273 Show(); |
| 274 |
| 275 // Lay out items horizontally from left to right. This method's name is |
| 276 // misleading, but matches linux and windows for now. |
| 277 CGFloat x = 0; |
| 278 for (std::set<Toolstrip*>::iterator iter = toolstrips_.begin(); |
| 279 iter != toolstrips_.end(); ++iter) { |
| 280 NSView* view = (*iter)->native_view(); |
| 281 NSRect frame = [view frame]; |
| 282 frame.origin.x = x; |
| 283 frame.origin.y = 0; |
| 284 frame.size.height = [controller_ height] - kExtensionShelfPaddingTop; |
| 285 [view setFrame:frame]; |
| 286 x += frame.size.width + kToolstripPadding; |
| 287 } |
| 288 |
| 289 is_adjusting_height_ = false; |
| 290 } |
| 291 |
| 292 void ExtensionShelfMac::LoadFromModel() { |
| 293 DCHECK(toolstrips_.empty()); |
| 294 int count = model_->count(); |
| 295 for (int i = 0; i < count; ++i) |
| 296 ToolstripInsertedAt(model_->ToolstripAt(i).host, i); |
| 297 AdjustHeight(); |
| 298 } |
| 299 |
| 300 void ExtensionShelfMac::DeleteToolstrips() { |
| 301 for (std::set<Toolstrip*>::iterator iter = toolstrips_.begin(); |
| 302 iter != toolstrips_.end(); ++iter) { |
| 303 (*iter)->RemoveToolstripFromController(controller_); |
| 304 delete *iter; |
| 305 } |
| 306 toolstrips_.clear(); |
| 307 } |
| 308 |
| 309 ExtensionShelfMac::Toolstrip* ExtensionShelfMac::ToolstripAtIndex(int index) { |
| 310 return static_cast<Toolstrip*>(model_->ToolstripAt(index).data); |
| 311 } |
| 312 |
| 313 |
| 314 @implementation ExtensionShelfController |
| 315 |
| 316 - (id)initWithBrowser:(Browser*)browser |
| 317 resizeDelegate:(id<ViewResizer>)resizeDelegate { |
| 318 if ((self = [super initWithNibName:@"ExtensionShelf" |
| 319 bundle:mac_util::MainAppBundle()])) { |
| 320 resizeDelegate_ = resizeDelegate; |
| 321 browser_ = browser; |
| 322 shelfHeight_ = [[self view] bounds].size.height; |
| 323 |
| 324 NSRect frame = [[self view] frame]; |
| 325 frame.size.height = 0; |
| 326 [[self view] setFrame:frame]; |
| 327 } |
| 328 return self; |
| 329 } |
| 330 |
| 331 - (void)wasInsertedIntoWindow { |
| 332 // The bridge_ calls cacheDisplayInRect:toBitmapImageRep:, which requires that |
| 333 // the view is in a superview to work. Hence, create the bridge object no |
| 334 // sooner. |
| 335 DCHECK(bridge_.get() == NULL); |
| 336 bridge_.reset(new ExtensionShelfMac(browser_, self)); |
| 337 } |
| 338 |
| 339 - (IBAction)show:(id)sender { |
| 340 [resizeDelegate_ resizeView:[self view] newHeight:shelfHeight_]; |
| 341 } |
| 342 |
| 343 - (IBAction)hide:(id)sender { |
| 344 [resizeDelegate_ resizeView:[self view] newHeight:0]; |
| 345 } |
| 346 |
| 347 - (CGFloat)height { |
| 348 return shelfHeight_; |
| 349 } |
| 350 |
| 351 - (void)updateVisibility:(id)sender { |
| 352 if(bridge_.get()) |
| 353 bridge_->AdjustHeight(); |
| 354 } |
| 355 |
| 356 @end |
| OLD | NEW |