Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(425)

Side by Side Diff: chrome/browser/cocoa/web_drag_source.mm

Issue 160083: Enable dragging of images to desktop (Finder), Preview, etc. (on Mac).... (Closed) Base URL: http://src.chromium.org/svn/trunk/src/
Patch Set: '' Created 11 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
Property Changes:
Name: svn:eol-style
+ LF
OLDNEW
(Empty)
1 // Copyright (c) 2009 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 #import "chrome/browser/cocoa/web_drag_source.h"
6
7 #include "base/file_util.h"
8 #include "base/string_util.h"
9 #include "base/sys_string_conversions.h"
10 #include "base/task.h"
11 #include "base/thread.h"
12 #include "chrome/browser/browser_process.h"
13 #include "chrome/browser/cocoa/nsimage_cache.h"
14 #include "chrome/browser/renderer_host/render_view_host.h"
15 #include "chrome/browser/tab_contents/tab_contents.h"
16 #include "chrome/browser/tab_contents/tab_contents_view_mac.h"
17 #include "net/base/file_stream.h"
18 #include "net/base/net_errors.h"
19 #include "net/base/net_util.h"
20 #import "third_party/mozilla/include/NSPasteboard+Utils.h"
21 #include "webkit/glue/webdropdata.h"
22
23 using base::SysNSStringToUTF8;
24 using base::SysUTF8ToNSString;
25 using base::SysUTF16ToNSString;
26 using net::FileStream;
27
28
29 namespace {
30
31 // Make a drag image from the drop data.
32 // TODO(viettrungluu@gmail.com): Move this somewhere more sensible.
33 NSImage* MakeDragImage(const WebDropData* drop_data) {
34 // TODO(viettrungluu@gmail.com): Just a stub for now. Make it do something.
35
36 // Default to returning a generic image.
37 return nsimage_cache::ImageNamed(@"nav.pdf");
38 }
39
40 // Returns a filename appropriate for the drop data (of form "FILENAME-seq.EXT"
41 // if seq > 0).
42 // TODO(viettrungluu@gmail.com): Refactor to make it common across platforms,
43 // and move it somewhere sensible.
44 FilePath GetFileNameFromDragData(
45 const WebDropData& drop_data, unsigned seq) {
46 // Images without ALT text will only have a file extension so we need to
47 // synthesize one from the provided extension and URL.
48 FilePath file_name([SysUTF16ToNSString(drop_data.file_description_filename)
49 fileSystemRepresentation]);
50 file_name = file_name.BaseName().RemoveExtension();
51
52 if (file_name.empty()) {
53 // Retrieve the name from the URL.
54 file_name = FilePath::FromWStringHack(
55 net::GetSuggestedFilename(drop_data.url, "", "", L""));
56 }
57
58 file_name = file_name.ReplaceExtension([SysUTF16ToNSString(
59 drop_data.file_extension) fileSystemRepresentation]);
60
61 if (seq > 0) {
62 file_name =
63 file_name.InsertBeforeExtension(std::string("-")+UintToString(seq));
64 }
65
66 return file_name;
67 }
68
69 // This class's sole task is to write out data for a promised file; the caller
70 // is responsible for opening the file.
71 class PromiseWriterTask : public Task {
72 public:
73 // Assumes ownership of file_stream.
74 PromiseWriterTask(const WebDropData& drop_data,
75 FileStream* file_stream);
76 virtual ~PromiseWriterTask();
77 virtual void Run();
78
79 private:
80 WebDropData drop_data_;
81
82 // This class takes ownership of file_stream_ and will close and delete it.
83 scoped_ptr<FileStream> file_stream_;
84 };
85
86 // Takes the drop data and an open file stream (which it takes ownership of and
87 // will close and delete).
88 PromiseWriterTask::PromiseWriterTask(const WebDropData& drop_data,
89 FileStream* file_stream) :
90 drop_data_(drop_data) {
91 file_stream_.reset(file_stream);
92 DCHECK(file_stream_.get());
93 }
94
95 PromiseWriterTask::~PromiseWriterTask() {
96 DCHECK(file_stream_.get());
97 if (file_stream_.get())
98 file_stream_->Close();
99 }
100
101 void PromiseWriterTask::Run() {
102 CHECK(file_stream_.get());
103 file_stream_->Write(drop_data_.file_contents.data(),
104 drop_data_.file_contents.length(),
105 NULL);
106
107 // Let our destructor take care of business.
108 }
109
110 } // namespace
111
112
113 @interface WebDragSource(Private)
114
115 - (void)fillPasteboard;
116 - (NSImage*)dragImage;
117
118 @end // @interface WebDragSource(Private)
119
120
121 @implementation WebDragSource
122
123 - (id)initWithContentsView:(TabContentsViewCocoa*)contentsView
124 dropData:(const WebDropData*)dropData
125 pasteboard:(NSPasteboard*)pboard {
126 if ((self = [super init])) {
127 contentsView_ = contentsView;
128 DCHECK(contentsView_);
129
130 dropData_.reset(new WebDropData(*dropData));
131 DCHECK(dropData_.get());
132
133 pasteboard_.reset([pboard retain]);
134 DCHECK(pasteboard_.get());
135
136 [self fillPasteboard];
137 }
138
139 return self;
140 }
141
142 - (void)lazyWriteToPasteboard:(NSPasteboard*)pboard forType:(NSString*)type {
143 // Be extra paranoid; avoid crashing.
144 if (!dropData_.get()) {
145 NOTREACHED() << "No drag-and-drop data available for lazy write.";
146 return;
147 }
148
149 // HTML.
150 if ([type isEqualToString:NSHTMLPboardType]) {
151 DCHECK(!dropData_->text_html.empty());
152 [pboard setString:SysUTF16ToNSString(dropData_->text_html)
153 forType:NSHTMLPboardType];
154
155 // URL.
156 } else if ([type isEqualToString:NSURLPboardType]) {
157 DCHECK(dropData_->url.is_valid());
158 [pboard setURLs:[NSArray
159 arrayWithObject:SysUTF8ToNSString(dropData_->url.spec())]
160 withTitles:[NSArray arrayWithObject:
161 SysUTF16ToNSString(dropData_->url_title)]];
162
163 // File contents.
164 } else if ([type isEqualToString:NSFileContentsPboardType] ||
165 [type isEqualToString:NSCreateFileContentsPboardType(
166 SysUTF16ToNSString(dropData_->file_extension))]) {
167 // TODO(viettrungluu@gmail.com): find something which is known to accept
168 // NSFileContentsPboardType to check that this actually works!
169 scoped_nsobject<NSFileWrapper> file_wrapper(
170 [[NSFileWrapper alloc] initRegularFileWithContents:[NSData
171 dataWithBytes:dropData_->file_contents.data()
172 length:dropData_->file_contents.length()]]);
173 [file_wrapper setPreferredFilename:SysUTF8ToNSString(
174 GetFileNameFromDragData(*dropData_, 0).value())];
175 [pboard writeFileWrapper:file_wrapper];
176
177 // TIFF.
178 } else if ([type isEqualToString:NSTIFFPboardType]) {
179 // FIXME(viettrungluu@gmail.com): This is a bit odd since we rely on Cocoa
180 // to render our image into a TIFF. This is also suboptimal since this is
181 // all done synchronously. I'm not sure there's much we can easily do about
182 // it.
183 scoped_nsobject<NSImage> image(
184 [[NSImage alloc] initWithData:[NSData
185 dataWithBytes:dropData_->file_contents.data()
186 length:dropData_->file_contents.length()]]);
187 [pboard setData:[image TIFFRepresentation] forType:NSTIFFPboardType];
188
189 // Plain text.
190 } else if ([type isEqualToString:NSStringPboardType]) {
191 DCHECK(!dropData_->plain_text.empty());
192 [pboard setString:SysUTF16ToNSString(dropData_->plain_text)
193 forType:NSStringPboardType];
194
195 // Oops!
196 } else {
197 NOTREACHED() << "Asked for a drag pasteboard type we didn't offer.";
198 }
199 }
200
201 - (void)startDrag {
202 NSEvent* currentEvent = [NSApp currentEvent];
203 [contentsView_ dragImage:[self dragImage]
204 at:[contentsView_
205 convertPoint:[currentEvent locationInWindow]
206 fromView:nil]
207 offset:NSZeroSize
208 event:currentEvent
209 pasteboard:pasteboard_
210 source:contentsView_
211 slideBack:YES];
212 }
213
214 - (void)endDragAt:(NSPoint)screenPoint
215 isCancelled:(BOOL)cancelled {
216 RenderViewHost* rvh = [contentsView_ tabContents]->render_view_host();
217 if (rvh) {
218 rvh->DragSourceSystemDragEnded();
219
220 // Convert |screenPoint| to view coordinates and flip it.
221 NSPoint localPoint = [contentsView_ convertPointFromBase:screenPoint];
222 NSRect viewFrame = [contentsView_ frame];
223 localPoint.y = viewFrame.size.height - localPoint.y;
224 // Flip |screenPoint|.
225 NSRect screenFrame = [[[contentsView_ window] screen] frame];
226 screenPoint.y = screenFrame.size.height - screenPoint.y;
227
228 if (cancelled) {
229 rvh->DragSourceCancelledAt(localPoint.x, localPoint.y,
230 screenPoint.x, screenPoint.y);
231 } else {
232 rvh->DragSourceEndedAt(localPoint.x, localPoint.y,
233 screenPoint.x, screenPoint.y);
234 }
235 }
236
237 // Make sure the pasteboard owner isn't us.
238 [pasteboard_ declareTypes:[NSArray array] owner:nil];
239 }
240
241 - (void)moveDragTo:(NSPoint)screenPoint {
242 RenderViewHost* rvh = [contentsView_ tabContents]->render_view_host();
243 if (rvh) {
244 // Convert |screenPoint| to view coordinates and flip it.
245 NSPoint localPoint = [contentsView_ convertPointFromBase:screenPoint];
246 NSRect viewFrame = [contentsView_ frame];
247 localPoint.y = viewFrame.size.height - localPoint.y;
248 // Flip |screenPoint|.
249 NSRect screenFrame = [[[contentsView_ window] screen] frame];
250 screenPoint.y = screenFrame.size.height - screenPoint.y;
251
252 rvh->DragSourceMovedTo(localPoint.x, localPoint.y,
253 screenPoint.x, screenPoint.y);
254 }
255 }
256
257 - (NSString*)dragPromisedFileTo:(NSString*)path {
258 // Be extra paranoid; avoid crashing.
259 if (!dropData_.get()) {
260 NOTREACHED() << "No drag-and-drop data available for promised file.";
261 return nil;
262 }
263
264 FileStream* file_stream = new FileStream;
265 DCHECK(file_stream);
266 if (!file_stream)
267 return nil;
268
269 FilePath path_name(SysNSStringToUTF8(path));
270 FilePath file_name;
271 unsigned seq;
272 const unsigned k_max_seq = 99;
273 for (seq = 0; seq <= k_max_seq; seq++) {
274 file_name = GetFileNameFromDragData(*dropData_, seq);
275 FilePath file_path = path_name.Append(file_name);
276
277 // Explicitly (and redundantly check) for file -- despite the fact that our
278 // open won't overwrite -- just to avoid log spew.
279 if (!file_util::PathExists(file_path) &&
280 file_stream->Open(path_name.Append(file_name),
281 base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE) == net::OK)
282 break;
283 }
284 if (seq > k_max_seq) {
285 delete file_stream;
286 return nil;
287 }
288
289 // The writer will take care of closing and deletion.
290 g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE,
291 new PromiseWriterTask(*dropData_, file_stream));
292
293 // Once we've created the file, we should return the file name.
294 return SysUTF8ToNSString(file_name.value());
295 }
296
297 @end // @implementation WebDragSource
298
299
300 @implementation WebDragSource (Private)
301
302 - (void)fillPasteboard {
303 DCHECK(pasteboard_.get());
304
305 [pasteboard_ declareTypes:[NSArray array] owner:contentsView_];
306
307 // HTML.
308 if (!dropData_->text_html.empty())
309 [pasteboard_ addTypes:[NSArray arrayWithObject:NSHTMLPboardType]
310 owner:contentsView_];
311
312 // URL.
313 if (dropData_->url.is_valid())
314 [pasteboard_ addTypes:[NSArray arrayWithObject:NSURLPboardType]
315 owner:contentsView_];
316
317 // File.
318 if (!dropData_->file_contents.empty()) {
319 NSString* file_ext = SysUTF16ToNSString(dropData_->file_extension);
320
321 // File contents (with and without specific type), file (HFS) promise, TIFF.
322 // TODO(viettrungluu@gmail.com): others?
323 [pasteboard_ addTypes:[NSArray arrayWithObjects:
324 NSFileContentsPboardType,
325 NSCreateFileContentsPboardType(file_ext),
326 NSFilesPromisePboardType,
327 NSTIFFPboardType,
328 nil]
329 owner:contentsView_];
330
331 // For the file promise, we need to specify the extension.
332 [pasteboard_ setPropertyList:[NSArray arrayWithObject:file_ext]
333 forType:NSFilesPromisePboardType];
334 }
335
336 // Plain text.
337 if (!dropData_->plain_text.empty())
338 [pasteboard_ addTypes:[NSArray arrayWithObject:NSStringPboardType]
339 owner:contentsView_];
340 }
341
342 - (NSImage*)dragImage {
343 return MakeDragImage(dropData_.get());
344 }
345
346 @end // @implementation WebDragSource (Private)
OLDNEW
« no previous file with comments | « chrome/browser/cocoa/web_drag_source.h ('k') | chrome/browser/tab_contents/tab_contents_view_mac.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698