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