mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-10-14 20:44:29 +00:00
Refactor, restructure and simplify copy and paste handling.
This change brings nearly all copy and paste related into a single, self-contained class. This new class is still somewhat coupled to CPPlatformWindow+DOM.j in the sense that it's highly specific and meant to be called in a very specialised way. Despite this, it's still a significant decoupling and the new code is more readable and easier to follow.
This commit is contained in:
1 parent
8dd252eee1
commit
757c79d15c
4 files changed
+575
-417
No files matched your search
@@ -26,6 +26,7 @@
|
||||
@import "CPPlatform.j"
|
||||
|
||||
@class CPMenu
|
||||
@class CPPlatformPasteboard
|
||||
|
||||
@global CPApp
|
||||
|
||||
@@ -33,46 +34,42 @@ var PrimaryPlatformWindow = NULL;
|
||||
|
||||
@implementation CPPlatformWindow : CPObject
|
||||
{
|
||||
CGRect _contentRect;
|
||||
CGRect _contentRect;
|
||||
|
||||
CPInteger _level;
|
||||
BOOL _hasShadow;
|
||||
unsigned _shadowStyle;
|
||||
CPString _title;
|
||||
CPInteger _level;
|
||||
BOOL _hasShadow;
|
||||
unsigned _shadowStyle;
|
||||
CPString _title;
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
DOMWindow _DOMWindow;
|
||||
DOMWindow _DOMWindow;
|
||||
|
||||
DOMElement _DOMBodyElement;
|
||||
DOMElement _DOMFocusElement;
|
||||
DOMElement _DOMEventGuard;
|
||||
DOMElement _DOMScrollingElement;
|
||||
id _hideDOMScrollingElementTimeout;
|
||||
DOMElement _DOMBodyElement;
|
||||
DOMElement _DOMFocusElement;
|
||||
DOMElement _DOMEventGuard;
|
||||
DOMElement _DOMScrollingElement;
|
||||
id _hideDOMScrollingElementTimeout;
|
||||
|
||||
CPArray _windowLevels;
|
||||
CPDictionary _windowLayers;
|
||||
CPArray _windowLevels;
|
||||
CPDictionary _windowLayers;
|
||||
|
||||
BOOL _mouseIsDown;
|
||||
BOOL _mouseDownIsRightClick;
|
||||
CGPoint _lastMouseEventLocation;
|
||||
CPWindow _mouseDownWindow;
|
||||
CPTimeInterval _lastMouseUp;
|
||||
CPTimeInterval _lastMouseDown;
|
||||
BOOL _mouseIsDown;
|
||||
BOOL _mouseDownIsRightClick;
|
||||
CGPoint _lastMouseEventLocation;
|
||||
CPWindow _mouseDownWindow;
|
||||
CPTimeInterval _lastMouseUp;
|
||||
CPTimeInterval _lastMouseDown;
|
||||
|
||||
Object _charCodes;
|
||||
unsigned _keyCode;
|
||||
unsigned _lastKey;
|
||||
BOOL _capsLockActive;
|
||||
BOOL _ignoreNativeCopyOrCutEvent;
|
||||
BOOL _ignoreNativePastePreparation;
|
||||
Object _charCodes;
|
||||
unsigned _keyCode;
|
||||
unsigned _lastKey;
|
||||
BOOL _capsLockActive;
|
||||
|
||||
BOOL _DOMEventMode;
|
||||
BOOL _DOMEventMode;
|
||||
|
||||
// Native Pasteboard Support
|
||||
DOMElement _DOMPasteboardElement;
|
||||
CPEvent _pasteboardKeyDownEvent;
|
||||
CPPlatformPasteboard _platformPasteboard;
|
||||
|
||||
CPString _overriddenEventType;
|
||||
CPString _overriddenEventType;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,515 @@
|
||||
/*
|
||||
* CPPlatformPasteboard.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Alexander Ljungberg.
|
||||
* Copyright 2013, SlevenBits Ltd.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPRunLoop.j>
|
||||
|
||||
@import "CPCompatibility.j"
|
||||
@import "CPEvent.j"
|
||||
@import "CPPasteboard.j"
|
||||
@import "CPPlatform.j"
|
||||
@import "CPPlatformWindow+DOMKeys.j"
|
||||
|
||||
@global CPApp
|
||||
@global CPPlatformWindow
|
||||
|
||||
// From CPPlatformWindow+DOM.j
|
||||
@global _CPDOMEventStop
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
|
||||
var hasEditableTarget = function(aDOMEvent)
|
||||
{
|
||||
var target = aDOMEvent.target || aDOMEvent.srcElement;
|
||||
|
||||
if (!target)
|
||||
return NO;
|
||||
|
||||
if (target.contentEditable == "true")
|
||||
return YES;
|
||||
|
||||
var nodeName = target.nodeName.toUpperCase();
|
||||
|
||||
return nodeName === "INPUT" || nodeName == "TEXTAREA";
|
||||
}
|
||||
|
||||
/*
|
||||
* This class encapsulates copy and paste related functionality for the
|
||||
* DOM environment. While originally all of this code was apread out in a
|
||||
* dozen places in CPPlatformWindow+DOM.j, this class serves to collect
|
||||
* and isolate that code for easier testing and maintenance.
|
||||
*/
|
||||
@implementation CPPlatformPasteboard : CPObject
|
||||
{
|
||||
DOMWindow _DOMWindow;
|
||||
|
||||
DOMElement _DOMPasteboardElement;
|
||||
|
||||
BOOL supportsNativeCopyAndPaste;
|
||||
BOOL hasBugWhichPreventsNonEditablePaste;
|
||||
BOOL hasBugWhichPreventsNonEditablePasteRedirect;
|
||||
|
||||
CPEvent _lastKeyDownEvent;
|
||||
BOOL currentEventIsNativePasteEvent;
|
||||
BOOL currentEventIsNativeCopyOrCutEvent;
|
||||
BOOL currentEventShouldBeSuppressed;
|
||||
BOOL currentEventShouldDefinitelyBubble;
|
||||
BOOL currentEventShouldDefinitelyNotBubble;
|
||||
|
||||
BOOL _ignoreNativeCopyOrCutEvent;
|
||||
BOOL _ignoreNativePastePreparation;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
supportsNativeCopyAndPaste = CPFeatureIsCompatible(CPJavaScriptClipboardEventsFeature);
|
||||
hasBugWhichPreventsNonEditablePaste = CPPlatformHasBug(CPJavaScriptPasteRequiresEditableTarget);
|
||||
hasBugWhichPreventsNonEditablePasteRedirect = CPPlatformHasBug(CPJavaScriptPasteCantRefocus);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setDOMWindow:(DOMWindow)aDOMWindow
|
||||
{
|
||||
if (_DOMWindow === aDOMWindow)
|
||||
return;
|
||||
|
||||
if (_DOMWindow)
|
||||
[self destroyDOMElements];
|
||||
|
||||
_DOMWindow = aDOMWindow;
|
||||
|
||||
if (_DOMWindow)
|
||||
[self createDOMElements];
|
||||
}
|
||||
|
||||
- (void)createDOMElements
|
||||
{
|
||||
var theDocument = _DOMWindow.document,
|
||||
_DOMBodyElement = theDocument.getElementById("cappuccino-body") || theDocument.body;
|
||||
|
||||
// Create Native Pasteboard handler.
|
||||
_DOMPasteboardElement = theDocument.createElement("textarea");
|
||||
|
||||
_DOMPasteboardElement.style.position = "absolute";
|
||||
_DOMPasteboardElement.style.top = "-10000px";
|
||||
_DOMPasteboardElement.style.zIndex = "999";
|
||||
_DOMPasteboardElement.className = "cpdontremove";
|
||||
|
||||
_DOMBodyElement.appendChild(_DOMPasteboardElement);
|
||||
|
||||
_DOMPasteboardElement.blur();
|
||||
|
||||
var copyEventCallback = function (anEvent) { return [self beforeCopyEvent:anEvent]; },
|
||||
nativeBeforeClipboardEventCallback = function (anEvent) { return [self nativeBeforeClipboardEvent:anEvent]; },
|
||||
nativeCopyOrCutEventCallback = function (anEvent) { return [self nativeCopyOrCutEvent:anEvent]; },
|
||||
pasteEventCallback = function (anEvent) { return [self beforePasteEvent:anEvent]; },
|
||||
nativePasteEventCallback = function (anEvent) { return [self nativePasteEvent:anEvent]; };
|
||||
|
||||
if (theDocument.addEventListener)
|
||||
{
|
||||
if (supportsNativeCopyAndPaste)
|
||||
{
|
||||
_DOMWindow.addEventListener("beforecopy", nativeBeforeClipboardEventCallback, NO);
|
||||
_DOMWindow.addEventListener("beforecut", nativeBeforeClipboardEventCallback, NO);
|
||||
_DOMWindow.addEventListener("beforepaste", nativeBeforeClipboardEventCallback, NO);
|
||||
_DOMWindow.addEventListener("copy", nativeCopyOrCutEventCallback, NO);
|
||||
_DOMWindow.addEventListener("cut", nativeCopyOrCutEventCallback, NO);
|
||||
_DOMWindow.addEventListener("paste", nativePasteEventCallback, NO);
|
||||
}
|
||||
else
|
||||
{
|
||||
theDocument.addEventListener("beforepaste", pasteEventCallback, NO);
|
||||
theDocument.addEventListener("beforecopy", copyEventCallback, NO);
|
||||
theDocument.addEventListener("beforecut", copyEventCallback, NO);
|
||||
}
|
||||
|
||||
_DOMWindow.addEventListener("unload", function()
|
||||
{
|
||||
if (supportsNativeCopyAndPaste)
|
||||
{
|
||||
_DOMWindow.removeEventListener("beforecopy", nativeBeforeClipboardEventCallback, NO);
|
||||
_DOMWindow.removeEventListener("beforecut", nativeBeforeClipboardEventCallback, NO);
|
||||
_DOMWindow.removeEventListener("beforepaste", nativeBeforeClipboardEventCallback, NO);
|
||||
_DOMWindow.removeEventListener("copy", nativeCopyOrCutEventCallback, NO);
|
||||
_DOMWindow.removeEventListener("cut", nativeCopyOrCutEventCallback, NO);
|
||||
_DOMWindow.removeEventListener("paste", nativePasteEventCallback, NO);
|
||||
}
|
||||
else
|
||||
{
|
||||
theDocument.removeEventListener("beforepaste", pasteEventCallback, NO);
|
||||
theDocument.removeEventListener("beforecopy", copyEventCallback, NO);
|
||||
theDocument.removeEventListener("beforecut", copyEventCallback, NO);
|
||||
}
|
||||
}, NO);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO If we wanted IE 8 and lower copy and paste it'd go here.
|
||||
}
|
||||
}
|
||||
|
||||
- (void)destroyDOMElements
|
||||
{
|
||||
var theDocument = _DOMWindow.document,
|
||||
_DOMBodyElement = theDocument.getElementById("cappuccino-body") || theDocument.body;
|
||||
|
||||
_DOMBodyElement.removeChild(_DOMPasteboardElement);
|
||||
_DOMPasteboardElement = nil;
|
||||
}
|
||||
|
||||
- (void)windowMaySendKeyEvent:(CPEvent)anEvent
|
||||
{
|
||||
// Reset our opinions.
|
||||
currentEventIsNativePasteEvent = NO;
|
||||
currentEventIsNativeCopyOrCutEvent = NO;
|
||||
currentEventShouldBeSuppressed = NO;
|
||||
currentEventShouldDefinitelyNotBubble = NO;
|
||||
currentEventShouldDefinitelyBubble = NO;
|
||||
|
||||
if (!anEvent)
|
||||
return;
|
||||
|
||||
if ([anEvent type] !== CPKeyDown)
|
||||
{
|
||||
// Reset these flags on key up.
|
||||
_ignoreNativePastePreparation = NO;
|
||||
_ignoreNativeCopyOrCutEvent = NO;
|
||||
return;
|
||||
}
|
||||
|
||||
var modifierFlags = [anEvent modifierFlags];
|
||||
|
||||
if (!(modifierFlags & (CPControlKeyMask | CPCommandKeyMask)))
|
||||
return;
|
||||
|
||||
_lastKeyDownEvent = anEvent;
|
||||
|
||||
var aDOMEvent = anEvent._DOMEvent,
|
||||
characters = [anEvent characters],
|
||||
mayRequireDOMPasteboardElement = [self _mayRequireDOMPasteboardElementHack:aDOMEvent flags:modifierFlags];
|
||||
|
||||
if (characters === "v" && mayRequireDOMPasteboardElement)
|
||||
{
|
||||
if (supportsNativeCopyAndPaste && hasBugWhichPreventsNonEditablePaste && hasBugWhichPreventsNonEditablePasteRedirect && !hasEditableTarget(aDOMEvent))
|
||||
{
|
||||
// You can't paste from the system clipboard into a non-editable area in Safari, neither using native
|
||||
// copy and paste nor our _DOMPasteboardElement hack. We will paste from the Cappuccino pasteboard only
|
||||
// and allow Safari to "beep" to indicate something went wrong.
|
||||
|
||||
// The key down will not result in a paste event being sent by Safari, so it's not a paste event.
|
||||
currentEventIsNativePasteEvent = NO;
|
||||
// Yes to get the beep.
|
||||
currentEventShouldDefinitelyBubble = YES;
|
||||
}
|
||||
else if (!(supportsNativeCopyAndPaste || hasBugWhichPreventsNonEditablePaste) && !_ignoreNativePastePreparation)
|
||||
{
|
||||
// We don't support native copy and paste so we must focus the _DOMPasteboardElement to receive the
|
||||
// paste content. This needs to be done at keyDown time (or in the case of modern Safari, before the keyDown,
|
||||
// which unfortunately isn't possible. See above.)
|
||||
_DOMPasteboardElement.focus();
|
||||
_DOMPasteboardElement.select();
|
||||
_DOMPasteboardElement.value = "";
|
||||
_DOMWindow.setNativeTimeout(function () { [self _checkDOMPasteboardElement]; }, 0);
|
||||
|
||||
currentEventIsNativePasteEvent = YES;
|
||||
}
|
||||
else if (supportsNativeCopyAndPaste)
|
||||
currentEventIsNativePasteEvent = YES;
|
||||
|
||||
if (currentEventIsNativePasteEvent)
|
||||
{
|
||||
// We need the event to propagate or nothing will be pasted.
|
||||
currentEventShouldDefinitelyBubble = YES;
|
||||
|
||||
// And we don't send the keydown because either our native paste envent handler will send it,
|
||||
// or the _checkDOMPasteboardElement check will.
|
||||
currentEventShouldBeSuppressed = YES;
|
||||
}
|
||||
}
|
||||
else if ((characters == "c" || characters == "x") && mayRequireDOMPasteboardElement)
|
||||
{
|
||||
currentEventIsNativeCopyOrCutEvent = YES;
|
||||
|
||||
// If _ignoreNativeCopyOrCutEvent, we already handled the copy/cut in beforeCopyEvent:.
|
||||
// If supportsNativeCopyAndPaste, we will be handling it in the nativebeforeCopyEvent: handler.
|
||||
// In both of those cases we don't want to send the event on keydown too, or we'll get 2X copy/cut operations.
|
||||
if (supportsNativeCopyAndPaste || _ignoreNativeCopyOrCutEvent)
|
||||
currentEventShouldBeSuppressed = YES;
|
||||
|
||||
currentEventShouldDefinitelyBubble = YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)windowShouldSuppressKeyEvent
|
||||
{
|
||||
return currentEventShouldBeSuppressed;
|
||||
}
|
||||
|
||||
- (void)windowDidSendKeyEvent:(CPEvent)anEvent
|
||||
{
|
||||
// Now that the copy event has been sent through the Cappuccino event stack, we can load any Cappuccino
|
||||
// pasteboard string into our DOMPasteboardElement hack, if necessary.
|
||||
if (!supportsNativeCopyAndPaste && currentEventIsNativeCopyOrCutEvent)
|
||||
[self _primeDOMPasteboardElement];
|
||||
}
|
||||
|
||||
- (BOOL)windowShouldStopPropagation
|
||||
{
|
||||
return currentEventShouldDefinitelyNotBubble;
|
||||
}
|
||||
|
||||
- (BOOL)windowShouldNotStopPropagation
|
||||
{
|
||||
return currentEventShouldDefinitelyBubble;
|
||||
}
|
||||
|
||||
- (CPEvent)_fakeClipboardEvent:(DOMEvent)aDOMEvent type:(CPString)aType
|
||||
{
|
||||
var keyCode = aType === "x" ? CPKeyCodes.X : (aType === "c" ? CPKeyCodes.C : CPKeyCodes.V),
|
||||
characters = aType,
|
||||
timestamp = [CPEvent currentTimestamp], // fake event, might as well use current timestamp
|
||||
windowNumber = [[CPApp keyWindow] windowNumber],
|
||||
modifierFlags = CPPlatformActionKeyMask,
|
||||
location = [_lastKeyDownEvent locationInWindow],
|
||||
anEvent = [CPEvent keyEventWithType:CPKeyDown location:location modifierFlags:modifierFlags
|
||||
timestamp:timestamp windowNumber:windowNumber context:nil
|
||||
characters:characters charactersIgnoringModifiers:characters isARepeat:NO keyCode:keyCode];
|
||||
|
||||
anEvent._data1 = @{ "simulated": YES };
|
||||
anEvent._DOMEvent = aDOMEvent;
|
||||
|
||||
return anEvent;
|
||||
}
|
||||
|
||||
- (void)beforeCopyEvent:(DOMEvent)aDOMEvent
|
||||
{
|
||||
if ([self _mayRequireDOMPasteboardElementHack:aDOMEvent flags:CPPlatformActionKeyMask] && !_ignoreNativeCopyOrCutEvent)
|
||||
{
|
||||
// we have to send out a fake copy or cut event so that we can force the copy/cut mechanisms to take place
|
||||
var anEvent = [self _fakeClipboardEvent:aDOMEvent type:(aDOMEvent.type === "beforecut" ? "x" : "c")];
|
||||
|
||||
[CPApp sendEvent:anEvent];
|
||||
|
||||
// Once we've sent it, we can load the copy information into the pasteboard hack.
|
||||
[self _primeDOMPasteboardElement];
|
||||
|
||||
//then we have to IGNORE the real keyboard event to prevent a double copy
|
||||
//safari also sends the beforecopy event twice, so we additionally check here and prevent two events
|
||||
_ignoreNativeCopyOrCutEvent = YES;
|
||||
}
|
||||
|
||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||
}
|
||||
|
||||
- (boolean)beforePasteEvent:(DOMEvent)aDOMEvent
|
||||
{
|
||||
// Set up to capture the paste in a temporary input field. We'll send the event after capture.
|
||||
if ([self _mayRequireDOMPasteboardElementHack:aDOMEvent flags:CPPlatformActionKeyMask])
|
||||
{
|
||||
_DOMPasteboardElement.focus();
|
||||
_DOMPasteboardElement.select();
|
||||
_DOMPasteboardElement.value = "";
|
||||
|
||||
_ignoreNativePastePreparation = YES;
|
||||
}
|
||||
|
||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||
}
|
||||
|
||||
/*
|
||||
Return true if the event may be a copy and paste event, but the target is not an input or text area.
|
||||
*/
|
||||
- (void)_mayRequireDOMPasteboardElementHack:(DOMEvent)aDOMEvent flags:(unsigned)modifierFlags
|
||||
{
|
||||
return !hasEditableTarget(aDOMEvent) && (modifierFlags & CPPlatformActionKeyMask);
|
||||
}
|
||||
|
||||
- (void)_primeDOMPasteboardElement
|
||||
{
|
||||
var pasteboard = [CPPasteboard generalPasteboard],
|
||||
types = [pasteboard types];
|
||||
|
||||
if (types.length)
|
||||
{
|
||||
if ([types indexOfObjectIdenticalTo:CPStringPboardType] !== CPNotFound)
|
||||
_DOMPasteboardElement.value = [pasteboard stringForType:CPStringPboardType];
|
||||
else
|
||||
_DOMPasteboardElement.value = [pasteboard _generateStateUID];
|
||||
|
||||
_DOMPasteboardElement.focus();
|
||||
_DOMPasteboardElement.select();
|
||||
|
||||
window.setNativeTimeout(function() { [self _clearDOMPasteboardElement]; }, 0);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_checkDOMPasteboardElement
|
||||
{
|
||||
if (supportsNativeCopyAndPaste)
|
||||
{
|
||||
[self _clearDOMPasteboardElement];
|
||||
return;
|
||||
}
|
||||
|
||||
var value = _DOMPasteboardElement.value;
|
||||
|
||||
if ([value length])
|
||||
{
|
||||
var pasteboard = [CPPasteboard generalPasteboard];
|
||||
|
||||
if ([pasteboard _stateUID] != value)
|
||||
{
|
||||
[pasteboard declareTypes:[CPStringPboardType] owner:self];
|
||||
[pasteboard setString:value forType:CPStringPboardType];
|
||||
}
|
||||
}
|
||||
|
||||
[self _clearDOMPasteboardElement];
|
||||
|
||||
[CPApp sendEvent:[self _fakeClipboardEvent:nil type:@"v"]];
|
||||
|
||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||
}
|
||||
|
||||
- (void)_clearDOMPasteboardElement
|
||||
{
|
||||
_DOMPasteboardElement.value = "";
|
||||
_DOMPasteboardElement.blur();
|
||||
}
|
||||
|
||||
- (boolean)nativeBeforeClipboardEvent:(DOMEvent)aDOMEvent
|
||||
{
|
||||
// Our job here is to return "false" if the given clipboard operation should be enabled even in a situation where
|
||||
// the browser might normally grey the option out.
|
||||
|
||||
// Allow these fields to do their own thing.
|
||||
if (hasEditableTarget(aDOMEvent))
|
||||
return true;
|
||||
|
||||
var returnValue = YES;
|
||||
|
||||
switch (aDOMEvent.type)
|
||||
{
|
||||
case "beforecopy":
|
||||
returnValue = !([CPApp targetForAction:@selector(copy:)]);
|
||||
break;
|
||||
case "beforecut":
|
||||
returnValue = !([CPApp targetForAction:@selector(cut:)]);
|
||||
break;
|
||||
case "beforepaste":
|
||||
returnValue = !([CPApp targetForAction:@selector(paste:)]);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!returnValue)
|
||||
_CPDOMEventStop(aDOMEvent, self);
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
- (boolean)nativePasteEvent:(DOMEvent)aDOMEvent
|
||||
{
|
||||
// This shouldn't happen.
|
||||
if (!supportsNativeCopyAndPaste)
|
||||
return;
|
||||
|
||||
var value;
|
||||
if (aDOMEvent.clipboardData && aDOMEvent.clipboardData.setData)
|
||||
value = aDOMEvent.clipboardData.getData('text/plain');
|
||||
else
|
||||
value = _DOMWindow.clipboardData.getData("Text");
|
||||
|
||||
if ([value length])
|
||||
{
|
||||
var pasteboard = [CPPasteboard generalPasteboard];
|
||||
|
||||
if ([pasteboard _stateUID] != value)
|
||||
{
|
||||
[pasteboard declareTypes:[CPStringPboardType] owner:self];
|
||||
[pasteboard setString:value forType:CPStringPboardType];
|
||||
}
|
||||
}
|
||||
|
||||
var anEvent = [self _fakeClipboardEvent:aDOMEvent type:"v"],
|
||||
platformWindow = [[anEvent window] platformWindow];
|
||||
|
||||
// By default we'll stop the native handling of the event since we're handling it ourselves. However, we need to
|
||||
// stop it before we send the event so that the event can overrule our choice. CPTextField for instance wants the
|
||||
// default handling when focused (which is to insert into the field).
|
||||
[platformWindow _propagateCurrentDOMEvent:NO]
|
||||
|
||||
[CPApp sendEvent:anEvent];
|
||||
|
||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||
|
||||
if (![platformWindow _willPropagateCurrentDOMEvent])
|
||||
_CPDOMEventStop(aDOMEvent, self);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
- (boolean)nativeCopyOrCutEvent:(DOMEvent)aDOMEvent
|
||||
{
|
||||
// This shouldn't happen.
|
||||
if (!supportsNativeCopyAndPaste)
|
||||
return;
|
||||
|
||||
var anEvent = [self _fakeClipboardEvent:aDOMEvent type:(aDOMEvent.type.indexOf("cut") != CPNotFound ? "x" : "c")],
|
||||
platformWindow = [[anEvent window] platformWindow];
|
||||
|
||||
[platformWindow _propagateCurrentDOMEvent:NO]
|
||||
|
||||
// Let the app react through copy: and cut: actions.
|
||||
[CPApp sendEvent:anEvent];
|
||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||
|
||||
// If StopDOMEventPropagation was set to NO, we don't try to write to the system clipboard. The control that did this
|
||||
// wants to use the default copy/cut functionality.
|
||||
if (![platformWindow _willPropagateCurrentDOMEvent])
|
||||
{
|
||||
// Now the app should have written whatever it wants to have copied to the Cappuccino clipboard. So now we need
|
||||
// to write it to the system board.
|
||||
_CPDOMEventStop(aDOMEvent, self);
|
||||
|
||||
var pasteboard = [CPPasteboard generalPasteboard];
|
||||
|
||||
if ([[pasteboard types] containsObject:CPStringPboardType])
|
||||
{
|
||||
var stringValue = [pasteboard stringForType:CPStringPboardType];
|
||||
|
||||
if (aDOMEvent.clipboardData && aDOMEvent.clipboardData.setData)
|
||||
aDOMEvent.clipboardData.setData('text/plain', stringValue);
|
||||
else
|
||||
_DOMWindow.clipboardData.setData('Text', stringValue);
|
||||
}
|
||||
}
|
||||
|
||||
return ![platformWindow _willPropagateCurrentDOMEvent];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
@@ -112,15 +112,16 @@
|
||||
@import <Foundation/CPSet.j>
|
||||
@import <Foundation/CPTimer.j>
|
||||
|
||||
@import "CPCursor.j"
|
||||
@import "CPCompatibility.j"
|
||||
@import "CPCursor.j"
|
||||
@import "CPDOMWindowLayer.j"
|
||||
@import "CPDragServer_Constants.j"
|
||||
@import "CPEvent.j"
|
||||
@import "CPPasteboard.j"
|
||||
@import "CPPlatform.j"
|
||||
@import "CPPlatformWindow.j"
|
||||
@import "CPPlatformPasteboard.j"
|
||||
@import "CPPlatformWindow+DOMKeys.j"
|
||||
@import "CPPlatformWindow.j"
|
||||
@import "CPText.j"
|
||||
@import "CPWindow_Constants.j"
|
||||
|
||||
@@ -135,7 +136,6 @@ var PlatformWindows = [CPSet set];
|
||||
|
||||
// Define up here so compressor knows about them.
|
||||
var CPDOMEventGetClickCount,
|
||||
CPDOMEventStop,
|
||||
StopDOMEventPropagation,
|
||||
StopContextMenuDOMEventPropagation;
|
||||
|
||||
@@ -197,31 +197,12 @@ var ModifierKeyCodes = [
|
||||
CPKeyCodes.SHIFT
|
||||
],
|
||||
|
||||
supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop],
|
||||
|
||||
supportsNativeCopyAndPaste = CPFeatureIsCompatible(CPJavaScriptClipboardEventsFeature),
|
||||
hasBugWhichPreventsNonEditablePaste = CPPlatformHasBug(CPJavaScriptPasteRequiresEditableTarget),
|
||||
hasBugWhichPreventsNonEditablePasteRedirect = CPPlatformHasBug(CPJavaScriptPasteCantRefocus);
|
||||
supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
|
||||
|
||||
var resizeTimer = nil;
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
|
||||
var hasEditableTarget = function(aDOMEvent)
|
||||
{
|
||||
var target = (aDOMEvent).target || (aDOMEvent).srcElement;
|
||||
|
||||
if (!target)
|
||||
return NO;
|
||||
|
||||
if (target.contentEditable == "true")
|
||||
return YES;
|
||||
|
||||
var nodeName = target.nodeName.toUpperCase();
|
||||
|
||||
return nodeName === "INPUT" || nodeName == "TEXTAREA";
|
||||
}
|
||||
|
||||
@implementation CPPlatformWindow (DOM)
|
||||
|
||||
- (id)_init
|
||||
@@ -236,6 +217,8 @@ var hasEditableTarget = function(aDOMEvent)
|
||||
_windowLevels = [];
|
||||
_windowLayers = @{};
|
||||
|
||||
_platformPasteboard = [CPPlatformPasteboard new];
|
||||
|
||||
[self registerDOMWindow];
|
||||
[self updateFromNativeContentRect];
|
||||
|
||||
@@ -322,19 +305,6 @@ var hasEditableTarget = function(aDOMEvent)
|
||||
|
||||
_DOMBodyElement.appendChild(_DOMFocusElement);
|
||||
|
||||
// Create Native Pasteboard handler.
|
||||
_DOMPasteboardElement = theDocument.createElement("textarea");
|
||||
|
||||
_DOMPasteboardElement.style.position = "absolute";
|
||||
_DOMPasteboardElement.style.top = "-10000px";
|
||||
_DOMPasteboardElement.style.zIndex = "999";
|
||||
_DOMPasteboardElement.className = "cpdontremove";
|
||||
|
||||
_DOMBodyElement.appendChild(_DOMPasteboardElement);
|
||||
|
||||
// Make sure the pastboard element is blurred.
|
||||
_DOMPasteboardElement.blur();
|
||||
|
||||
// Create a full screen div to protect against iframes and other elements
|
||||
// from consuming events during tracking
|
||||
// FIXME: multiple windows
|
||||
@@ -373,13 +343,6 @@ var hasEditableTarget = function(aDOMEvent)
|
||||
_DOMScrollingElement.scrollLeft = 150;
|
||||
}
|
||||
|
||||
- (void)registerDOMElement:(DOMElement)anElement
|
||||
{
|
||||
var nativeBeforeClipboardEventCallback = function (anEvent) { return [self nativeBeforeClipboardEvent:anEvent]; };
|
||||
|
||||
anElement.addEventListener("beforepaste", nativeBeforeClipboardEventCallback, NO);
|
||||
}
|
||||
|
||||
- (void)registerDOMWindow
|
||||
{
|
||||
var theDocument = _DOMWindow.document;
|
||||
@@ -395,6 +358,8 @@ var hasEditableTarget = function(aDOMEvent)
|
||||
[self createDOMElements];
|
||||
[self _addLayers];
|
||||
|
||||
[_platformPasteboard setDOMWindow:_DOMWindow];
|
||||
|
||||
var theClass = [self class],
|
||||
|
||||
dragEventImplementation = class_getMethodImplementation(theClass, @selector(dragEvent:)),
|
||||
@@ -404,20 +369,6 @@ var hasEditableTarget = function(aDOMEvent)
|
||||
resizeEventImplementation = class_getMethodImplementation(theClass, resizeEventSelector),
|
||||
resizeEventCallback = function (anEvent) { resizeEventImplementation(self, nil, anEvent); },
|
||||
|
||||
copyEventSelector = @selector(copyEvent:),
|
||||
copyEventImplementation = class_getMethodImplementation(theClass, copyEventSelector),
|
||||
copyEventCallback = function (anEvent) {copyEventImplementation(self, nil, anEvent); },
|
||||
|
||||
nativeBeforeClipboardEventCallback = function (anEvent) { return [self nativeBeforeClipboardEvent:anEvent]; },
|
||||
|
||||
nativeCopyOrCutEventCallback = function (anEvent) { return [self nativeCopyOrCutEvent:anEvent]; },
|
||||
|
||||
pasteEventSelector = @selector(pasteEvent:),
|
||||
pasteEventImplementation = class_getMethodImplementation(theClass, pasteEventSelector),
|
||||
pasteEventCallback = function (anEvent) { return pasteEventImplementation(self, nil, anEvent); },
|
||||
|
||||
nativePasteEventCallback = function (anEvent) { return [self nativePasteEvent:anEvent]; },
|
||||
|
||||
keyEventSelector = @selector(keyEvent:),
|
||||
keyEventImplementation = class_getMethodImplementation(theClass, keyEventSelector),
|
||||
keyEventCallback = function (anEvent) { return keyEventImplementation(self, nil, anEvent); },
|
||||
@@ -455,22 +406,6 @@ var hasEditableTarget = function(aDOMEvent)
|
||||
theDocument.addEventListener("mousemove", mouseEventCallback, NO);
|
||||
theDocument.addEventListener("contextmenu", contextMenuEventCallback, NO);
|
||||
|
||||
if (supportsNativeCopyAndPaste)
|
||||
{
|
||||
_DOMWindow.addEventListener("beforecopy", nativeBeforeClipboardEventCallback, NO);
|
||||
_DOMWindow.addEventListener("beforecut", nativeBeforeClipboardEventCallback, NO);
|
||||
_DOMWindow.addEventListener("beforepaste", nativeBeforeClipboardEventCallback, NO);
|
||||
_DOMWindow.addEventListener("copy", nativeCopyOrCutEventCallback, NO);
|
||||
_DOMWindow.addEventListener("cut", nativeCopyOrCutEventCallback, NO);
|
||||
_DOMWindow.addEventListener("paste", nativePasteEventCallback, NO);
|
||||
}
|
||||
else
|
||||
{
|
||||
theDocument.addEventListener("beforepaste", pasteEventCallback, NO);
|
||||
theDocument.addEventListener("beforecopy", copyEventCallback, NO);
|
||||
theDocument.addEventListener("beforecut", copyEventCallback, NO);
|
||||
}
|
||||
|
||||
theDocument.addEventListener("keyup", keyEventCallback, NO);
|
||||
theDocument.addEventListener("keydown", keyEventCallback, NO);
|
||||
theDocument.addEventListener("keypress", keyEventCallback, NO);
|
||||
@@ -500,22 +435,6 @@ var hasEditableTarget = function(aDOMEvent)
|
||||
theDocument.removeEventListener("keydown", keyEventCallback, NO);
|
||||
theDocument.removeEventListener("keypress", keyEventCallback, NO);
|
||||
|
||||
if (supportsNativeCopyAndPaste)
|
||||
{
|
||||
_DOMWindow.removeEventListener("beforecopy", nativeBeforeClipboardEventCallback, NO);
|
||||
_DOMWindow.removeEventListener("beforecut", nativeBeforeClipboardEventCallback, NO);
|
||||
_DOMWindow.removeEventListener("beforepaste", nativeBeforeClipboardEventCallback, NO);
|
||||
_DOMWindow.removeEventListener("copy", nativeCopyOrCutEventCallback, NO);
|
||||
_DOMWindow.removeEventListener("cut", nativeCopyOrCutEventCallback, NO);
|
||||
_DOMWindow.removeEventListener("paste", nativePasteEventCallback, NO);
|
||||
}
|
||||
else
|
||||
{
|
||||
theDocument.removeEventListener("beforepaste", pasteEventCallback, NO);
|
||||
theDocument.removeEventListener("beforecopy", copyEventCallback, NO);
|
||||
theDocument.removeEventListener("beforecut", copyEventCallback, NO);
|
||||
}
|
||||
|
||||
theDocument.removeEventListener("touchstart", touchEventCallback, NO);
|
||||
theDocument.removeEventListener("touchend", touchEventCallback, NO);
|
||||
theDocument.removeEventListener("touchmove", touchEventCallback, NO);
|
||||
@@ -531,6 +450,8 @@ var hasEditableTarget = function(aDOMEvent)
|
||||
|
||||
[PlatformWindows removeObject:self];
|
||||
|
||||
[_platformPasteboard setDOMWindow:nil];
|
||||
|
||||
self._DOMWindow = nil;
|
||||
}, NO);
|
||||
}
|
||||
@@ -552,7 +473,7 @@ var hasEditableTarget = function(aDOMEvent)
|
||||
theDocument.onmousewheel = scrollEventCallback;
|
||||
|
||||
_DOMBodyElement.ondrag = function () { return NO; };
|
||||
_DOMBodyElement.onselectstart = function () { return _DOMWindow.event.srcElement === _DOMPasteboardElement; };
|
||||
_DOMBodyElement.onselectstart = function () { return _DOMWindow.event.srcElement === _platformPasteboard._DOMPasteboardElement; };
|
||||
|
||||
_DOMWindow.attachEvent("onunload", function()
|
||||
{
|
||||
@@ -581,6 +502,8 @@ var hasEditableTarget = function(aDOMEvent)
|
||||
|
||||
[PlatformWindows removeObject:self];
|
||||
|
||||
[_platformPasteboard setDOMWindow:nil];
|
||||
|
||||
self._DOMWindow = nil;
|
||||
}, NO);
|
||||
}
|
||||
@@ -767,9 +690,7 @@ var hasEditableTarget = function(aDOMEvent)
|
||||
StopDOMEventPropagation = NO;
|
||||
}
|
||||
|
||||
var isNativePasteEvent = NO,
|
||||
isNativeCopyOrCutEvent = NO,
|
||||
overrideCharacters = nil,
|
||||
var overrideCharacters = nil,
|
||||
charactersIgnoringModifiers = @"";
|
||||
|
||||
switch (aDOMEvent.type)
|
||||
@@ -811,47 +732,6 @@ var hasEditableTarget = function(aDOMEvent)
|
||||
//we are simply going to skip all keypress events that use cmd/ctrl key
|
||||
//this lets us be consistent in all browsers and send on the keydown
|
||||
//which means we can cancel the event early enough, but only if sendEvent needs to
|
||||
|
||||
var mayRequireDOMPasteboardElement = [self _requiresDOMPasteboardElement:aDOMEvent flags:modifierFlags];
|
||||
|
||||
// If this could be a native PASTE event, then we need to further examine it before
|
||||
// sending a CPEvent. Select our element to see if anything gets pasted in it.
|
||||
if (characters === "v" && mayRequireDOMPasteboardElement)
|
||||
{
|
||||
StopDOMEventPropagation = NO;
|
||||
|
||||
if (supportsNativeCopyAndPaste && hasBugWhichPreventsNonEditablePaste && hasBugWhichPreventsNonEditablePasteRedirect && !hasEditableTarget(aDOMEvent))
|
||||
{
|
||||
// You can't paste from the system clipboard into a non-editable area in Safari, neither using native
|
||||
// copy and paste nor our _DOMPasteboardElement hack. We will paste from the Cappuccino pasteboard only
|
||||
// and allow Safari to "beep" to indicate something went wrong.
|
||||
|
||||
isNativePasteEvent = NO;
|
||||
}
|
||||
else if (!(supportsNativeCopyAndPaste || hasBugWhichPreventsNonEditablePaste) && !_ignoreNativePastePreparation)
|
||||
{
|
||||
// We don't support native copy and paste so we must focus the _DOMPasteboardElement to receive the
|
||||
// paste content.
|
||||
_DOMPasteboardElement.focus();
|
||||
_DOMPasteboardElement.select();
|
||||
_DOMPasteboardElement.value = "";
|
||||
|
||||
isNativePasteEvent = YES;
|
||||
}
|
||||
else if (supportsNativeCopyAndPaste)
|
||||
isNativePasteEvent = YES;
|
||||
}
|
||||
|
||||
// However, of this could be a native COPY event, we need to let the normal event-process take place so it
|
||||
// can capture our internal Cappuccino pasteboard.
|
||||
else if ((characters == "c" || characters == "x") && mayRequireDOMPasteboardElement)
|
||||
{
|
||||
StopDOMEventPropagation = NO;
|
||||
isNativeCopyOrCutEvent = YES;
|
||||
|
||||
if (!supportsNativeCopyAndPaste && _ignoreNativeCopyOrCutEvent)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (CPKeyCodes.firesKeyPressEvent(_keyCode, _lastKey, aDOMEvent.shiftKey, aDOMEvent.ctrlKey, aDOMEvent.altKey))
|
||||
{
|
||||
@@ -897,18 +777,6 @@ var hasEditableTarget = function(aDOMEvent)
|
||||
timestamp:timestamp windowNumber:windowNumber context:nil
|
||||
characters:characters charactersIgnoringModifiers:charactersIgnoringModifiers isARepeat:isARepeat keyCode:charCode];
|
||||
|
||||
if (isNativePasteEvent)
|
||||
{
|
||||
_pasteboardKeyDownEvent = event;
|
||||
|
||||
// If we are using the paste into field hack, we need to check it in a moment.
|
||||
if (!supportsNativeCopyAndPaste)
|
||||
window.setNativeTimeout(function () { [self _checkPasteboardElement]; }, 0);
|
||||
|
||||
// We need the event to propagate or nothing will be pasted.
|
||||
StopDOMEventPropagation = NO;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "keyup":
|
||||
@@ -918,8 +786,6 @@ var hasEditableTarget = function(aDOMEvent)
|
||||
_keyCode = -1;
|
||||
_lastKey = -1;
|
||||
_charCodes[keyCode] = nil;
|
||||
_ignoreNativeCopyOrCutEvent = NO;
|
||||
_ignoreNativePastePreparation = NO;
|
||||
|
||||
// check for caps lock state
|
||||
if (keyCode === CPKeyCodes.CAPS_LOCK)
|
||||
@@ -948,249 +814,29 @@ var hasEditableTarget = function(aDOMEvent)
|
||||
break;
|
||||
}
|
||||
|
||||
// If we are going to use our native cut and paste handlers, they will fake the keydown later. So we need to
|
||||
// not send it here too, or a single cut turns into 2 cuts, etc.
|
||||
if (event && !isNativePasteEvent && (!supportsNativeCopyAndPaste || !isNativeCopyOrCutEvent))
|
||||
{
|
||||
if (event)
|
||||
event._DOMEvent = aDOMEvent;
|
||||
|
||||
[_platformPasteboard windowMaySendKeyEvent:event];
|
||||
|
||||
if (event && ![_platformPasteboard windowShouldSuppressKeyEvent])
|
||||
{
|
||||
[CPApp sendEvent:event];
|
||||
|
||||
if (!supportsNativeCopyAndPaste && isNativeCopyOrCutEvent)
|
||||
{
|
||||
// If this is a native copy event, then check if the pasteboard has anything in it.
|
||||
[self _primeDOMPasteboardElement];
|
||||
}
|
||||
[_platformPasteboard windowDidSendKeyEvent:event];
|
||||
}
|
||||
|
||||
if (StopDOMEventPropagation)
|
||||
CPDOMEventStop(aDOMEvent, self);
|
||||
var didStop = NO;
|
||||
// Platform pasteboard can overrule the decision to stop propagation either way, or it might have no opinion.
|
||||
if ([_platformPasteboard windowShouldStopPropagation] || (StopDOMEventPropagation && ![_platformPasteboard windowShouldNotStopPropagation]))
|
||||
{
|
||||
didStop = YES;
|
||||
_CPDOMEventStop(aDOMEvent, self);
|
||||
}
|
||||
|
||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||
|
||||
return !StopDOMEventPropagation
|
||||
}
|
||||
|
||||
- (CPEvent)_fakeClipboardEvent:(DOMEvent)aDOMEvent type:(CPString)aType
|
||||
{
|
||||
var keyCode = aType === "x" ? CPKeyCodes.X : (aType === "c" ? CPKeyCodes.C : CPKeyCodes.V),
|
||||
characters = aType,
|
||||
timestamp = [CPEvent currentTimestamp], // fake event, might as well use current timestamp
|
||||
windowNumber = [[CPApp keyWindow] windowNumber],
|
||||
modifierFlags = CPPlatformActionKeyMask,
|
||||
location = _lastMouseEventLocation || CGPointMakeZero(),
|
||||
event = [CPEvent keyEventWithType:CPKeyDown location:location modifierFlags:modifierFlags
|
||||
timestamp:timestamp windowNumber:windowNumber context:nil
|
||||
characters:characters charactersIgnoringModifiers:characters isARepeat:NO keyCode:keyCode];
|
||||
|
||||
event._data1 = @{ "simulated": YES };
|
||||
event._DOMEvent = aDOMEvent;
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
- (void)copyEvent:(DOMEvent)aDOMEvent
|
||||
{
|
||||
if ([self _requiresDOMPasteboardElement:aDOMEvent flags:CPPlatformActionKeyMask] && !_ignoreNativeCopyOrCutEvent)
|
||||
{
|
||||
// we have to send out a fake copy or cut event so that we can force the copy/cut mechanisms to take place
|
||||
var event = [self _fakeClipboardEvent:aDOMEvent type:(aDOMEvent.type === "beforecut" ? "x" : "c")];
|
||||
|
||||
[CPApp sendEvent:event];
|
||||
|
||||
[self _primeDOMPasteboardElement];
|
||||
|
||||
//then we have to IGNORE the real keyboard event to prevent a double copy
|
||||
//safari also sends the beforecopy event twice, so we additionally check here and prevent two events
|
||||
_ignoreNativeCopyOrCutEvent = YES;
|
||||
}
|
||||
|
||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||
}
|
||||
|
||||
- (boolean)nativeBeforeClipboardEvent:(DOMEvent)aDOMEvent
|
||||
{
|
||||
// Our job here is to return "false" if the given clipboard operation should be enabled even in a situation where
|
||||
// the browser might normally grey the option out.
|
||||
|
||||
// Allow these fields to do their own thing.
|
||||
if (hasEditableTarget(aDOMEvent))
|
||||
return true;
|
||||
|
||||
var returnValue = YES;
|
||||
|
||||
switch (aDOMEvent.type)
|
||||
{
|
||||
case "beforecopy":
|
||||
returnValue = !([CPApp targetForAction:@selector(copy:)]);
|
||||
break;
|
||||
case "beforecut":
|
||||
returnValue = !([CPApp targetForAction:@selector(cut:)]);
|
||||
break;
|
||||
case "beforepaste":
|
||||
returnValue = !([CPApp targetForAction:@selector(paste:)]);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!returnValue)
|
||||
CPDOMEventStop(aDOMEvent, self);
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
- (boolean)nativePasteEvent:(DOMEvent)aDOMEvent
|
||||
{
|
||||
// This shouldn't happen.
|
||||
if (!supportsNativeCopyAndPaste)
|
||||
return;
|
||||
|
||||
var value;
|
||||
if (aDOMEvent.clipboardData && aDOMEvent.clipboardData.setData)
|
||||
value = aDOMEvent.clipboardData.getData('text/plain');
|
||||
else
|
||||
value = window.clipboardData.getData("Text");
|
||||
|
||||
if ([value length])
|
||||
{
|
||||
var pasteboard = [CPPasteboard generalPasteboard];
|
||||
|
||||
if ([pasteboard _stateUID] != value)
|
||||
{
|
||||
[pasteboard declareTypes:[CPStringPboardType] owner:self];
|
||||
[pasteboard setString:value forType:CPStringPboardType];
|
||||
}
|
||||
}
|
||||
|
||||
// By default we'll stop the native handling of the event since we're handling it ourselves. However, we need to
|
||||
// stop it before we send the event so that the event can overrule our choice. CPTextField for instance wants the
|
||||
// default handling when focused (which is to insert into the field).
|
||||
StopDOMEventPropagation = YES;
|
||||
|
||||
[CPApp sendEvent:[self _fakeClipboardEvent:aDOMEvent type:"v"]];
|
||||
|
||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||
|
||||
if (StopDOMEventPropagation)
|
||||
CPDOMEventStop(aDOMEvent, self);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
- (boolean)nativeCopyOrCutEvent:(DOMEvent)aDOMEvent
|
||||
{
|
||||
// This shouldn't happen.
|
||||
if (!supportsNativeCopyAndPaste)
|
||||
return;
|
||||
|
||||
StopDOMEventPropagation = YES;
|
||||
|
||||
// Let the app react through copy: and cut: actions.
|
||||
[CPApp sendEvent:[self _fakeClipboardEvent:aDOMEvent type:(aDOMEvent.type.indexOf("cut") != CPNotFound ? "x" : "c")]];
|
||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||
|
||||
// If StopDOMEventPropagation was set to NO, we don't try to write to the system clipboard. The control that did this
|
||||
// wants to use the default copy/cut functionality.
|
||||
if (StopDOMEventPropagation)
|
||||
{
|
||||
// Now the app should have written whatever it wants to have copied to the Cappuccino clipboard. So now we need
|
||||
// to write it to the system board.
|
||||
CPDOMEventStop(aDOMEvent, self);
|
||||
|
||||
var pasteboard = [CPPasteboard generalPasteboard];
|
||||
|
||||
if ([[pasteboard types] containsObject:CPStringPboardType])
|
||||
{
|
||||
var stringValue = [pasteboard stringForType:CPStringPboardType];
|
||||
|
||||
if (aDOMEvent.clipboardData && aDOMEvent.clipboardData.setData)
|
||||
aDOMEvent.clipboardData.setData('text/plain', stringValue);
|
||||
else
|
||||
window.clipboardData.setData('Text', stringValue);
|
||||
}
|
||||
}
|
||||
|
||||
return StopDOMEventPropagation;
|
||||
}
|
||||
|
||||
- (boolean)pasteEvent:(DOMEvent)aDOMEvent
|
||||
{
|
||||
// Set up to capture the paste in a temporary input field. We'll send the event after capture.
|
||||
if ([self _requiresDOMPasteboardElement:aDOMEvent flags:CPPlatformActionKeyMask])
|
||||
{
|
||||
_DOMPasteboardElement.focus();
|
||||
_DOMPasteboardElement.select();
|
||||
_DOMPasteboardElement.value = "";
|
||||
|
||||
_ignoreNativePastePreparation = YES;
|
||||
}
|
||||
|
||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||
}
|
||||
|
||||
/*
|
||||
Return true if the event may be a copy and paste event, but the target is not an input or text area.
|
||||
*/
|
||||
- (void)_requiresDOMPasteboardElement:(DOMEvent)aDOMEvent flags:(unsigned)modifierFlags
|
||||
{
|
||||
return !hasEditableTarget(aDOMEvent) && (modifierFlags & CPPlatformActionKeyMask);
|
||||
}
|
||||
|
||||
- (void)_primeDOMPasteboardElement
|
||||
{
|
||||
if (supportsNativeCopyAndPaste)
|
||||
return;
|
||||
|
||||
var pasteboard = [CPPasteboard generalPasteboard],
|
||||
types = [pasteboard types];
|
||||
|
||||
if (types.length)
|
||||
{
|
||||
if ([types indexOfObjectIdenticalTo:CPStringPboardType] !== CPNotFound)
|
||||
_DOMPasteboardElement.value = [pasteboard stringForType:CPStringPboardType];
|
||||
else
|
||||
_DOMPasteboardElement.value = [pasteboard _generateStateUID];
|
||||
|
||||
_DOMPasteboardElement.focus();
|
||||
_DOMPasteboardElement.select();
|
||||
|
||||
window.setNativeTimeout(function() { [self _clearPasteboardElement]; }, 0);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_checkPasteboardElement
|
||||
{
|
||||
if (supportsNativeCopyAndPaste)
|
||||
{
|
||||
[self _clearPasteboardElement];
|
||||
return;
|
||||
}
|
||||
|
||||
var value = _DOMPasteboardElement.value;
|
||||
|
||||
if ([value length])
|
||||
{
|
||||
var pasteboard = [CPPasteboard generalPasteboard];
|
||||
|
||||
if ([pasteboard _stateUID] != value)
|
||||
{
|
||||
[pasteboard declareTypes:[CPStringPboardType] owner:self];
|
||||
[pasteboard setString:value forType:CPStringPboardType];
|
||||
}
|
||||
}
|
||||
|
||||
[self _clearPasteboardElement];
|
||||
|
||||
[CPApp sendEvent:_pasteboardKeyDownEvent];
|
||||
|
||||
_pasteboardKeyDownEvent = nil;
|
||||
|
||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||
}
|
||||
|
||||
- (void)_clearPasteboardElement
|
||||
{
|
||||
_DOMPasteboardElement.value = "";
|
||||
_DOMPasteboardElement.blur();
|
||||
return !didStop;
|
||||
}
|
||||
|
||||
- (void)scrollEvent:(DOMEvent)aDOMEvent
|
||||
@@ -1287,7 +933,7 @@ Return true if the event may be a copy and paste event, but the target is not an
|
||||
|
||||
// We set StopDOMEventPropagation = NO on line 1008
|
||||
//if (StopDOMEventPropagation)
|
||||
// CPDOMEventStop(aDOMEvent, self);
|
||||
// _CPDOMEventStop(aDOMEvent, self);
|
||||
|
||||
// Reset the DOM elements scroll offset
|
||||
_DOMScrollingElement.scrollLeft = 150;
|
||||
@@ -1529,7 +1175,7 @@ Return true if the event may be a copy and paste event, but the target is not an
|
||||
}
|
||||
|
||||
if (StopDOMEventPropagation && (!supportsNativeDragAndDrop || type !== "mousedown" && !isDragging))
|
||||
CPDOMEventStop(aDOMEvent, self);
|
||||
_CPDOMEventStop(aDOMEvent, self);
|
||||
|
||||
// If there are any tracking event listeners (listening for CPLeftMouseDraggedMask)
|
||||
// then show the event guard so we don't lose events to iframes
|
||||
@@ -1556,7 +1202,7 @@ Return true if the event may be a copy and paste event, but the target is not an
|
||||
- (void)contextMenuEvent:(DOMEvent)aDOMEvent
|
||||
{
|
||||
if (StopContextMenuDOMEventPropagation)
|
||||
CPDOMEventStop(aDOMEvent, self);
|
||||
_CPDOMEventStop(aDOMEvent, self);
|
||||
|
||||
return !StopContextMenuDOMEventPropagation;
|
||||
}
|
||||
@@ -1957,7 +1603,8 @@ var CPDOMEventGetClickCount = function(aComparisonEvent, aTimestamp, aLocation)
|
||||
ABS(comparisonLocation.y - aLocation.y) < CLICK_SPACE_DELTA) ? [aComparisonEvent clickCount] + 1 : 1;
|
||||
};
|
||||
|
||||
var CPDOMEventStop = function(aDOMEvent, aPlatformWindow)
|
||||
// Global.
|
||||
_CPDOMEventStop = function(aDOMEvent, aPlatformWindow)
|
||||
{
|
||||
// IE Model
|
||||
aDOMEvent.cancelBubble = true;
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
@import <Foundation/Foundation.j>
|
||||
@import <AppKit/AppKit.j>
|
||||
|
||||
@import "../CPTrace.j"
|
||||
@import "CollectionViewItem.j"
|
||||
|
||||
@implementation AppController : CPObject
|
||||
|
||||
Reference in new issue
Block a user