1 /*
2 * Copyright (C) 2007 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #include "config.h"
27 #include "WebDragClient.h"
28 #include "WebDropSource.h"
29 #include "WebKitGraphics.h"
30 #include "WebView.h"
31
32 #include <shlobj.h>
33
34 #pragma warning(push, 0)
35 #include <WebCore/ClipboardWin.h>
36 #include <WebCore/DragData.h>
37 #include <WebCore/Font.h>
38 #include <WebCore/FontDescription.h>
39 #include <WebCore/FontSelector.h>
40 #include <WebCore/FrameView.h>
41 #include <WebCore/GraphicsContext.h>
42 #include <WebCore/Page.h>
43 #include <WebCore/Settings.h>
44 #include <WebCore/StringTruncator.h>
45 #include <WebCore/WebCoreTextRenderer.h>
46 #pragma warning(pop)
47
48 namespace WebCore {
49 HBITMAP allocImage(HDC dc, IntSize size, PlatformGraphicsContext** targetRef);
50 void deallocContext(PlatformGraphicsContext* target);
51 }
52
53
54 #define DRAG_LABEL_BORDER_X 4
55 // Keep border_y in synch with DragController::LinkDragBorderInset
56 #define DRAG_LABEL_BORDER_Y 2
57 #define DRAG_LABEL_RADIUS 5
58 #define DRAG_LABEL_BORDER_Y_OFFSET 2
59
60 #define MIN_DRAG_LABEL_WIDTH_BEFORE_CLIP 120
61
62 // Observation says max size is 200x200
63 #define MAX_DRAG_LABEL_WIDTH 200
64 #define MAX_DRAG_LABEL_STRING_WIDTH (MAX_DRAG_LABEL_WIDTH - 2 * DRAG_LABEL_BORDER_X)
65
66 #define DRAG_LINK_LABEL_FONT_SIZE 11
67 #define DRAG_LINK_URL_FONT_SIZE 10
68
69 using namespace WebCore;
70
WebDragClient(WebView * webView)71 WebDragClient::WebDragClient(WebView* webView)
72 : m_webView(webView)
73 {
74 ASSERT(webView);
75 }
76
actionMaskForDrag(DragData * dragData)77 DragDestinationAction WebDragClient::actionMaskForDrag(DragData* dragData)
78 {
79 COMPtr<IWebUIDelegate> delegateRef = 0;
80 //Default behaviour (eg. no delegate, or callback not implemented) is to allow
81 //any action
82 WebDragDestinationAction mask = WebDragDestinationActionAny;
83 if (SUCCEEDED(m_webView->uiDelegate(&delegateRef)))
84 delegateRef->dragDestinationActionMaskForDraggingInfo(m_webView, dragData->platformData(), &mask);
85
86 return (DragDestinationAction)mask;
87 }
88
willPerformDragDestinationAction(DragDestinationAction action,DragData * dragData)89 void WebDragClient::willPerformDragDestinationAction(DragDestinationAction action, DragData* dragData)
90 {
91 //Default delegate for willPerformDragDestinationAction has no side effects
92 //so we just call the delegate, and don't worry about whether it's implemented
93 COMPtr<IWebUIDelegate> delegateRef = 0;
94 if (SUCCEEDED(m_webView->uiDelegate(&delegateRef)))
95 delegateRef->willPerformDragDestinationAction(m_webView, (WebDragDestinationAction)action, dragData->platformData());
96 }
97
dragSourceActionMaskForPoint(const IntPoint & windowPoint)98 DragSourceAction WebDragClient::dragSourceActionMaskForPoint(const IntPoint& windowPoint)
99 {
100 COMPtr<IWebUIDelegate> delegateRef = 0;
101 WebDragSourceAction action = WebDragSourceActionAny;
102 POINT localpt = core(m_webView)->mainFrame()->view()->windowToContents(windowPoint);
103 if (SUCCEEDED(m_webView->uiDelegate(&delegateRef)))
104 delegateRef->dragSourceActionMaskForPoint(m_webView, &localpt, &action);
105 return (DragSourceAction)action;
106 }
107
willPerformDragSourceAction(DragSourceAction action,const IntPoint & intPoint,Clipboard * clipboard)108 void WebDragClient::willPerformDragSourceAction(DragSourceAction action, const IntPoint& intPoint, Clipboard* clipboard)
109 {
110 COMPtr<IWebUIDelegate> uiDelegate;
111 if (!SUCCEEDED(m_webView->uiDelegate(&uiDelegate)))
112 return;
113
114 POINT point = intPoint;
115 COMPtr<IDataObject> dataObject = static_cast<ClipboardWin*>(clipboard)->dataObject();
116
117 COMPtr<IDataObject> newDataObject;
118 HRESULT result = uiDelegate->willPerformDragSourceAction(m_webView, static_cast<WebDragSourceAction>(action), &point, dataObject.get(), &newDataObject);
119 if (result == S_OK && newDataObject != dataObject)
120 static_cast<ClipboardWin*>(clipboard)->setExternalDataObject(newDataObject.get());
121 }
122
startDrag(DragImageRef image,const IntPoint & imageOrigin,const IntPoint & dragPoint,Clipboard * clipboard,Frame * frame,bool isLink)123 void WebDragClient::startDrag(DragImageRef image, const IntPoint& imageOrigin, const IntPoint& dragPoint, Clipboard* clipboard, Frame* frame, bool isLink)
124 {
125 //FIXME: Allow UIDelegate to override behaviour <rdar://problem/5015953>
126
127 //We liberally protect everything, to protect against a load occurring mid-drag
128 RefPtr<Frame> frameProtector = frame;
129 COMPtr<IDragSourceHelper> helper;
130 COMPtr<IDataObject> dataObject;
131 COMPtr<WebView> viewProtector = m_webView;
132 COMPtr<IDropSource> source;
133 if (FAILED(WebDropSource::createInstance(m_webView, &source)))
134 return;
135
136 dataObject = static_cast<ClipboardWin*>(clipboard)->dataObject();
137 if (source && (image || dataObject)) {
138 if (image) {
139 if(SUCCEEDED(CoCreateInstance(CLSID_DragDropHelper, 0, CLSCTX_INPROC_SERVER,
140 IID_IDragSourceHelper,(LPVOID*)&helper))) {
141 BITMAP b;
142 GetObject(image, sizeof(BITMAP), &b);
143 SHDRAGIMAGE sdi;
144 sdi.sizeDragImage.cx = b.bmWidth;
145 sdi.sizeDragImage.cy = b.bmHeight;
146 sdi.crColorKey = 0xffffffff;
147 sdi.hbmpDragImage = image;
148 sdi.ptOffset.x = dragPoint.x() - imageOrigin.x();
149 sdi.ptOffset.y = dragPoint.y() - imageOrigin.y();
150 if (isLink)
151 sdi.ptOffset.y = b.bmHeight - sdi.ptOffset.y;
152
153 helper->InitializeFromBitmap(&sdi, dataObject.get());
154 }
155 }
156
157 //FIXME: Ensure correct drag ops are available <rdar://problem/5015957>
158 DWORD okEffect = DROPEFFECT_COPY | DROPEFFECT_LINK | DROPEFFECT_MOVE;
159 DWORD effect;
160 COMPtr<IWebUIDelegate> ui;
161 if (SUCCEEDED(m_webView->uiDelegate(&ui))) {
162 COMPtr<IWebUIDelegatePrivate> uiPrivate;
163 if (SUCCEEDED(ui->QueryInterface(IID_IWebUIDelegatePrivate, (void**)&uiPrivate)))
164 if (SUCCEEDED(uiPrivate->doDragDrop(m_webView, dataObject.get(), source.get(), okEffect, &effect)))
165 return;
166 }
167
168 DoDragDrop(dataObject.get(), source.get(), okEffect, &effect);
169 }
170 }
171
dragLabelFont(int size,bool bold,FontRenderingMode renderingMode)172 static Font dragLabelFont(int size, bool bold, FontRenderingMode renderingMode)
173 {
174 NONCLIENTMETRICS metrics;
175 metrics.cbSize = sizeof(metrics);
176 SystemParametersInfo(SPI_GETNONCLIENTMETRICS, metrics.cbSize, &metrics, 0);
177 FontDescription desc;
178 desc.setWeight(bold ? FontWeightBold : FontWeightNormal);
179 FontFamily family;
180 family.setFamily(metrics.lfSmCaptionFont.lfFaceName);
181 desc.setFamily(family);
182 desc.setSpecifiedSize((float)size);
183 desc.setComputedSize((float)size);
184 desc.setRenderingMode(renderingMode);
185 Font result = Font(desc, 0, 0);
186 result.update(0);
187 return result;
188 }
189
createDragImageForLink(KURL & url,const String & inLabel,Frame * frame)190 DragImageRef WebDragClient::createDragImageForLink(KURL& url, const String& inLabel, Frame* frame)
191 {
192 // This is more or less an exact match for the Mac OS X code.
193
194 const Font* labelFont;
195 const Font* urlFont;
196
197 if (frame->settings() && frame->settings()->fontRenderingMode() == AlternateRenderingMode) {
198 static const Font alternateRenderingModeLabelFont = dragLabelFont(DRAG_LINK_LABEL_FONT_SIZE, true, AlternateRenderingMode);
199 static const Font alternateRenderingModeURLFont = dragLabelFont(DRAG_LINK_URL_FONT_SIZE, false, AlternateRenderingMode);
200 labelFont = &alternateRenderingModeLabelFont;
201 urlFont = &alternateRenderingModeURLFont;
202 } else {
203 static const Font normalRenderingModeLabelFont = dragLabelFont(DRAG_LINK_LABEL_FONT_SIZE, true, NormalRenderingMode);
204 static const Font normalRenderingModeURLFont = dragLabelFont(DRAG_LINK_URL_FONT_SIZE, false, NormalRenderingMode);
205 labelFont = &normalRenderingModeLabelFont;
206 urlFont = &normalRenderingModeURLFont;
207 }
208
209 bool drawURLString = true;
210 bool clipURLString = false;
211 bool clipLabelString = false;
212
213 String urlString = url.string();
214 String label = inLabel;
215 if (label.isEmpty()) {
216 drawURLString = false;
217 label = urlString;
218 }
219
220 //First step in drawing the link drag image width
221 TextRun labelRun(label.impl());
222 TextRun urlRun(urlString.impl());
223 IntSize labelSize(labelFont->width(labelRun), labelFont->ascent() + labelFont->descent());
224
225 if (labelSize.width() > MAX_DRAG_LABEL_STRING_WIDTH){
226 labelSize.setWidth(MAX_DRAG_LABEL_STRING_WIDTH);
227 clipLabelString = true;
228 }
229
230 IntSize urlStringSize;
231 IntSize imageSize(labelSize.width() + DRAG_LABEL_BORDER_X * 2,
232 labelSize.height() + DRAG_LABEL_BORDER_Y * 2);
233
234 if (drawURLString) {
235 urlStringSize.setWidth(urlFont->width(urlRun));
236 urlStringSize.setHeight(urlFont->ascent() + urlFont->descent());
237 imageSize.setHeight(imageSize.height() + urlStringSize.height());
238 if (urlStringSize.width() > MAX_DRAG_LABEL_STRING_WIDTH) {
239 imageSize.setWidth(MAX_DRAG_LABEL_WIDTH);
240 clipURLString = true;
241 } else {
242 imageSize.setWidth(std::max(labelSize.width(), urlStringSize.width()) + DRAG_LABEL_BORDER_X * 2);
243 }
244 }
245
246 // We now know how big the image needs to be, so we create and
247 // fill the background
248 HBITMAP image = 0;
249 HDC dc = GetDC(0);
250 HDC workingDC = CreateCompatibleDC(dc);
251 if (!workingDC) {
252 ReleaseDC(0, dc);
253 return 0;
254 }
255
256 PlatformGraphicsContext* contextRef;
257 image = allocImage(workingDC, imageSize, &contextRef);
258 if (!image) {
259 DeleteDC(workingDC);
260 ReleaseDC(0, dc);
261 return 0;
262 }
263
264 ::SelectObject(workingDC, image);
265 GraphicsContext context(contextRef);
266 // On Mac alpha is {0.7, 0.7, 0.7, 0.8}, however we can't control alpha
267 // for drag images on win, so we use 1
268 static const Color backgroundColor(140, 140, 140);
269 static const IntSize radii(DRAG_LABEL_RADIUS, DRAG_LABEL_RADIUS);
270 IntRect rect(0, 0, imageSize.width(), imageSize.height());
271 context.fillRoundedRect(rect, radii, radii, radii, radii, backgroundColor);
272
273 // Draw the text
274 static const Color topColor(0, 0, 0, 255); //original alpha = 0.75
275 static const Color bottomColor(255, 255, 255, 127); //original alpha = 0.5
276 if (drawURLString) {
277 if (clipURLString)
278 urlString = StringTruncator::rightTruncate(urlString, imageSize.width() - (DRAG_LABEL_BORDER_X * 2.0f), *urlFont, false);
279 IntPoint textPos(DRAG_LABEL_BORDER_X, imageSize.height() - (DRAG_LABEL_BORDER_Y_OFFSET + urlFont->descent()));
280 WebCoreDrawDoubledTextAtPoint(context, urlString, textPos, *urlFont, topColor, bottomColor);
281 }
282
283 if (clipLabelString)
284 label = StringTruncator::rightTruncate(label, imageSize.width() - (DRAG_LABEL_BORDER_X * 2.0f), *labelFont, false);
285
286 IntPoint textPos(DRAG_LABEL_BORDER_X, DRAG_LABEL_BORDER_Y + labelFont->pixelSize());
287 WebCoreDrawDoubledTextAtPoint(context, label, textPos, *labelFont, topColor, bottomColor);
288
289 deallocContext(contextRef);
290 DeleteDC(workingDC);
291 ReleaseDC(0, dc);
292 return image;
293 }
294
dragControllerDestroyed()295 void WebDragClient::dragControllerDestroyed()
296 {
297 delete this;
298 }
299