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/DragController.h> 37 #include <WebCore/DragData.h> 38 #include <WebCore/Font.h> 39 #include <WebCore/FontDescription.h> 40 #include <WebCore/FontSelector.h> 41 #include <WebCore/FrameView.h> 42 #include <WebCore/GraphicsContext.h> 43 #include <WebCore/Page.h> 44 #include <WebCore/Settings.h> 45 #include <WebCore/StringTruncator.h> 46 #include <WebCore/WebCoreTextRenderer.h> 47 #pragma warning(pop) 48 49 namespace WebCore { 50 HBITMAP allocImage(HDC dc, IntSize size, PlatformGraphicsContext** targetRef); 51 void deallocContext(PlatformGraphicsContext* target); 52 } 53 54 55 #define DRAG_LABEL_BORDER_X 4 56 // Keep border_y in synch with DragController::LinkDragBorderInset 57 #define DRAG_LABEL_BORDER_Y 2 58 #define DRAG_LABEL_RADIUS 5 59 #define DRAG_LABEL_BORDER_Y_OFFSET 2 60 61 #define MIN_DRAG_LABEL_WIDTH_BEFORE_CLIP 120 62 63 // Observation says max size is 200x200 64 #define MAX_DRAG_LABEL_WIDTH 200 65 #define MAX_DRAG_LABEL_STRING_WIDTH (MAX_DRAG_LABEL_WIDTH - 2 * DRAG_LABEL_BORDER_X) 66 67 #define DRAG_LINK_LABEL_FONT_SIZE 11 68 #define DRAG_LINK_URL_FONT_SIZE 10 69 70 using namespace WebCore; 71 72 static DWORD draggingSourceOperationMaskToDragCursors(DragOperation op) 73 { 74 DWORD result = DROPEFFECT_NONE; 75 if (op == DragOperationEvery) 76 return DROPEFFECT_COPY | DROPEFFECT_LINK | DROPEFFECT_MOVE; 77 if (op & DragOperationCopy) 78 result |= DROPEFFECT_COPY; 79 if (op & DragOperationLink) 80 result |= DROPEFFECT_LINK; 81 if (op & DragOperationMove) 82 result |= DROPEFFECT_MOVE; 83 if (op & DragOperationGeneric) 84 result |= DROPEFFECT_MOVE; 85 return result; 86 } 87 88 WebDragClient::WebDragClient(WebView* webView) 89 : m_webView(webView) 90 { 91 ASSERT(webView); 92 } 93 94 DragDestinationAction WebDragClient::actionMaskForDrag(DragData* dragData) 95 { 96 COMPtr<IWebUIDelegate> delegateRef = 0; 97 //Default behaviour (eg. no delegate, or callback not implemented) is to allow 98 //any action 99 WebDragDestinationAction mask = WebDragDestinationActionAny; 100 if (SUCCEEDED(m_webView->uiDelegate(&delegateRef))) 101 delegateRef->dragDestinationActionMaskForDraggingInfo(m_webView, dragData->platformData(), &mask); 102 103 return (DragDestinationAction)mask; 104 } 105 106 void WebDragClient::willPerformDragDestinationAction(DragDestinationAction action, DragData* dragData) 107 { 108 //Default delegate for willPerformDragDestinationAction has no side effects 109 //so we just call the delegate, and don't worry about whether it's implemented 110 COMPtr<IWebUIDelegate> delegateRef = 0; 111 if (SUCCEEDED(m_webView->uiDelegate(&delegateRef))) 112 delegateRef->willPerformDragDestinationAction(m_webView, (WebDragDestinationAction)action, dragData->platformData()); 113 } 114 115 DragSourceAction WebDragClient::dragSourceActionMaskForPoint(const IntPoint& windowPoint) 116 { 117 COMPtr<IWebUIDelegate> delegateRef = 0; 118 WebDragSourceAction action = WebDragSourceActionAny; 119 POINT localpt = core(m_webView)->mainFrame()->view()->windowToContents(windowPoint); 120 if (SUCCEEDED(m_webView->uiDelegate(&delegateRef))) 121 delegateRef->dragSourceActionMaskForPoint(m_webView, &localpt, &action); 122 return (DragSourceAction)action; 123 } 124 125 void WebDragClient::willPerformDragSourceAction(DragSourceAction action, const IntPoint& intPoint, Clipboard* clipboard) 126 { 127 COMPtr<IWebUIDelegate> uiDelegate; 128 if (!SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) 129 return; 130 131 POINT point = intPoint; 132 COMPtr<IDataObject> dataObject = static_cast<ClipboardWin*>(clipboard)->dataObject(); 133 134 COMPtr<IDataObject> newDataObject; 135 HRESULT result = uiDelegate->willPerformDragSourceAction(m_webView, static_cast<WebDragSourceAction>(action), &point, dataObject.get(), &newDataObject); 136 if (result == S_OK && newDataObject != dataObject) 137 static_cast<ClipboardWin*>(clipboard)->setExternalDataObject(newDataObject.get()); 138 } 139 140 void WebDragClient::startDrag(DragImageRef image, const IntPoint& imageOrigin, const IntPoint& dragPoint, Clipboard* clipboard, Frame* frame, bool isLink) 141 { 142 //FIXME: Allow UIDelegate to override behaviour <rdar://problem/5015953> 143 144 //We liberally protect everything, to protect against a load occurring mid-drag 145 RefPtr<Frame> frameProtector = frame; 146 COMPtr<IDragSourceHelper> helper; 147 COMPtr<IDataObject> dataObject; 148 COMPtr<WebView> viewProtector = m_webView; 149 COMPtr<IDropSource> source; 150 if (FAILED(WebDropSource::createInstance(m_webView, &source))) 151 return; 152 153 dataObject = static_cast<ClipboardWin*>(clipboard)->dataObject(); 154 if (source && (image || dataObject)) { 155 if (image) { 156 if(SUCCEEDED(CoCreateInstance(CLSID_DragDropHelper, 0, CLSCTX_INPROC_SERVER, 157 IID_IDragSourceHelper,(LPVOID*)&helper))) { 158 BITMAP b; 159 GetObject(image, sizeof(BITMAP), &b); 160 SHDRAGIMAGE sdi; 161 sdi.sizeDragImage.cx = b.bmWidth; 162 sdi.sizeDragImage.cy = b.bmHeight; 163 sdi.crColorKey = 0xffffffff; 164 sdi.hbmpDragImage = image; 165 sdi.ptOffset.x = dragPoint.x() - imageOrigin.x(); 166 sdi.ptOffset.y = dragPoint.y() - imageOrigin.y(); 167 if (isLink) 168 sdi.ptOffset.y = b.bmHeight - sdi.ptOffset.y; 169 170 helper->InitializeFromBitmap(&sdi, dataObject.get()); 171 } 172 } 173 174 DWORD okEffect = draggingSourceOperationMaskToDragCursors(m_webView->page()->dragController()->sourceDragOperation()); 175 DWORD effect = DROPEFFECT_NONE; 176 COMPtr<IWebUIDelegate> ui; 177 HRESULT hr = E_NOTIMPL; 178 if (SUCCEEDED(m_webView->uiDelegate(&ui))) { 179 COMPtr<IWebUIDelegatePrivate> uiPrivate; 180 if (SUCCEEDED(ui->QueryInterface(IID_IWebUIDelegatePrivate, (void**)&uiPrivate))) 181 hr = uiPrivate->doDragDrop(m_webView, dataObject.get(), source.get(), okEffect, &effect); 182 } 183 if (hr == E_NOTIMPL) 184 hr = DoDragDrop(dataObject.get(), source.get(), okEffect, &effect); 185 186 DragOperation operation = DragOperationNone; 187 if (hr == DRAGDROP_S_DROP) { 188 if (effect & DROPEFFECT_COPY) 189 operation = DragOperationCopy; 190 else if (effect & DROPEFFECT_LINK) 191 operation = DragOperationLink; 192 else if (effect & DROPEFFECT_MOVE) 193 operation = DragOperationMove; 194 } 195 frame->eventHandler()->dragSourceEndedAt(generateMouseEvent(m_webView, false), operation); 196 } 197 } 198 199 static Font dragLabelFont(int size, bool bold, FontRenderingMode renderingMode) 200 { 201 NONCLIENTMETRICS metrics; 202 metrics.cbSize = sizeof(metrics); 203 SystemParametersInfo(SPI_GETNONCLIENTMETRICS, metrics.cbSize, &metrics, 0); 204 FontDescription desc; 205 desc.setWeight(bold ? FontWeightBold : FontWeightNormal); 206 FontFamily family; 207 family.setFamily(metrics.lfSmCaptionFont.lfFaceName); 208 desc.setFamily(family); 209 desc.setSpecifiedSize((float)size); 210 desc.setComputedSize((float)size); 211 desc.setRenderingMode(renderingMode); 212 Font result = Font(desc, 0, 0); 213 result.update(0); 214 return result; 215 } 216 217 DragImageRef WebDragClient::createDragImageForLink(KURL& url, const String& inLabel, Frame* frame) 218 { 219 // This is more or less an exact match for the Mac OS X code. 220 221 const Font* labelFont; 222 const Font* urlFont; 223 224 if (frame->settings() && frame->settings()->fontRenderingMode() == AlternateRenderingMode) { 225 static const Font alternateRenderingModeLabelFont = dragLabelFont(DRAG_LINK_LABEL_FONT_SIZE, true, AlternateRenderingMode); 226 static const Font alternateRenderingModeURLFont = dragLabelFont(DRAG_LINK_URL_FONT_SIZE, false, AlternateRenderingMode); 227 labelFont = &alternateRenderingModeLabelFont; 228 urlFont = &alternateRenderingModeURLFont; 229 } else { 230 static const Font normalRenderingModeLabelFont = dragLabelFont(DRAG_LINK_LABEL_FONT_SIZE, true, NormalRenderingMode); 231 static const Font normalRenderingModeURLFont = dragLabelFont(DRAG_LINK_URL_FONT_SIZE, false, NormalRenderingMode); 232 labelFont = &normalRenderingModeLabelFont; 233 urlFont = &normalRenderingModeURLFont; 234 } 235 236 bool drawURLString = true; 237 bool clipURLString = false; 238 bool clipLabelString = false; 239 240 String urlString = url.string(); 241 String label = inLabel; 242 if (label.isEmpty()) { 243 drawURLString = false; 244 label = urlString; 245 } 246 247 //First step in drawing the link drag image width 248 TextRun labelRun(label.impl()); 249 TextRun urlRun(urlString.impl()); 250 IntSize labelSize(labelFont->width(labelRun), labelFont->ascent() + labelFont->descent()); 251 252 if (labelSize.width() > MAX_DRAG_LABEL_STRING_WIDTH){ 253 labelSize.setWidth(MAX_DRAG_LABEL_STRING_WIDTH); 254 clipLabelString = true; 255 } 256 257 IntSize urlStringSize; 258 IntSize imageSize(labelSize.width() + DRAG_LABEL_BORDER_X * 2, 259 labelSize.height() + DRAG_LABEL_BORDER_Y * 2); 260 261 if (drawURLString) { 262 urlStringSize.setWidth(urlFont->width(urlRun)); 263 urlStringSize.setHeight(urlFont->ascent() + urlFont->descent()); 264 imageSize.setHeight(imageSize.height() + urlStringSize.height()); 265 if (urlStringSize.width() > MAX_DRAG_LABEL_STRING_WIDTH) { 266 imageSize.setWidth(MAX_DRAG_LABEL_WIDTH); 267 clipURLString = true; 268 } else { 269 imageSize.setWidth(std::max(labelSize.width(), urlStringSize.width()) + DRAG_LABEL_BORDER_X * 2); 270 } 271 } 272 273 // We now know how big the image needs to be, so we create and 274 // fill the background 275 HBITMAP image = 0; 276 HDC dc = GetDC(0); 277 HDC workingDC = CreateCompatibleDC(dc); 278 if (!workingDC) { 279 ReleaseDC(0, dc); 280 return 0; 281 } 282 283 PlatformGraphicsContext* contextRef; 284 image = allocImage(workingDC, imageSize, &contextRef); 285 if (!image) { 286 DeleteDC(workingDC); 287 ReleaseDC(0, dc); 288 return 0; 289 } 290 291 ::SelectObject(workingDC, image); 292 GraphicsContext context(contextRef); 293 // On Mac alpha is {0.7, 0.7, 0.7, 0.8}, however we can't control alpha 294 // for drag images on win, so we use 1 295 static const Color backgroundColor(140, 140, 140); 296 static const IntSize radii(DRAG_LABEL_RADIUS, DRAG_LABEL_RADIUS); 297 IntRect rect(0, 0, imageSize.width(), imageSize.height()); 298 context.fillRoundedRect(rect, radii, radii, radii, radii, backgroundColor, DeviceColorSpace); 299 300 // Draw the text 301 static const Color topColor(0, 0, 0, 255); //original alpha = 0.75 302 static const Color bottomColor(255, 255, 255, 127); //original alpha = 0.5 303 if (drawURLString) { 304 if (clipURLString) 305 urlString = StringTruncator::rightTruncate(urlString, imageSize.width() - (DRAG_LABEL_BORDER_X * 2.0f), *urlFont, false); 306 IntPoint textPos(DRAG_LABEL_BORDER_X, imageSize.height() - (DRAG_LABEL_BORDER_Y_OFFSET + urlFont->descent())); 307 WebCoreDrawDoubledTextAtPoint(context, urlString, textPos, *urlFont, topColor, bottomColor); 308 } 309 310 if (clipLabelString) 311 label = StringTruncator::rightTruncate(label, imageSize.width() - (DRAG_LABEL_BORDER_X * 2.0f), *labelFont, false); 312 313 IntPoint textPos(DRAG_LABEL_BORDER_X, DRAG_LABEL_BORDER_Y + labelFont->pixelSize()); 314 WebCoreDrawDoubledTextAtPoint(context, label, textPos, *labelFont, topColor, bottomColor); 315 316 deallocContext(contextRef); 317 DeleteDC(workingDC); 318 ReleaseDC(0, dc); 319 return image; 320 } 321 322 void WebDragClient::dragControllerDestroyed() 323 { 324 delete this; 325 } 326