| Index: chrome/browser/ui/cocoa/bookmarks/bookmark_drag_drop.mm
|
| diff --git a/chrome/browser/ui/cocoa/bookmarks/bookmark_drag_drop.mm b/chrome/browser/ui/cocoa/bookmarks/bookmark_drag_drop.mm
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..6718e7c1bf3fd9bcc77f4545f780b86460fe7740
|
| --- /dev/null
|
| +++ b/chrome/browser/ui/cocoa/bookmarks/bookmark_drag_drop.mm
|
| @@ -0,0 +1,190 @@
|
| +// Copyright 2012 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/bookmarks/bookmark_drag_drop.h"
|
| +
|
| +#import <Cocoa/Cocoa.h>
|
| +
|
| +#include <cmath>
|
| +
|
| +#include "base/logging.h"
|
| +#include "base/memory/scoped_nsobject.h"
|
| +#include "base/message_loop.h"
|
| +#include "base/string16.h"
|
| +#include "base/sys_string_conversions.h"
|
| +#include "chrome/browser/bookmarks/bookmark_model_factory.h"
|
| +#include "chrome/browser/bookmarks/bookmark_model.h"
|
| +#include "chrome/browser/bookmarks/bookmark_node_data.h"
|
| +#include "chrome/browser/bookmarks/bookmark_pasteboard_helper_mac.h"
|
| +#include "chrome/browser/bookmarks/bookmark_utils.h"
|
| +#include "chrome/browser/profiles/profile.h"
|
| +#import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.h"
|
| +#include "grit/ui_resources.h"
|
| +#include "ui/base/resource/resource_bundle.h"
|
| +#include "ui/gfx/mac/nsimage_cache.h"
|
| +#include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
|
| +
|
| +namespace chrome {
|
| +
|
| +namespace {
|
| +
|
| +// Make a drag image from the drop data.
|
| +NSImage* MakeDragImage(BookmarkModel* model,
|
| + const std::vector<const BookmarkNode*>& nodes) {
|
| + if (nodes.size() == 1) {
|
| + const BookmarkNode* node = nodes[0];
|
| + const gfx::Image& favicon = model->GetFavicon(node);
|
| + return DragImageForBookmark(
|
| + favicon.IsEmpty() ? nil : favicon.ToNSImage(), node->GetTitle());
|
| + } else {
|
| + // TODO(feldstein): Do something better than this. Should have badging
|
| + // and a single drag image.
|
| + // http://crbug.com/37264
|
| + return [NSImage imageNamed:NSImageNameMultipleDocuments];
|
| + }
|
| +}
|
| +
|
| +// Draws string |title| within box |frame|, positioning it at the origin.
|
| +// Truncates text with fading if it is too long to fit horizontally.
|
| +// Based on code from GradientButtonCell but simplified where possible.
|
| +void DrawTruncatedTitle(NSAttributedString* title, NSRect frame) {
|
| + NSSize size = [title size];
|
| + if (std::floor(size.width) <= NSWidth(frame)) {
|
| + [title drawAtPoint:frame.origin];
|
| + return;
|
| + }
|
| +
|
| + // Gradient is about twice our line height long.
|
| + CGFloat gradient_width = std::min(size.height * 2, NSWidth(frame) / 4);
|
| + NSRect solid_part, gradient_part;
|
| + NSDivideRect(frame, &gradient_part, &solid_part, gradient_width, NSMaxXEdge);
|
| + CGContextRef context = static_cast<CGContextRef>(
|
| + [[NSGraphicsContext currentContext] graphicsPort]);
|
| + CGContextBeginTransparencyLayerWithRect(context, NSRectToCGRect(frame), 0);
|
| + { // Draw text clipped to frame.
|
| + gfx::ScopedNSGraphicsContextSaveGState scoped_state;
|
| + [NSBezierPath clipRect:frame];
|
| + [title drawAtPoint:frame.origin];
|
| + }
|
| +
|
| + NSColor* color = [NSColor blackColor];
|
| + NSColor* alpha_color = [color colorWithAlphaComponent:0.0];
|
| + scoped_nsobject<NSGradient> mask(
|
| + [[NSGradient alloc] initWithStartingColor:color
|
| + endingColor:alpha_color]);
|
| + // Draw the gradient mask.
|
| + CGContextSetBlendMode(context, kCGBlendModeDestinationIn);
|
| + [mask drawFromPoint:NSMakePoint(NSMaxX(frame) - gradient_width,
|
| + NSMinY(frame))
|
| + toPoint:NSMakePoint(NSMaxX(frame),
|
| + NSMinY(frame))
|
| + options:NSGradientDrawsBeforeStartingLocation];
|
| + CGContextEndTransparencyLayer(context);
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +NSImage* DragImageForBookmark(NSImage* favicon, const string16& title) {
|
| + // If no favicon, use a default.
|
| + if (!favicon) {
|
| + ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
|
| + favicon = rb.GetNativeImageNamed(IDR_DEFAULT_FAVICON).ToNSImage();
|
| + }
|
| +
|
| + // If no title, just use icon.
|
| + if (title.empty())
|
| + return favicon;
|
| + NSString* ns_title = base::SysUTF16ToNSString(title);
|
| +
|
| + // Set the look of the title.
|
| + NSDictionary* attrs =
|
| + [NSDictionary dictionaryWithObject:[NSFont systemFontOfSize:
|
| + [NSFont smallSystemFontSize]]
|
| + forKey:NSFontAttributeName];
|
| + scoped_nsobject<NSAttributedString> rich_title(
|
| + [[NSAttributedString alloc] initWithString:ns_title
|
| + attributes:attrs]);
|
| +
|
| + // Set up sizes and locations for rendering.
|
| + const CGFloat kIconMargin = 2.0; // Gap between icon and text.
|
| + CGFloat text_left = [favicon size].width + kIconMargin;
|
| + NSSize drag_image_size = [favicon size];
|
| + NSSize text_size = [rich_title size];
|
| + CGFloat max_text_width = bookmarks::kDefaultBookmarkWidth - text_left;
|
| + text_size.width = std::min(text_size.width, max_text_width);
|
| + drag_image_size.width = text_left + text_size.width;
|
| +
|
| + // Render the drag image.
|
| + NSImage* drag_image =
|
| + [[[NSImage alloc] initWithSize:drag_image_size] autorelease];
|
| + [drag_image lockFocus];
|
| + [favicon drawAtPoint:NSMakePoint(0, 0)
|
| + fromRect:NSZeroRect
|
| + operation:NSCompositeSourceOver
|
| + fraction:0.7];
|
| + NSRect target_text_rect = NSMakeRect(text_left, 0,
|
| + text_size.width, drag_image_size.height);
|
| + DrawTruncatedTitle(rich_title, target_text_rect);
|
| + [drag_image unlockFocus];
|
| +
|
| + return drag_image;
|
| +}
|
| +
|
| +} // namespace chrome
|
| +
|
| +namespace bookmark_utils {
|
| +
|
| +void DragBookmarks(Profile* profile,
|
| + const std::vector<const BookmarkNode*>& nodes,
|
| + gfx::NativeView view) {
|
| + DCHECK(!nodes.empty());
|
| +
|
| + // Allow nested message loop so we get DnD events as we drag this around.
|
| + bool was_nested = MessageLoop::current()->IsNested();
|
| + MessageLoop::current()->SetNestableTasksAllowed(true);
|
| +
|
| + std::vector<BookmarkNodeData::Element> elements;
|
| + for (std::vector<const BookmarkNode*>::const_iterator it = nodes.begin();
|
| + it != nodes.end(); ++it) {
|
| + elements.push_back(BookmarkNodeData::Element(*it));
|
| + }
|
| +
|
| + bookmark_pasteboard_helper_mac::WriteToPasteboard(
|
| + bookmark_pasteboard_helper_mac::kDragPasteboard,
|
| + elements,
|
| + profile->GetPath().value());
|
| +
|
| + // Synthesize an event for dragging, since we can't be sure that
|
| + // [NSApp currentEvent] will return a valid dragging event.
|
| + NSWindow* window = [view window];
|
| + NSPoint position = [window mouseLocationOutsideOfEventStream];
|
| + NSTimeInterval event_time = [[NSApp currentEvent] timestamp];
|
| + NSEvent* drag_event = [NSEvent mouseEventWithType:NSLeftMouseDragged
|
| + location:position
|
| + modifierFlags:NSLeftMouseDraggedMask
|
| + timestamp:event_time
|
| + windowNumber:[window windowNumber]
|
| + context:nil
|
| + eventNumber:0
|
| + clickCount:1
|
| + pressure:1.0];
|
| +
|
| + // TODO(avi): Do better than this offset.
|
| + NSImage* drag_image = chrome::MakeDragImage(
|
| + BookmarkModelFactory::GetForProfile(profile), nodes);
|
| + NSSize image_size = [drag_image size];
|
| + position.x -= std::floor(image_size.width / 2);
|
| + position.y -= std::floor(image_size.height / 5);
|
| + [window dragImage:drag_image
|
| + at:position
|
| + offset:NSZeroSize
|
| + event:drag_event
|
| + pasteboard:[NSPasteboard pasteboardWithName:NSDragPboard]
|
| + source:nil
|
| + slideBack:YES];
|
| +
|
| + MessageLoop::current()->SetNestableTasksAllowed(was_nested);
|
| +}
|
| +
|
| +} // namespace bookmark_utils
|
|
|