diff options
Diffstat (limited to 'chrome/browser/ui/cocoa/notifications/balloon_controller.mm')
-rw-r--r-- | chrome/browser/ui/cocoa/notifications/balloon_controller.mm | 241 |
1 files changed, 241 insertions, 0 deletions
diff --git a/chrome/browser/ui/cocoa/notifications/balloon_controller.mm b/chrome/browser/ui/cocoa/notifications/balloon_controller.mm new file mode 100644 index 0000000..f0d914e --- /dev/null +++ b/chrome/browser/ui/cocoa/notifications/balloon_controller.mm @@ -0,0 +1,241 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ui/cocoa/notifications/balloon_controller.h" + +#include "app/l10n_util.h" +#include "app/mac/nsimage_cache.h" +#include "app/resource_bundle.h" +#import "base/mac/cocoa_protocols.h" +#include "base/mac_util.h" +#import "base/scoped_nsobject.h" +#include "base/utf_string_conversions.h" +#include "chrome/browser/notifications/balloon.h" +#include "chrome/browser/notifications/desktop_notification_service.h" +#include "chrome/browser/notifications/notification.h" +#include "chrome/browser/notifications/notification_options_menu_model.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/renderer_host/render_view_host.h" +#import "chrome/browser/ui/cocoa/hover_image_button.h" +#import "chrome/browser/ui/cocoa/menu_controller.h" +#import "chrome/browser/ui/cocoa/notifications/balloon_view.h" +#include "chrome/browser/ui/cocoa/notifications/balloon_view_host_mac.h" +#include "grit/generated_resources.h" +#include "grit/theme_resources.h" + +namespace { + +// Margin, in pixels, between the notification frame and the contents +// of the notification. +const int kTopMargin = 1; +const int kBottomMargin = 2; +const int kLeftMargin = 2; +const int kRightMargin = 2; + +} // namespace + +@interface BalloonController (Private) +- (void)updateTrackingRect; +@end + +@implementation BalloonController + +- (id)initWithBalloon:(Balloon*)balloon { + NSString* nibpath = + [mac_util::MainAppBundle() pathForResource:@"Notification" + ofType:@"nib"]; + if ((self = [super initWithWindowNibPath:nibpath owner:self])) { + balloon_ = balloon; + [self initializeHost]; + menuModel_.reset(new NotificationOptionsMenuModel(balloon)); + menuController_.reset([[MenuController alloc] initWithModel:menuModel_.get() + useWithPopUpButtonCell:NO]); + } + return self; +} + +- (void)awakeFromNib { + DCHECK([self window]); + DCHECK_EQ(self, [[self window] delegate]); + + NSImage* image = app::mac::GetCachedImageWithName(@"balloon_wrench.pdf"); + [optionsButton_ setDefaultImage:image]; + [optionsButton_ setDefaultOpacity:0.6]; + [optionsButton_ setHoverImage:image]; + [optionsButton_ setHoverOpacity:0.9]; + [optionsButton_ setPressedImage:image]; + [optionsButton_ setPressedOpacity:1.0]; + [[optionsButton_ cell] setHighlightsBy:NSNoCellMask]; + + NSString* sourceLabelText = l10n_util::GetNSStringF( + IDS_NOTIFICATION_BALLOON_SOURCE_LABEL, + balloon_->notification().display_source()); + [originLabel_ setStringValue:sourceLabelText]; + + // This condition is false in unit tests which have no RVH. + if (htmlContents_.get()) { + gfx::NativeView contents = htmlContents_->native_view(); + [contents setFrame:NSMakeRect(kLeftMargin, kTopMargin, 0, 0)]; + [[htmlContainer_ superview] addSubview:contents + positioned:NSWindowBelow + relativeTo:nil]; + } + + // Use the standard close button for a utility window. + closeButton_ = [NSWindow standardWindowButton:NSWindowCloseButton + forStyleMask:NSUtilityWindowMask]; + NSRect frame = [closeButton_ frame]; + [closeButton_ setFrame:NSMakeRect(6, 1, frame.size.width, frame.size.height)]; + [closeButton_ setTarget:self]; + [closeButton_ setAction:@selector(closeButtonPressed:)]; + [shelf_ addSubview:closeButton_]; + [self updateTrackingRect]; + + // Set the initial position without animating (the balloon should not + // yet be visible). + DCHECK(![[self window] isVisible]); + NSRect balloon_frame = NSMakeRect(balloon_->GetPosition().x(), + balloon_->GetPosition().y(), + [self desiredTotalWidth], + [self desiredTotalHeight]); + [[self window] setFrame:balloon_frame + display:NO]; +} + +- (void)updateTrackingRect { + if (closeButtonTrackingTag_) + [shelf_ removeTrackingRect:closeButtonTrackingTag_]; + + closeButtonTrackingTag_ = [shelf_ addTrackingRect:[closeButton_ frame] + owner:self + userData:nil + assumeInside:NO]; +} + +- (BOOL)handleEvent:(NSEvent*)event { + BOOL eventHandled = NO; + if ([event type] == NSLeftMouseDown) { + NSPoint mouse = [shelf_ convertPoint:[event locationInWindow] + fromView:nil]; + if (NSPointInRect(mouse, [closeButton_ frame])) { + [closeButton_ mouseDown:event]; + + // Bring back the front process that is deactivated when we click the + // close button. + if (frontProcessNum_.highLongOfPSN || frontProcessNum_.lowLongOfPSN) { + SetFrontProcessWithOptions(&frontProcessNum_, + kSetFrontProcessFrontWindowOnly); + frontProcessNum_.highLongOfPSN = 0; + frontProcessNum_.lowLongOfPSN = 0; + } + + eventHandled = YES; + } else if (NSPointInRect(mouse, [optionsButton_ frame])) { + [optionsButton_ mouseDown:event]; + eventHandled = YES; + } + } + return eventHandled; +} + +- (void) mouseEntered:(NSEvent*)event { + [[closeButton_ cell] setHighlighted:YES]; + + // Remember the current front process so that we can bring it back later. + if (!frontProcessNum_.highLongOfPSN && !frontProcessNum_.lowLongOfPSN) + GetFrontProcess(&frontProcessNum_); +} + +- (void) mouseExited:(NSEvent*)event { + [[closeButton_ cell] setHighlighted:NO]; + + frontProcessNum_.highLongOfPSN = 0; + frontProcessNum_.lowLongOfPSN = 0; +} + +- (IBAction)optionsButtonPressed:(id)sender { + [NSMenu popUpContextMenu:[menuController_ menu] + withEvent:[NSApp currentEvent] + forView:optionsButton_]; +} + +- (IBAction)permissionRevoked:(id)sender { + DesktopNotificationService* service = + balloon_->profile()->GetDesktopNotificationService(); + service->DenyPermission(balloon_->notification().origin_url()); +} + +- (IBAction)closeButtonPressed:(id)sender { + [self closeBalloon:YES]; + [self close]; +} + +- (void)close { + if (closeButtonTrackingTag_) + [shelf_ removeTrackingRect:closeButtonTrackingTag_]; + + [super close]; +} + +- (void)closeBalloon:(bool)byUser { + if (!balloon_) + return; + [self close]; + if (htmlContents_.get()) + htmlContents_->Shutdown(); + if (balloon_) + balloon_->OnClose(byUser); + balloon_ = NULL; +} + +- (void)updateContents { + DCHECK(htmlContents_.get()) << "BalloonView::Update called before Show"; + if (htmlContents_->render_view_host()) + htmlContents_->render_view_host()->NavigateToURL( + balloon_->notification().content_url()); +} + +- (void)repositionToBalloon { + DCHECK(balloon_); + int x = balloon_->GetPosition().x(); + int y = balloon_->GetPosition().y(); + int w = [self desiredTotalWidth]; + int h = [self desiredTotalHeight]; + + if (htmlContents_.get()) + htmlContents_->UpdateActualSize(balloon_->content_size()); + + [[[self window] animator] setFrame:NSMakeRect(x, y, w, h) + display:YES]; +} + +// Returns the total width the view should be to accommodate the balloon. +- (int)desiredTotalWidth { + return (balloon_ ? balloon_->content_size().width() : 0) + + kLeftMargin + kRightMargin; +} + +// Returns the total height the view should be to accommodate the balloon. +- (int)desiredTotalHeight { + return (balloon_ ? balloon_->content_size().height() : 0) + + kTopMargin + kBottomMargin + [shelf_ frame].size.height; +} + +// Returns the BalloonHost { +- (BalloonViewHost*) getHost { + return htmlContents_.get(); +} + +// Initializes the renderer host showing the HTML contents. +- (void)initializeHost { + htmlContents_.reset(new BalloonViewHost(balloon_)); + htmlContents_->Init(); +} + +// NSWindowDelegate notification. +- (void)windowWillClose:(NSNotification*)notif { + [self autorelease]; +} + +@end |