| // 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/bookmarks/bookmark_pasteboard_helper_mac.h" |
| |
| #import <Cocoa/Cocoa.h> |
| |
| #include "base/sys_string_conversions.h" |
| #include "chrome/browser/bookmarks/bookmark_model.h" |
| #include "chrome/browser/ui/cocoa/bookmarks/bookmark_drag_source.h" |
| #include "chrome/browser/tab_contents/tab_contents_view_mac.h" |
| |
| NSString* const kBookmarkDictionaryListPboardType = |
| @"BookmarkDictionaryListPboardType"; |
| |
| namespace { |
| |
| // An unofficial standard pasteboard title type to be provided alongside the |
| // |NSURLPboardType|. |
| NSString* const kNSURLTitlePboardType = |
| @"public.url-name"; |
| |
| // Pasteboard type used to store profile path to determine which profile |
| // a set of bookmarks came from. |
| NSString* const kChromiumProfilePathPboardType = |
| @"ChromiumProfilePathPboardType"; |
| |
| // Internal bookmark ID for a bookmark node. Used only when moving inside |
| // of one profile. |
| NSString* const kChromiumBookmarkId = |
| @"ChromiumBookmarkId"; |
| |
| // Mac WebKit uses this type, declared in |
| // WebKit/mac/History/WebURLsWithTitles.h. |
| NSString* const kWebURLsWithTitlesPboardType = |
| @"WebURLsWithTitlesPboardType"; |
| |
| // Keys for the type of node in BookmarkDictionaryListPboardType. |
| NSString* const kWebBookmarkType = |
| @"WebBookmarkType"; |
| |
| NSString* const kWebBookmarkTypeList = |
| @"WebBookmarkTypeList"; |
| |
| NSString* const kWebBookmarkTypeLeaf = |
| @"WebBookmarkTypeLeaf"; |
| |
| void ConvertPlistToElements(NSArray* input, |
| std::vector<BookmarkNodeData::Element>& elements) { |
| NSUInteger len = [input count]; |
| for (NSUInteger i = 0; i < len; ++i) { |
| NSDictionary* pboardBookmark = [input objectAtIndex:i]; |
| scoped_ptr<BookmarkNode> new_node(new BookmarkNode(0, GURL())); |
| int64 node_id = |
| [[pboardBookmark objectForKey:kChromiumBookmarkId] longLongValue]; |
| new_node->set_id(node_id); |
| BOOL is_folder = [[pboardBookmark objectForKey:kWebBookmarkType] |
| isEqualToString:kWebBookmarkTypeList]; |
| if (is_folder) { |
| new_node->set_type(BookmarkNode::FOLDER); |
| NSString* title = [pboardBookmark objectForKey:@"Title"]; |
| new_node->set_title(base::SysNSStringToUTF16(title)); |
| } else { |
| new_node->set_type(BookmarkNode::URL); |
| NSDictionary* uriDictionary = |
| [pboardBookmark objectForKey:@"URIDictionary"]; |
| NSString* title = [uriDictionary objectForKey:@"title"]; |
| NSString* urlString = [pboardBookmark objectForKey:@"URLString"]; |
| new_node->set_title(base::SysNSStringToUTF16(title)); |
| new_node->SetURL(GURL(base::SysNSStringToUTF8(urlString))); |
| } |
| BookmarkNodeData::Element e = BookmarkNodeData::Element(new_node.get()); |
| if(is_folder) |
| ConvertPlistToElements([pboardBookmark objectForKey:@"Children"], |
| e.children); |
| elements.push_back(e); |
| } |
| } |
| |
| bool ReadBookmarkDictionaryListPboardType(NSPasteboard* pb, |
| std::vector<BookmarkNodeData::Element>& elements) { |
| NSArray* bookmarks = |
| [pb propertyListForType:kBookmarkDictionaryListPboardType]; |
| if (!bookmarks) return false; |
| ConvertPlistToElements(bookmarks, elements); |
| return true; |
| } |
| |
| bool ReadWebURLsWithTitlesPboardType(NSPasteboard* pb, |
| std::vector<BookmarkNodeData::Element>& elements) { |
| NSArray* bookmarkPairs = |
| [pb propertyListForType:kWebURLsWithTitlesPboardType]; |
| if (![bookmarkPairs isKindOfClass:[NSArray class]]) { |
| return false; |
| } |
| NSArray* urlsArr = [bookmarkPairs objectAtIndex:0]; |
| NSArray* titlesArr = [bookmarkPairs objectAtIndex:1]; |
| if ([urlsArr count] < 1) { |
| return false; |
| } |
| if ([urlsArr count] != [titlesArr count]) { |
| return false; |
| } |
| |
| NSUInteger len = [titlesArr count]; |
| for (NSUInteger i = 0; i < len; ++i) { |
| string16 title = base::SysNSStringToUTF16([titlesArr objectAtIndex:i]); |
| std::string url = base::SysNSStringToUTF8([urlsArr objectAtIndex:i]); |
| if (!url.empty()) { |
| BookmarkNodeData::Element element; |
| element.is_url = true; |
| element.url = GURL(url); |
| element.title = title; |
| elements.push_back(element); |
| } |
| } |
| return true; |
| } |
| |
| bool ReadNSURLPboardType(NSPasteboard* pb, |
| std::vector<BookmarkNodeData::Element>& elements) { |
| NSURL* url = [NSURL URLFromPasteboard:pb]; |
| if (url == nil) { |
| return false; |
| } |
| std::string urlString = base::SysNSStringToUTF8([url absoluteString]); |
| NSString* title = [pb stringForType:kNSURLTitlePboardType]; |
| if (!title) |
| title = [pb stringForType:NSStringPboardType]; |
| |
| BookmarkNodeData::Element element; |
| element.is_url = true; |
| element.url = GURL(urlString); |
| element.title = base::SysNSStringToUTF16(title); |
| elements.push_back(element); |
| return true; |
| } |
| |
| NSArray* GetPlistForBookmarkList( |
| const std::vector<BookmarkNodeData::Element>& elements) { |
| NSMutableArray* plist = [NSMutableArray array]; |
| for (size_t i = 0; i < elements.size(); ++i) { |
| BookmarkNodeData::Element element = elements[i]; |
| if (element.is_url) { |
| NSString* title = base::SysUTF16ToNSString(element.title); |
| NSString* url = base::SysUTF8ToNSString(element.url.spec()); |
| int64 elementId = element.get_id(); |
| NSNumber* idNum = [NSNumber numberWithLongLong:elementId]; |
| NSDictionary* uriDictionary = [NSDictionary dictionaryWithObjectsAndKeys: |
| title, @"title", nil]; |
| NSDictionary* object = [NSDictionary dictionaryWithObjectsAndKeys: |
| uriDictionary, @"URIDictionary", |
| url, @"URLString", |
| kWebBookmarkTypeLeaf, kWebBookmarkType, |
| idNum, kChromiumBookmarkId, |
| nil]; |
| [plist addObject:object]; |
| } else { |
| NSString* title = base::SysUTF16ToNSString(element.title); |
| NSArray* children = GetPlistForBookmarkList(element.children); |
| int64 elementId = element.get_id(); |
| NSNumber* idNum = [NSNumber numberWithLongLong:elementId]; |
| NSDictionary* object = [NSDictionary dictionaryWithObjectsAndKeys: |
| title, @"Title", |
| children, @"Children", |
| kWebBookmarkTypeList, kWebBookmarkType, |
| idNum, kChromiumBookmarkId, |
| nil]; |
| [plist addObject:object]; |
| } |
| } |
| return plist; |
| } |
| |
| void WriteBookmarkDictionaryListPboardType(NSPasteboard* pb, |
| const std::vector<BookmarkNodeData::Element>& elements) { |
| NSArray* plist = GetPlistForBookmarkList(elements); |
| [pb setPropertyList:plist forType:kBookmarkDictionaryListPboardType]; |
| } |
| |
| void FillFlattenedArraysForBookmarks( |
| const std::vector<BookmarkNodeData::Element>& elements, |
| NSMutableArray* titles, NSMutableArray* urls) { |
| for (size_t i = 0; i < elements.size(); ++i) { |
| BookmarkNodeData::Element element = elements[i]; |
| if (element.is_url) { |
| NSString* title = base::SysUTF16ToNSString(element.title); |
| NSString* url = base::SysUTF8ToNSString(element.url.spec()); |
| [titles addObject:title]; |
| [urls addObject:url]; |
| } else { |
| FillFlattenedArraysForBookmarks(element.children, titles, urls); |
| } |
| } |
| } |
| |
| void WriteSimplifiedBookmarkTypes(NSPasteboard* pb, |
| const std::vector<BookmarkNodeData::Element>& elements) { |
| NSMutableArray* titles = [NSMutableArray array]; |
| NSMutableArray* urls = [NSMutableArray array]; |
| FillFlattenedArraysForBookmarks(elements, titles, urls); |
| |
| // These bookmark types only act on urls, not folders. |
| if ([urls count] < 1) |
| return; |
| |
| // Write WebURLsWithTitlesPboardType. |
| [pb setPropertyList:[NSArray arrayWithObjects:urls, titles, nil] |
| forType:kWebURLsWithTitlesPboardType]; |
| |
| // Write NSStringPboardType. |
| [pb setString:[urls componentsJoinedByString:@"\n"] |
| forType:NSStringPboardType]; |
| |
| // Write NSURLPboardType (with title). |
| NSURL* url = [NSURL URLWithString:[urls objectAtIndex:0]]; |
| [url writeToPasteboard:pb]; |
| NSString* titleString = [titles objectAtIndex:0]; |
| [pb setString:titleString forType:kNSURLTitlePboardType]; |
| } |
| |
| void WriteToClipboardPrivate( |
| const std::vector<BookmarkNodeData::Element>& elements, |
| NSPasteboard* pb, |
| FilePath::StringType profile_path) { |
| if (elements.empty()) |
| return; |
| |
| NSArray* types = [NSArray arrayWithObjects:kBookmarkDictionaryListPboardType, |
| kWebURLsWithTitlesPboardType, |
| NSStringPboardType, |
| NSURLPboardType, |
| kNSURLTitlePboardType, |
| kChromiumProfilePathPboardType, |
| nil]; |
| [pb declareTypes:types owner:nil]; |
| [pb setString:base::SysUTF8ToNSString(profile_path) |
| forType:kChromiumProfilePathPboardType]; |
| WriteBookmarkDictionaryListPboardType(pb, elements); |
| WriteSimplifiedBookmarkTypes(pb, elements); |
| } |
| |
| bool ReadFromClipboardPrivate( |
| std::vector<BookmarkNodeData::Element>& elements, |
| NSPasteboard* pb, |
| FilePath::StringType* profile_path) { |
| elements.clear(); |
| NSString* profile = [pb stringForType:kChromiumProfilePathPboardType]; |
| profile_path->assign(base::SysNSStringToUTF8(profile)); |
| return (ReadBookmarkDictionaryListPboardType(pb, elements) || |
| ReadWebURLsWithTitlesPboardType(pb, elements) || |
| ReadNSURLPboardType(pb, elements)); |
| } |
| |
| bool ClipboardContainsBookmarksPrivate(NSPasteboard* pb) { |
| NSArray* availableTypes = |
| [NSArray arrayWithObjects:kBookmarkDictionaryListPboardType, |
| kWebURLsWithTitlesPboardType, |
| NSURLPboardType, |
| nil]; |
| return [pb availableTypeFromArray:availableTypes] != nil; |
| } |
| |
| } // anonymous namespace |
| |
| namespace bookmark_pasteboard_helper_mac { |
| |
| void WriteToClipboard(const std::vector<BookmarkNodeData::Element>& elements, |
| FilePath::StringType profile_path) { |
| NSPasteboard* pb = [NSPasteboard generalPasteboard]; |
| WriteToClipboardPrivate(elements, pb, profile_path); |
| } |
| |
| void WriteToDragClipboard( |
| const std::vector<BookmarkNodeData::Element>& elements, |
| FilePath::StringType profile_path) { |
| NSPasteboard* pb = [NSPasteboard pasteboardWithName:NSDragPboard]; |
| WriteToClipboardPrivate(elements, pb, profile_path); |
| } |
| |
| bool ReadFromClipboard(std::vector<BookmarkNodeData::Element>& elements, |
| FilePath::StringType* profile_path) { |
| NSPasteboard* pb = [NSPasteboard generalPasteboard]; |
| return ReadFromClipboardPrivate(elements, pb, profile_path); |
| } |
| |
| bool ReadFromDragClipboard(std::vector<BookmarkNodeData::Element>& elements, |
| FilePath::StringType* profile_path) { |
| NSPasteboard* pb = [NSPasteboard pasteboardWithName:NSDragPboard]; |
| return ReadFromClipboardPrivate(elements, pb, profile_path); |
| } |
| |
| |
| bool ClipboardContainsBookmarks() { |
| NSPasteboard* pb = [NSPasteboard generalPasteboard]; |
| return ClipboardContainsBookmarksPrivate(pb); |
| } |
| |
| bool DragClipboardContainsBookmarks() { |
| NSPasteboard* pb = [NSPasteboard pasteboardWithName:NSDragPboard]; |
| return ClipboardContainsBookmarksPrivate(pb); |
| } |
| |
| void StartDrag(Profile* profile, const std::vector<const BookmarkNode*>& nodes, |
| gfx::NativeView view) { |
| DCHECK([view isKindOfClass:[TabContentsViewCocoa class]]); |
| TabContentsViewCocoa* tabView = static_cast<TabContentsViewCocoa*>(view); |
| 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)); |
| } |
| NSPasteboard* pb = [NSPasteboard pasteboardWithName:NSDragPboard]; |
| scoped_nsobject<BookmarkDragSource> source([[BookmarkDragSource alloc] |
| initWithContentsView:tabView |
| dropData:elements |
| profile:profile |
| pasteboard:pb |
| dragOperationMask:NSDragOperationEvery]); |
| [source startDrag]; |
| } |
| |
| } // namespace bookmark_pasteboard_helper_mac |