| OLD | NEW |
| (Empty) | |
| 1 // Copyright 2012 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 #include "chrome/browser/ui/cocoa/bookmarks/bookmark_drag_drop.h" |
| 6 |
| 7 #import <Cocoa/Cocoa.h> |
| 8 |
| 9 #include <cmath> |
| 10 |
| 11 #include "base/logging.h" |
| 12 #include "base/memory/scoped_nsobject.h" |
| 13 #include "base/message_loop.h" |
| 14 #include "base/string16.h" |
| 15 #include "base/sys_string_conversions.h" |
| 16 #include "chrome/browser/bookmarks/bookmark_model_factory.h" |
| 17 #include "chrome/browser/bookmarks/bookmark_model.h" |
| 18 #include "chrome/browser/bookmarks/bookmark_node_data.h" |
| 19 #include "chrome/browser/bookmarks/bookmark_pasteboard_helper_mac.h" |
| 20 #include "chrome/browser/bookmarks/bookmark_utils.h" |
| 21 #include "chrome/browser/profiles/profile.h" |
| 22 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.h" |
| 23 #include "grit/ui_resources.h" |
| 24 #include "ui/base/resource/resource_bundle.h" |
| 25 #include "ui/gfx/mac/nsimage_cache.h" |
| 26 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h" |
| 27 |
| 28 namespace chrome { |
| 29 |
| 30 namespace { |
| 31 |
| 32 // Make a drag image from the drop data. |
| 33 NSImage* MakeDragImage(BookmarkModel* model, |
| 34 const std::vector<const BookmarkNode*>& nodes) { |
| 35 if (nodes.size() == 1) { |
| 36 const BookmarkNode* node = nodes[0]; |
| 37 const gfx::Image& favicon = model->GetFavicon(node); |
| 38 return DragImageForBookmark( |
| 39 favicon.IsEmpty() ? nil : favicon.ToNSImage(), node->GetTitle()); |
| 40 } else { |
| 41 // TODO(feldstein): Do something better than this. Should have badging |
| 42 // and a single drag image. |
| 43 // http://crbug.com/37264 |
| 44 return [NSImage imageNamed:NSImageNameMultipleDocuments]; |
| 45 } |
| 46 } |
| 47 |
| 48 // Draws string |title| within box |frame|, positioning it at the origin. |
| 49 // Truncates text with fading if it is too long to fit horizontally. |
| 50 // Based on code from GradientButtonCell but simplified where possible. |
| 51 void DrawTruncatedTitle(NSAttributedString* title, NSRect frame) { |
| 52 NSSize size = [title size]; |
| 53 if (std::floor(size.width) <= NSWidth(frame)) { |
| 54 [title drawAtPoint:frame.origin]; |
| 55 return; |
| 56 } |
| 57 |
| 58 // Gradient is about twice our line height long. |
| 59 CGFloat gradient_width = std::min(size.height * 2, NSWidth(frame) / 4); |
| 60 NSRect solid_part, gradient_part; |
| 61 NSDivideRect(frame, &gradient_part, &solid_part, gradient_width, NSMaxXEdge); |
| 62 CGContextRef context = static_cast<CGContextRef>( |
| 63 [[NSGraphicsContext currentContext] graphicsPort]); |
| 64 CGContextBeginTransparencyLayerWithRect(context, NSRectToCGRect(frame), 0); |
| 65 { // Draw text clipped to frame. |
| 66 gfx::ScopedNSGraphicsContextSaveGState scoped_state; |
| 67 [NSBezierPath clipRect:frame]; |
| 68 [title drawAtPoint:frame.origin]; |
| 69 } |
| 70 |
| 71 NSColor* color = [NSColor blackColor]; |
| 72 NSColor* alpha_color = [color colorWithAlphaComponent:0.0]; |
| 73 scoped_nsobject<NSGradient> mask( |
| 74 [[NSGradient alloc] initWithStartingColor:color |
| 75 endingColor:alpha_color]); |
| 76 // Draw the gradient mask. |
| 77 CGContextSetBlendMode(context, kCGBlendModeDestinationIn); |
| 78 [mask drawFromPoint:NSMakePoint(NSMaxX(frame) - gradient_width, |
| 79 NSMinY(frame)) |
| 80 toPoint:NSMakePoint(NSMaxX(frame), |
| 81 NSMinY(frame)) |
| 82 options:NSGradientDrawsBeforeStartingLocation]; |
| 83 CGContextEndTransparencyLayer(context); |
| 84 } |
| 85 |
| 86 } // namespace |
| 87 |
| 88 NSImage* DragImageForBookmark(NSImage* favicon, const string16& title) { |
| 89 // If no favicon, use a default. |
| 90 if (!favicon) { |
| 91 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
| 92 favicon = rb.GetNativeImageNamed(IDR_DEFAULT_FAVICON).ToNSImage(); |
| 93 } |
| 94 |
| 95 // If no title, just use icon. |
| 96 if (title.empty()) |
| 97 return favicon; |
| 98 NSString* ns_title = base::SysUTF16ToNSString(title); |
| 99 |
| 100 // Set the look of the title. |
| 101 NSDictionary* attrs = |
| 102 [NSDictionary dictionaryWithObject:[NSFont systemFontOfSize: |
| 103 [NSFont smallSystemFontSize]] |
| 104 forKey:NSFontAttributeName]; |
| 105 scoped_nsobject<NSAttributedString> rich_title( |
| 106 [[NSAttributedString alloc] initWithString:ns_title |
| 107 attributes:attrs]); |
| 108 |
| 109 // Set up sizes and locations for rendering. |
| 110 const CGFloat kIconMargin = 2.0; // Gap between icon and text. |
| 111 CGFloat text_left = [favicon size].width + kIconMargin; |
| 112 NSSize drag_image_size = [favicon size]; |
| 113 NSSize text_size = [rich_title size]; |
| 114 CGFloat max_text_width = bookmarks::kDefaultBookmarkWidth - text_left; |
| 115 text_size.width = std::min(text_size.width, max_text_width); |
| 116 drag_image_size.width = text_left + text_size.width; |
| 117 |
| 118 // Render the drag image. |
| 119 NSImage* drag_image = |
| 120 [[[NSImage alloc] initWithSize:drag_image_size] autorelease]; |
| 121 [drag_image lockFocus]; |
| 122 [favicon drawAtPoint:NSMakePoint(0, 0) |
| 123 fromRect:NSZeroRect |
| 124 operation:NSCompositeSourceOver |
| 125 fraction:0.7]; |
| 126 NSRect target_text_rect = NSMakeRect(text_left, 0, |
| 127 text_size.width, drag_image_size.height); |
| 128 DrawTruncatedTitle(rich_title, target_text_rect); |
| 129 [drag_image unlockFocus]; |
| 130 |
| 131 return drag_image; |
| 132 } |
| 133 |
| 134 } // namespace chrome |
| 135 |
| 136 namespace bookmark_utils { |
| 137 |
| 138 void DragBookmarks(Profile* profile, |
| 139 const std::vector<const BookmarkNode*>& nodes, |
| 140 gfx::NativeView view) { |
| 141 DCHECK(!nodes.empty()); |
| 142 |
| 143 // Allow nested message loop so we get DnD events as we drag this around. |
| 144 bool was_nested = MessageLoop::current()->IsNested(); |
| 145 MessageLoop::current()->SetNestableTasksAllowed(true); |
| 146 |
| 147 std::vector<BookmarkNodeData::Element> elements; |
| 148 for (std::vector<const BookmarkNode*>::const_iterator it = nodes.begin(); |
| 149 it != nodes.end(); ++it) { |
| 150 elements.push_back(BookmarkNodeData::Element(*it)); |
| 151 } |
| 152 |
| 153 bookmark_pasteboard_helper_mac::WriteToPasteboard( |
| 154 bookmark_pasteboard_helper_mac::kDragPasteboard, |
| 155 elements, |
| 156 profile->GetPath().value()); |
| 157 |
| 158 // Synthesize an event for dragging, since we can't be sure that |
| 159 // [NSApp currentEvent] will return a valid dragging event. |
| 160 NSWindow* window = [view window]; |
| 161 NSPoint position = [window mouseLocationOutsideOfEventStream]; |
| 162 NSTimeInterval event_time = [[NSApp currentEvent] timestamp]; |
| 163 NSEvent* drag_event = [NSEvent mouseEventWithType:NSLeftMouseDragged |
| 164 location:position |
| 165 modifierFlags:NSLeftMouseDraggedMask |
| 166 timestamp:event_time |
| 167 windowNumber:[window windowNumber] |
| 168 context:nil |
| 169 eventNumber:0 |
| 170 clickCount:1 |
| 171 pressure:1.0]; |
| 172 |
| 173 // TODO(avi): Do better than this offset. |
| 174 NSImage* drag_image = chrome::MakeDragImage( |
| 175 BookmarkModelFactory::GetForProfile(profile), nodes); |
| 176 NSSize image_size = [drag_image size]; |
| 177 position.x -= std::floor(image_size.width / 2); |
| 178 position.y -= std::floor(image_size.height / 5); |
| 179 [window dragImage:drag_image |
| 180 at:position |
| 181 offset:NSZeroSize |
| 182 event:drag_event |
| 183 pasteboard:[NSPasteboard pasteboardWithName:NSDragPboard] |
| 184 source:nil |
| 185 slideBack:YES]; |
| 186 |
| 187 MessageLoop::current()->SetNestableTasksAllowed(was_nested); |
| 188 } |
| 189 |
| 190 } // namespace bookmark_utils |
| OLD | NEW |