mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-08-25 04:57:03 +00:00
1260 lines
35 KiB
Plaintext
1260 lines
35 KiB
Plaintext
/*
|
|
* CPWindow.j
|
|
* AppKit
|
|
*
|
|
* Created by Francisco Tolmasky.
|
|
* Copyright 2008, 280 North, Inc.
|
|
*
|
|
* 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/CPNotificationCenter.j>
|
|
import <Foundation/CPUndoManager.j>
|
|
|
|
import "CGGeometry.j"
|
|
import "CPAnimation.j"
|
|
import "CPResponder.j"
|
|
|
|
#include "Platform/Platform.h"
|
|
#include "Platform/DOM/CPDOMDisplayServer.h"
|
|
|
|
#include "CoreGraphics/CGGeometry.h"
|
|
|
|
|
|
CPBorderlessWindowMask = 0;
|
|
CPTitledWindowMask = 1 << 0;
|
|
CPClosableWindowMask = 1 << 1;
|
|
CPMiniaturizableWindowMask = 1 << 2;
|
|
CPResizableWindowMask = 1 << 3;
|
|
CPTexturedBackgroundWindowMask = 1 << 8;
|
|
|
|
CPBorderlessBridgeWindowMask = 1 << 20;
|
|
CPHUDBackgroundWindowMask = 1 << 21;
|
|
|
|
CPWindowNotSizable = 0;
|
|
CPWindowMinXMargin = 1;
|
|
CPWindowWidthSizable = 2;
|
|
CPWindowMaxXMargin = 4;
|
|
CPWindowMinYMargin = 8;
|
|
CPWindowHeightSizable = 16;
|
|
CPWindowMaxYMargin = 32;
|
|
|
|
CPNormalWindowLevel = 4;
|
|
CPFloatingWindowLevel = 5;
|
|
CPSubmenuWindowLevel = 6;
|
|
CPTornOffMenuWindowLevel = 6;
|
|
CPMainMenuWindowLevel = 8;
|
|
CPStatusWindowLevel = 9;
|
|
CPModalPanelWindowLevel = 10;
|
|
CPPopUpMenuWindowLevel = 11;
|
|
CPDraggingWindowLevel = 12;
|
|
CPScreenSaverWindowLevel = 13;
|
|
|
|
CPWindowOut = 0;
|
|
CPWindowAbove = 1;
|
|
CPWindowBelow = 2;
|
|
|
|
CPWindowWillCloseNotification = @"CPWindowWillCloseNotification";
|
|
CPWindowDidBecomeMainNotification = @"CPWindowDidBecomeMainNotification";
|
|
CPWindowDidResignMainNotification = @"CPWindowDidResignMainNotification";
|
|
|
|
|
|
var SHADOW_MARGIN_LEFT = 20.0,
|
|
SHADOW_MARGIN_RIGHT = 19.0,
|
|
SHADOW_MARGIN_TOP = 10.0,
|
|
SHADOW_MARGIN_BOTTOM = 10.0,
|
|
SHADOW_DISTANCE = 5.0,
|
|
|
|
_CPWindowShadowColor = nil;
|
|
|
|
var CPWindowSaveImage = nil,
|
|
CPWindowSavingImage = nil;
|
|
|
|
@implementation CPWindow : CPResponder
|
|
{
|
|
int _windowNumber;
|
|
unsigned _styleMask;
|
|
CGRect _frame;
|
|
int _level;
|
|
BOOL _isVisible;
|
|
BOOL _isAnimating;
|
|
BOOL _hasShadow;
|
|
BOOL _isMovableByWindowBackground;
|
|
|
|
BOOL _isDocumentEdited;
|
|
BOOL _isDocumentSaving;
|
|
|
|
CPNinePartImageView _shadowView;
|
|
|
|
CPView _windowView;
|
|
CPView _contentView;
|
|
CPView _toolbarView;
|
|
|
|
CPView _mouseOverView;
|
|
CPView _leftMouseDownView;
|
|
CPView _rightMouseDownView;
|
|
|
|
CPToolbar _toolbar;
|
|
CPResponder _firstResponder;
|
|
id _delegate;
|
|
|
|
CPString _title;
|
|
|
|
BOOL _acceptsMouseMovedEvents;
|
|
|
|
CPWindowController _windowController;
|
|
|
|
CGSize _minSize;
|
|
CGSize _maxSize;
|
|
|
|
CGRect _resizeFrame;
|
|
CGPoint _mouseDraggedPoint;
|
|
|
|
CPUndoManager _undoManager;
|
|
CPURL _representedURL;
|
|
|
|
// Bridge Support
|
|
DOMElement _DOMElement;
|
|
CPDOMWindowBridge _bridge;
|
|
unsigned _autoresizingMask;
|
|
|
|
BOOL _delegateRespondsToWindowWillReturnUndoManagerSelector;
|
|
}
|
|
|
|
+ (void)initialize
|
|
{
|
|
if (self != [CPWindow class])
|
|
return;
|
|
|
|
var bundle = [CPBundle bundleForClass:[CPWindow class]];
|
|
|
|
CPWindowResizeIndicatorImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindowResizeIndicator.png"] size:CGSizeMake(12.0, 12.0)];
|
|
|
|
CPWindowSavingImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPProgressIndicator/CPProgressIndicatorSpinningStyleRegular.gif"] size:CGSizeMake(16.0, 16.0)]
|
|
}
|
|
|
|
- (id)initWithContentRect:(CGRect)aContentRect styleMask:(unsigned int)aStyleMask
|
|
{
|
|
return [self initWithContentRect:aContentRect styleMask:aStyleMask bridge:[CPDOMWindowBridge sharedDOMWindowBridge]];
|
|
}
|
|
|
|
- (id)initWithContentRect:(CGRect)aContentRect styleMask:(unsigned int)aStyleMask bridge:(CPDOMWindowBridge)aBridge
|
|
{
|
|
self = [super init];
|
|
|
|
if (self)
|
|
{
|
|
// Set up our window number.
|
|
_windowNumber = [CPApp._windows count];
|
|
CPApp._windows[_windowNumber] = self;
|
|
|
|
_styleMask = aStyleMask;
|
|
|
|
_frame = [self frameRectForContentRect:aContentRect];
|
|
|
|
_level = CPNormalWindowLevel;
|
|
_hasShadow = NO;
|
|
|
|
_minSize = CGSizeMake(0.0, 0.0);
|
|
_maxSize = CGSizeMake(1000000.0, 1000000.0);
|
|
|
|
if (_styleMask & CPBorderlessBridgeWindowMask)
|
|
_autoresizingMask = CPWindowWidthSizable | CPWindowHeightSizable;
|
|
|
|
// Create our border view which is the actual root of our view hierarchy.
|
|
_windowView = [[_CPWindowView alloc] initWithFrame:CGRectMake(0.0, 0.0, CGRectGetWidth(_frame), CGRectGetHeight(_frame)) forStyleMask:_styleMask];
|
|
|
|
[_windowView _setWindow:self];
|
|
[_windowView setNextResponder:self];
|
|
|
|
[self setMovableByWindowBackground:aStyleMask & CPHUDBackgroundWindowMask];
|
|
|
|
// Create a generic content view.
|
|
[self setContentView:[[CPView alloc] initWithFrame:CGRectMakeZero()]];
|
|
|
|
_firstResponder = self;
|
|
|
|
_DOMElement = document.createElement("div");
|
|
|
|
_DOMElement.style.position = "absolute";
|
|
_DOMElement.style.visibility = "visible";
|
|
_DOMElement.style.zIndex = 0;
|
|
|
|
CPDOMDisplayServerSetStyleLeftTop(_DOMElement, NULL, _CGRectGetMinX(_frame), _CGRectGetMinY(_frame));
|
|
CPDOMDisplayServerSetStyleSize(_DOMElement, 1, 1);
|
|
|
|
CPDOMDisplayServerAppendChild(_DOMElement, _windowView._DOMElement);
|
|
|
|
[self setBridge:aBridge];
|
|
|
|
[self setNextResponder:CPApp];
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (unsigned)styleMask
|
|
{
|
|
return _styleMask;
|
|
}
|
|
|
|
+ (CGRect)frameRectForContentRect:(CGRect)aContentRect styleMask:(unsigned)aStyleMask
|
|
{
|
|
var frame = CGRectMakeCopy(aContentRect);
|
|
|
|
return frame;
|
|
}
|
|
|
|
- (CGRect)contentRectForFrameRect:(CGRect)aFrame
|
|
{
|
|
// FIXME: EXTRA RECT COPY
|
|
var contentRect = CGRectMakeCopy([_windowView bounds]);
|
|
|
|
if (_styleMask & CPHUDBackgroundWindowMask)
|
|
{
|
|
contentRect.origin.x += 7.0;
|
|
contentRect.origin.y += 30.0;
|
|
contentRect.size.width -= 14.0;
|
|
contentRect.size.height -= 40.0;
|
|
}
|
|
|
|
else if (_styleMask & CPBorderlessBridgeWindowMask)
|
|
{
|
|
// The full width, like borderless.
|
|
}
|
|
|
|
if ([_toolbar isVisible])
|
|
{
|
|
var toolbarHeight = CGRectGetHeight([_toolbarView frame]);
|
|
|
|
contentRect.origin.y += toolbarHeight;
|
|
contentRect.size.height -= toolbarHeight;
|
|
}
|
|
|
|
return contentRect;
|
|
}
|
|
|
|
- (CGRect)frameRectForContentRect:(CGRect)aContentRect
|
|
{
|
|
if (_styleMask & CPBorderlessBridgeWindowMask)
|
|
return _bridge ? [_bridge visibleFrame] : CGRectMakeZero();
|
|
|
|
var frame = [[self class] frameRectForContentRect:aContentRect styleMask:_styleMask];
|
|
|
|
return frame;
|
|
}
|
|
|
|
- (CGRect)frame
|
|
{
|
|
return _frame;
|
|
}
|
|
|
|
- (void)setFrame:(CGRect)aFrame display:(BOOL)shouldDisplay animate:(BOOL)shouldAnimate
|
|
{
|
|
if (shouldAnimate)
|
|
{
|
|
var animation = [[_CPWindowFrameAnimation alloc] initWithWindow:self targetFrame:aFrame];
|
|
|
|
[animation startAnimation];
|
|
}
|
|
else
|
|
{
|
|
[self setFrameOrigin:aFrame.origin];
|
|
[self setFrameSize:aFrame.size];
|
|
}
|
|
}
|
|
|
|
- (void)setFrame:(CGRect)aFrame
|
|
{
|
|
[self setFrame:aFrame display:YES animate:NO];
|
|
}
|
|
|
|
- (void)setFrameOrigin:(CGPoint)anOrigin
|
|
{
|
|
var origin = _frame.origin;
|
|
|
|
if (_CGPointEqualToPoint(origin, anOrigin))
|
|
return;
|
|
|
|
origin.x = anOrigin.x;
|
|
origin.y = anOrigin.y;
|
|
|
|
#if PLATFORM(DOM)
|
|
CPDOMDisplayServerSetStyleLeftTop(_DOMElement, NULL, origin.x, origin.y);
|
|
#endif
|
|
}
|
|
|
|
- (void)setFrameSize:(CGSize)aSize
|
|
{
|
|
aSize = _CGSizeMake(MIN(MAX(aSize.width, _minSize.width), _maxSize.width), MIN(MAX(aSize.height, _minSize.height), _maxSize.height));
|
|
|
|
if (_CGSizeEqualToSize(_frame.size, aSize))
|
|
return;
|
|
|
|
_frame.size = aSize;
|
|
|
|
[_windowView setFrameSize:aSize];
|
|
|
|
if (_hasShadow)
|
|
[_shadowView setFrameSize:_CGSizeMake(SHADOW_MARGIN_LEFT + aSize.width + SHADOW_MARGIN_RIGHT, SHADOW_MARGIN_BOTTOM + aSize.height + SHADOW_MARGIN_TOP + SHADOW_DISTANCE)];
|
|
|
|
if (!_isAnimating && [_delegate respondsToSelector:@selector(windowDidResize:)])
|
|
[_delegate windowDidResize:self];
|
|
}
|
|
|
|
- (void)trackMoveWithEvent:(CPEvent)anEvent
|
|
{
|
|
var type = [anEvent type];
|
|
|
|
if (type == CPLeftMouseUp)
|
|
return;
|
|
|
|
else if (type == CPLeftMouseDown)
|
|
_mouseDraggedPoint = [self convertBaseToBridge:[anEvent locationInWindow]];
|
|
|
|
else if (type == CPLeftMouseDragged)
|
|
{
|
|
var location = [self convertBaseToBridge:[anEvent locationInWindow]];
|
|
|
|
[self setFrameOrigin:CGPointMake(_CGRectGetMinX(_frame) + (location.x - _mouseDraggedPoint.x), _CGRectGetMinY(_frame) + (location.y - _mouseDraggedPoint.y))];
|
|
|
|
_mouseDraggedPoint = location;
|
|
}
|
|
|
|
[CPApp setTarget:self selector:@selector(trackMoveWithEvent:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
|
|
}
|
|
|
|
- (void)trackResizeWithEvent:(CPEvent)anEvent
|
|
{
|
|
var location = [anEvent locationInWindow],
|
|
type = [anEvent type];
|
|
|
|
if (type == CPLeftMouseUp)
|
|
return;
|
|
|
|
else if (type == CPLeftMouseDown)
|
|
_resizeFrame = CGRectMake(location.x, location.y, CGRectGetWidth(_frame), CGRectGetHeight(_frame));
|
|
|
|
else if (type == CPLeftMouseDragged)
|
|
[self setFrameSize:CGSizeMake(CGRectGetWidth(_resizeFrame) + location.x - CGRectGetMinX(_resizeFrame), CGRectGetHeight(_resizeFrame) + location.y - CGRectGetMinY(_resizeFrame))];
|
|
|
|
[CPApp setTarget:self selector:@selector(trackResizeWithEvent:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
|
|
}
|
|
|
|
- (void)orderFront:(id)aSender
|
|
{
|
|
[_bridge order:CPWindowAbove window:self relativeTo:nil];
|
|
}
|
|
|
|
- (void)orderBack:(id)aSender
|
|
{
|
|
//[_bridge order:CPWindowBelow
|
|
}
|
|
|
|
- (void)orderOut:(id)aSender
|
|
{
|
|
if ([_delegate respondsToSelector:@selector(windowWillClose:)])
|
|
[_delegate windowWillClose:self];
|
|
|
|
[_bridge order:CPWindowOut window:self relativeTo:nil];
|
|
|
|
if ([CPApp keyWindow] == self)
|
|
{
|
|
[self resignKeyWindow];
|
|
|
|
CPApp._keyWindow = nil;
|
|
}
|
|
}
|
|
|
|
- (void)orderWindow:(CPWindowOrderingMode)aPlace relativeTo:(int)otherWindowNumber
|
|
{
|
|
[_bridge order:aPlace window:self relativeTo:CPApp._windows[otherWindowNumber]];
|
|
}
|
|
|
|
- (void)setLevel:(int)aLevel
|
|
{
|
|
_level = aLevel;
|
|
}
|
|
|
|
- (int)level
|
|
{
|
|
return _level;
|
|
}
|
|
|
|
- (BOOL)isVisible
|
|
{
|
|
return _isVisible;
|
|
}
|
|
|
|
- (BOOL)showsResizeIndicator
|
|
{
|
|
return [_windowView showsResizeIndicator];
|
|
}
|
|
|
|
- (void)setShowsResizeIndicator:(BOOL)shouldShowResizeIndicator
|
|
{
|
|
[_windowView setShowsResizeIndicator:shouldShowResizeIndicator];
|
|
}
|
|
|
|
- (CGSize)resizeIndicatorOffset
|
|
{
|
|
return _resizeIndicatorOffset;
|
|
}
|
|
|
|
- (void)setResizeIndicatorOffset:(CGSize)aSize
|
|
{
|
|
_resizeIndicatorOffset = CGSizeCreateCopy(aSize);
|
|
|
|
if (_showsResizeIndicator)
|
|
{
|
|
_resizeIndicator.style.top = (CGRectGetMaxY(_windowView._frame) - 13.0 + _resizeIndicatorOffset.height) + "px";
|
|
_resizeIndicator.style.left = (CGRectGetMaxX(_windowView._frame) - 13.0 + _resizeIndicatorOffset.width) + "px";
|
|
}
|
|
}
|
|
|
|
- (void)setContentView:(CPView)aView
|
|
{
|
|
if (_contentView)
|
|
[_contentView removeFromSuperview];
|
|
|
|
_contentView = aView;
|
|
[_contentView setFrame:[self contentRectForFrameRect:_frame]];
|
|
|
|
[_contentView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
|
[_windowView addSubview:_contentView];
|
|
}
|
|
|
|
- (CPView)contentView
|
|
{
|
|
return _contentView;
|
|
}
|
|
|
|
- (void)setBackgroundColor:(CPColor)aColor
|
|
{
|
|
[_windowView setBackgroundColor:aColor];
|
|
}
|
|
|
|
- (CPColor)backgroundColor
|
|
{
|
|
return [_windowView backgroundColor];
|
|
}
|
|
|
|
- (void)setMinSize:(CGSize)aSize
|
|
{
|
|
if (CGSizeEqualToSize(_minSize, aSize))
|
|
return;
|
|
|
|
_minSize = CGSizeCreateCopy(aSize);
|
|
|
|
var size = CGSizeMakeCopy([self frame].size),
|
|
needsFrameChange = NO;
|
|
|
|
if (size.width < _minSize.width)
|
|
{
|
|
size.width = _minSize.width;
|
|
needsFrameChange = YES;
|
|
}
|
|
|
|
if (size.height < _minSize.height)
|
|
{
|
|
size.height = _minSize.height;
|
|
needsFrameChange = YES;
|
|
}
|
|
|
|
if (needsFrameChange)
|
|
[self setFrameSize:size];
|
|
}
|
|
|
|
- (CGSize)minSize
|
|
{
|
|
return _minSize;
|
|
}
|
|
|
|
- (void)setMaxSize:(CGSize)aSize
|
|
{
|
|
if (CGSizeEqualToSize(_maxSize, aSize))
|
|
return;
|
|
|
|
_maxSize = CGSizeCreateCopy(aSize);
|
|
|
|
var size = CGSizeMakeCopy([self frame].size),
|
|
needsFrameChange = NO;
|
|
|
|
if (size.width > _maxSize.width)
|
|
{
|
|
size.width = _maxSize.width;
|
|
needsFrameChange = YES;
|
|
}
|
|
|
|
if (size.height > _maxSize.height)
|
|
{
|
|
size.height = _maxSize.height;
|
|
needsFrameChange = YES;
|
|
}
|
|
|
|
if (needsFrameChange)
|
|
[self setFrameSize:size];
|
|
}
|
|
|
|
- (CGSize)maxSize
|
|
{
|
|
return _maxSize;
|
|
}
|
|
|
|
- (BOOL)hasShadow
|
|
{
|
|
return _hasShadow;
|
|
}
|
|
|
|
- (void)setHasShadow:(BOOL)shouldHaveShadow
|
|
{
|
|
if (_hasShadow == shouldHaveShadow)
|
|
return;
|
|
|
|
_hasShadow = shouldHaveShadow;
|
|
|
|
if (_hasShadow)
|
|
{
|
|
var bounds = [_windowView bounds];
|
|
|
|
_shadowView = [[CPView alloc] initWithFrame:CGRectMake(-SHADOW_MARGIN_LEFT, -SHADOW_MARGIN_TOP + SHADOW_DISTANCE,
|
|
SHADOW_MARGIN_LEFT + CGRectGetWidth(bounds) + SHADOW_MARGIN_RIGHT, SHADOW_MARGIN_TOP + CGRectGetHeight(bounds) + SHADOW_MARGIN_BOTTOM)];
|
|
|
|
if (!_CPWindowShadowColor)
|
|
{
|
|
var bundle = [CPBundle bundleForClass:[self class]];
|
|
|
|
_CPWindowShadowColor = [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices:
|
|
[
|
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/CPWindowShadow0.png"] size:CGSizeMake(20.0, 19.0)],
|
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/CPWindowShadow1.png"] size:CGSizeMake(1.0, 19.0)],
|
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/CPWindowShadow2.png"] size:CGSizeMake(19.0, 19.0)],
|
|
|
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/CPWindowShadow3.png"] size:CGSizeMake(20.0, 1.0)],
|
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/CPWindowShadow4.png"] size:CGSizeMake(1.0, 1.0)],
|
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/CPWindowShadow5.png"] size:CGSizeMake(19.0, 1.0)],
|
|
|
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/CPWindowShadow6.png"] size:CGSizeMake(20.0, 18.0)],
|
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/CPWindowShadow7.png"] size:CGSizeMake(1.0, 18.0)],
|
|
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/CPWindowShadow8.png"] size:CGSizeMake(19.0, 18.0)]
|
|
]]];
|
|
}
|
|
|
|
[_shadowView setBackgroundColor:_CPWindowShadowColor];
|
|
[_shadowView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
|
|
|
CPDOMDisplayServerInsertBefore(_DOMElement, _shadowView._DOMElement, _windowView._DOMElement);
|
|
}
|
|
else
|
|
{
|
|
CPDOMDisplayServerRemoveChild(_DOMElement, _shadowView._DOMElement);
|
|
|
|
_shadowView = nil;
|
|
}
|
|
}
|
|
|
|
- (void)setDelegate:(id)aDelegate
|
|
{
|
|
_delegate = aDelegate;
|
|
|
|
_delegateRespondsToWindowWillReturnUndoManagerSelector = [_delegate respondsToSelector:@selector(windowWillReturnUndoManager:)];
|
|
|
|
if ([_delegate respondsToSelector:@selector(windowDidBecomeMain:)])
|
|
[defaultCenter
|
|
addObserver:_delegate
|
|
selector:@selector(windowDidBecomeMain:)
|
|
name:CPWindowDidBecomeMainNotification
|
|
object:self];
|
|
|
|
if ([_delegate respondsToSelector:@selector(windowDidResignMain:)])
|
|
[defaultCenter
|
|
addObserver:_delegate
|
|
selector:@selector(windowDidResignMain:)
|
|
name:CPWindowDidResignMainNotification
|
|
object:self];
|
|
}
|
|
|
|
- (id)delegate
|
|
{
|
|
return _delegate;
|
|
}
|
|
|
|
- (void)setWindowController:(CPWindow)aWindowController
|
|
{
|
|
_windowController = aWindowController;
|
|
}
|
|
|
|
- (CPWindowController)windowController
|
|
{
|
|
return _windowController;
|
|
}
|
|
|
|
- (void)doCommandBySelector:(SEL)aSelector
|
|
{
|
|
if ([_delegate respondsToSelector:aSelector])
|
|
[_delegate performSelector:aSelector];
|
|
else
|
|
[super doCommandBySelector:aSelector];
|
|
}
|
|
|
|
- (BOOL)acceptsFirstResponder
|
|
{
|
|
return YES;
|
|
}
|
|
|
|
- (void)makeFirstResponder:(CPResponder)aResponder
|
|
{
|
|
if (_firstResponder == aResponder)
|
|
return YES;
|
|
|
|
if(![_firstResponder resignFirstResponder])
|
|
return NO;
|
|
|
|
if(!aResponder || ![aResponder acceptsFirstResponder] || ![aResponder becomeFirstResponder])
|
|
{
|
|
_firstResponder = self;
|
|
|
|
return NO;
|
|
}
|
|
|
|
_firstResponder = aResponder;
|
|
|
|
return YES;
|
|
}
|
|
|
|
- (CPResponder)firstResponder
|
|
{
|
|
return _firstResponder;
|
|
}
|
|
|
|
- (BOOL)acceptsMouseMovedEvents
|
|
{
|
|
return _acceptsMouseMovedEvents;
|
|
}
|
|
|
|
- (void)setAcceptsMouseMovedEvents:(BOOL)shouldAcceptMouseMovedEvents
|
|
{
|
|
_acceptsMouseMovedEvents = shouldAcceptMouseMovedEvents;
|
|
}
|
|
|
|
// Managing Titles
|
|
|
|
- (CPString)title
|
|
{
|
|
return _title;
|
|
}
|
|
|
|
- (void)setTitle:(CPString)aTitle
|
|
{
|
|
_title = aTitle;
|
|
|
|
[_windowView setTitle:aTitle];
|
|
|
|
[self _synchronizeMenuBarTitleWithWindowTitle];
|
|
}
|
|
|
|
- (void)setTitleWithRepresentedFilename:(CPString)aFilePath
|
|
{
|
|
[self setRepresentedFilename:aFilePath];
|
|
[self setTitle:[aFilePath lastPathComponent]];
|
|
}
|
|
|
|
- (void)setRepresentedFilename:(CPString)aFilePath
|
|
{
|
|
// FIXME: urls vs filepaths and all.
|
|
[self setRepresentedURL:aFilePath];
|
|
}
|
|
|
|
- (CPString)representedFilename
|
|
{
|
|
return _representedURL;
|
|
}
|
|
|
|
- (void)setRepresentedURL:(CPURL)aURL
|
|
{
|
|
_representedURL = aURL;
|
|
}
|
|
|
|
- (CPURL)representedURL
|
|
{
|
|
return _representedURL;
|
|
}
|
|
|
|
// Moving
|
|
|
|
- (void)setMovableByWindowBackground:(BOOL)shouldBeMovableByWindowBackground
|
|
{
|
|
_isMovableByWindowBackground = shouldBeMovableByWindowBackground;
|
|
}
|
|
|
|
- (BOOL)isMovableByWindowBackground
|
|
{
|
|
return _isMovableByWindowBackground;
|
|
}
|
|
|
|
- (void)center
|
|
{
|
|
var size = [self frame].size,
|
|
bridgeSize = [_bridge contentBounds].size;
|
|
|
|
[self setFrameOrigin:CGPointMake((bridgeSize.width - size.width) / 2.0, (bridgeSize.height - size.height) / 2.0)];
|
|
}
|
|
|
|
- (void)sendEvent:(CPEvent)anEvent
|
|
{
|
|
var type = [anEvent type],
|
|
point = [anEvent locationInWindow];
|
|
|
|
switch (type)
|
|
{
|
|
case CPKeyUp: return [[self firstResponder] keyUp:anEvent];
|
|
case CPKeyDown: return [[self firstResponder] keyDown:anEvent];
|
|
|
|
case CPScrollWheel: return [[_windowView hitTest:point] scrollWheel:anEvent];
|
|
|
|
case CPLeftMouseUp: if (!_leftMouseDownView)
|
|
return [[_windowView hitTest:point] mouseUp:anEvent];
|
|
|
|
[_leftMouseDownView mouseUp:anEvent]
|
|
|
|
_leftMouseDownView = nil;
|
|
|
|
return;
|
|
case CPLeftMouseDown: _leftMouseDownView = [_windowView hitTest:point];
|
|
|
|
if (_leftMouseDownView != _firstResponder && [_leftMouseDownView acceptsFirstResponder])
|
|
[self makeFirstResponder:_leftMouseDownView];
|
|
|
|
var theWindow = [anEvent window];
|
|
|
|
if ([theWindow isKeyWindow] || [theWindow becomesKeyOnlyIfNeeded])
|
|
return [_leftMouseDownView mouseDown:anEvent];
|
|
else
|
|
{
|
|
// FIXME: delayed ordering?
|
|
[self makeKeyAndOrderFront:self];
|
|
|
|
if ([_leftMouseDownView acceptsFirstMouse:anEvent])
|
|
return [_leftMouseDownView mouseDown:anEvent]
|
|
}
|
|
break;
|
|
case CPLeftMouseDragged: if (!_leftMouseDownView)
|
|
return [[_windowView hitTest:point] mouseDragged:anEvent];
|
|
|
|
return [_leftMouseDownView mouseDragged:anEvent];
|
|
|
|
case CPRightMouseUp: return [_rightMouseDownView mouseUp:anEvent];
|
|
case CPRightMouseDown: _rightMouseDownView = [_windowView hitTest:point];
|
|
return [_rightMouseDownView mouseDown:anEvent];
|
|
case CPRightMouseDragged: return [_rightMouseDownView mouseDragged:anEvent];
|
|
|
|
case CPMouseMoved: if (!_acceptsMouseMovedEvents)
|
|
return;
|
|
|
|
var hitTestView = [_windowView hitTest:point];
|
|
|
|
if (hitTestView != _mouseOverView)
|
|
{
|
|
if (_mouseOverView)
|
|
[_mouseOverView mouseExited:[CPEvent mouseEventWithType:CPMouseExited location:point
|
|
modifierFlags:[anEvent modifierFlags] timestamp:[anEvent timestamp] windowNumber:_windowNumber context:nil eventNumber:-1 clickCount:1 pressure:0]];
|
|
|
|
if (hitTestView)
|
|
[hitTestView mouseEntered:[CPEvent mouseEventWithType:CPMouseEntered location:point
|
|
modifierFlags:[anEvent modifierFlags] timestamp:[anEvent timestamp] windowNumber:_windowNumber context:nil eventNumber:-1 clickCount:1 pressure:0]];
|
|
|
|
_mouseOverView = hitTestView;
|
|
}
|
|
|
|
[_mouseOverView mouseMoved:anEvent];
|
|
}
|
|
}
|
|
|
|
- (int)windowNumber
|
|
{
|
|
return _windowNumber;
|
|
}
|
|
|
|
- (void)becomeKeyWindow
|
|
{
|
|
if (_firstResponder != self && [_firstResponder respondsToSelector:@selector(becomeKeyWindow)])
|
|
[_firstResponder becomeKeyWindow];
|
|
}
|
|
|
|
- (BOOL)canBecomeKeyWindow
|
|
{
|
|
return YES;
|
|
}
|
|
|
|
- (BOOL)isKeyWindow
|
|
{
|
|
return [CPApp keyWindow] == self;
|
|
}
|
|
|
|
- (void)makeKeyAndOrderFront:(id)aSender
|
|
{
|
|
[self orderFront:self];
|
|
|
|
[self makeKeyWindow];
|
|
[self makeMainWindow];
|
|
}
|
|
|
|
- (void)makeKeyWindow
|
|
{
|
|
if (![self canBecomeKeyWindow])
|
|
return;
|
|
|
|
[CPApp._keyWindow resignKeyWindow];
|
|
|
|
CPApp._keyWindow = self;
|
|
|
|
[self becomeKeyWindow];
|
|
}
|
|
|
|
- (void)resignKeyWindow
|
|
{
|
|
if (_firstResponder != self && [_firstResponder respondsToSelector:@selector(resignKeyWindow)])
|
|
[_firstResponder resignKeyWindow];
|
|
|
|
if ([_delegate respondsToSelector:@selector(windowDidResignKey:)])
|
|
[_delegate windowDidResignKey:self];
|
|
}
|
|
|
|
- (void)dragImage:(CPImage)anImage at:(CGPoint)imageLocation offset:(CGSize)mouseOffset event:(CPEvent)anEvent pasteboard:(CPPasteboard)aPasteboard source:(id)aSourceObject slideBack:(BOOL)slideBack
|
|
{
|
|
[[CPDragServer sharedDragServer] dragImage:anImage fromWindow:self at:[self convertBaseToBridge:imageLocation] offset:mouseOffset event:anEvent pasteboard:aPasteboard source:aSourceObject slideBack:slideBack];
|
|
}
|
|
|
|
- (void)dragView:(CPView)aView at:(CGPoint)imageLocation offset:(CGSize)mouseOffset event:(CPEvent)anEvent pasteboard:(CPPasteboard)aPasteboard source:(id)aSourceObject slideBack:(BOOL)slideBack
|
|
{
|
|
[[CPDragServer sharedDragServer] dragView:aView fromWindow:self at:[self convertBaseToBridge:imageLocation] offset:mouseOffset event:anEvent pasteboard:aPasteboard source:aSourceObject slideBack:slideBack];
|
|
}
|
|
|
|
// Accessing Editing Status
|
|
|
|
- (void)setDocumentEdited:(BOOL)isDocumentEdited
|
|
{
|
|
if (_isDocumentEdited == isDocumentEdited)
|
|
return;
|
|
|
|
_isDocumentEdited = isDocumentEdited;
|
|
|
|
[CPMenu _setMenuBarIconImageAlphaValue:_isDocumentEdited ? 0.5 : 1.0];
|
|
}
|
|
|
|
- (BOOL)isDocumentEdited
|
|
{
|
|
return _isDocumentEdited;
|
|
}
|
|
|
|
- (void)setDocumentSaving:(BOOL)isDocumentSaving
|
|
{
|
|
if (_isDocumentSaving == isDocumentSaving)
|
|
return;
|
|
|
|
_isDocumentSaving = isDocumentSaving;
|
|
|
|
[self _synchronizeSaveMenuWithDocumentSaving];
|
|
|
|
[_windowView windowDidChangeDocumentSaving];
|
|
}
|
|
|
|
- (BOOL)isDocumentSaving
|
|
{
|
|
return _isDocumentSaving;
|
|
}
|
|
|
|
- (void)_synchronizeSaveMenuWithDocumentSaving
|
|
{
|
|
if (![self isMainWindow])
|
|
return;
|
|
|
|
var mainMenu = [CPApp mainMenu],
|
|
index = [mainMenu indexOfItemWithTitle:_isDocumentSaving ? @"Save" : @"Saving..."];
|
|
|
|
if (index == CPNotFound)
|
|
return;
|
|
|
|
var item = [mainMenu itemAtIndex:index];
|
|
|
|
if (_isDocumentSaving)
|
|
{
|
|
CPWindowSaveImage = [item image];
|
|
|
|
[item setTitle:@"Saving..."];
|
|
[item setImage:CPWindowSavingImage];
|
|
[item setEnabled:NO];
|
|
}
|
|
else
|
|
{
|
|
[item setTitle:@"Save"];
|
|
[item setImage:CPWindowSaveImage];
|
|
[item setEnabled:YES];
|
|
}
|
|
}
|
|
|
|
// Closing Windows
|
|
|
|
- (void)performClose:(id)aSender
|
|
{
|
|
if ([_delegate respondsToSelector:@selector(windowShouldClose:)] && ![_delegate windowShouldClose:self] ||
|
|
[self respondsToSelector:@selector(windowShouldClose:)] && ![self windowShouldClose:self])
|
|
return;
|
|
|
|
[self close];
|
|
}
|
|
|
|
- (void)close
|
|
{
|
|
[[CPNotificationCenter defaultCenter] postNotificationName:CPWindowWillCloseNotification object:self];
|
|
|
|
[self orderOut:nil];
|
|
}
|
|
|
|
// Managing Main Status
|
|
|
|
- (BOOL)isMainWindow
|
|
{
|
|
return [CPApp mainWindow] == self;
|
|
}
|
|
|
|
- (BOOL)canBecomeMainWindow
|
|
{
|
|
// FIXME: Also check if we can resize and titlebar.
|
|
if ([self isVisible])
|
|
return YES;
|
|
|
|
return NO;
|
|
}
|
|
|
|
- (void)makeMainWindow
|
|
{
|
|
if (![self canBecomeMainWindow])
|
|
return;
|
|
|
|
[CPApp._mainWindow resignMainWindow];
|
|
|
|
CPApp._mainWindow = self;
|
|
|
|
[self becomeMainWindow];
|
|
}
|
|
|
|
- (void)becomeMainWindow
|
|
{
|
|
[self _synchronizeMenuBarTitleWithWindowTitle];
|
|
[self _synchronizeSaveMenuWithDocumentSaving];
|
|
|
|
[[CPNotificationCenter defaultCenter]
|
|
postNotificationName:CPWindowDidBecomeMainNotification
|
|
object:self];
|
|
}
|
|
|
|
- (void)resignMainWindow
|
|
{
|
|
[[CPNotificationCenter defaultCenter]
|
|
postNotificationName:CPWindowDidResignMainNotification
|
|
object:self];
|
|
}
|
|
|
|
// Managing Toolbars
|
|
|
|
- (CPToolbar)toolbar
|
|
{
|
|
return _toolbar;
|
|
}
|
|
|
|
- (void)setToolbar:(CPView)aToolbar
|
|
{
|
|
if (_toolbar == aToolbar)
|
|
return;
|
|
|
|
// Cleanup old toolbar
|
|
if (_toolbar)
|
|
{
|
|
[self _setToolbarVisible:NO];
|
|
|
|
_toolbar._window = nil;
|
|
_toolbarView = nil;
|
|
}
|
|
|
|
if (_toolbar = aToolbar)
|
|
{
|
|
// Set up new toolbar
|
|
_toolbar = aToolbar;
|
|
_toolbar._window = self;
|
|
|
|
if ([_toolbar isVisible])
|
|
[self _setToolbarVisible:YES];
|
|
|
|
[_toolbar _reloadToolbarItems];
|
|
}
|
|
}
|
|
|
|
- (void)_setToolbarVisible:(BOOL)aFlag
|
|
{
|
|
if (aFlag)
|
|
{
|
|
if (!_toolbarView)
|
|
_toolbarView = [_toolbar _toolbarView];
|
|
|
|
[_toolbarView setFrame:CGRectMake(0.0, 0.0, CGRectGetWidth([_windowView bounds]), CGRectGetHeight([_toolbarView frame]))];
|
|
[_windowView addSubview:_toolbarView];
|
|
}
|
|
else
|
|
[_toolbarView removeFromSuperview];
|
|
|
|
[_contentView setFrame:[self contentRectForFrameRect:[_windowView bounds]]];
|
|
}
|
|
|
|
// Managing Sheets
|
|
|
|
- (void)_setAttachedSheetFrameOrigin
|
|
{
|
|
// Position the sheet above the contentRect.
|
|
var contentRect = [[self contentView] frame],
|
|
sheetFrame = CGRectMakeCopy([_attachedSheet frame]);
|
|
|
|
sheetFrame.origin.y = CGRectGetMinY(_frame) + CGRectGetMinY(contentRect);
|
|
sheetFrame.origin.x = CGRectGetMinX(_frame) + FLOOR((CGRectGetWidth(_frame) - CGRectGetWidth(sheetFrame)) / 2.0);
|
|
|
|
[_attachedSheet setFrameOrigin:sheetFrame.origin];
|
|
}
|
|
|
|
- (void)_animateAttachedSheet
|
|
{
|
|
/* NSWindow *sheet = [sheetContext sheet];
|
|
NSRect sheetFrame;
|
|
|
|
[_sheetContext autorelease];
|
|
_sheetContext=[sheetContext retain];
|
|
|
|
[self _setSheetOrigin];
|
|
sheetFrame = [sheet frame];
|
|
|
|
[(NSWindowBackgroundView *)[sheet _backgroundView] setBorderType:NSButtonBorder];
|
|
[[sheet contentView] setAutoresizesSubviews:NO];
|
|
[[sheet contentView] setAutoresizingMask:NSViewNotSizable];
|
|
|
|
// [(NSWindowBackgroundView *)[sheet _backgroundView] cacheImageForAnimation];
|
|
|
|
[_attachedSheet setFrame:CPRectMake(CGRectGetMinX(sheetFrame), CGRectGetMinY(sheetFrame), CGRectGetWidth(sheetFrame), 0.0) display:YES];
|
|
[sheet setFrame:NSMakeRect(sheetFrame.origin.x, NSMaxY([self frame]), sheetFrame.size.width, 0) display:YES];
|
|
[self _setSheetOriginAndFront];
|
|
|
|
[sheet setFrame:sheetFrame display:YES animate:YES];*/
|
|
}
|
|
|
|
- (void)_attachSheet:(CPWindow)aSheet modalDelegate:(id)aModalDelegate didEndSelector:(SEL)aDidEndSelector contextInfo:(id)aContextInfo
|
|
{
|
|
// Set this as our attached sheet.
|
|
_attachedSheet = aSheet;
|
|
|
|
// If a window is ever run as a sheet, then it's sheet bit is set to YES.
|
|
aSheet._isSheet = YES;
|
|
|
|
[self _setAttachedSheetFrameOrigin];
|
|
|
|
// Place this window above ourselves.
|
|
[_bridge order:CPWindowAbove window:aSheet relativeTo:self];
|
|
}
|
|
|
|
- (CPWindow)attachedSheet
|
|
{
|
|
return _attachedSheet;
|
|
}
|
|
|
|
- (BOOL)isSheet
|
|
{
|
|
return _isSheet;
|
|
}
|
|
|
|
//
|
|
|
|
- (BOOL)becomesKeyOnlyIfNeeded
|
|
{
|
|
return NO;
|
|
}
|
|
|
|
- (BOOL)worksWhenModal
|
|
{
|
|
return NO;
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation CPWindow (MenuBar)
|
|
|
|
- (void)_synchronizeMenuBarTitleWithWindowTitle
|
|
{
|
|
// Windows with Documents automatically update the native window title and the menu bar title.
|
|
if (![_windowController document] || ![self isMainWindow])
|
|
return;
|
|
|
|
[CPMenu setMenuBarTitle:_title];
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation CPWindow (BridgeSupport)
|
|
|
|
- (void)setBridge:(CPDOMWindowBridge)aBridge
|
|
{
|
|
if (_bridge == aBridge)
|
|
return;
|
|
|
|
if (_bridge)
|
|
{
|
|
[self orderOut:self];
|
|
// FIXME: If the bridge changes, then we have to recreate all of our subviews' DOM Elements.
|
|
}
|
|
|
|
_bridge = aBridge;
|
|
|
|
if (_styleMask & CPBorderlessBridgeWindowMask)
|
|
[self setFrame:[aBridge contentBounds]];
|
|
}
|
|
|
|
- (void)resizeWithOldBridgeSize:(CGSize)aSize
|
|
{
|
|
if (_styleMask & CPBorderlessBridgeWindowMask)
|
|
return [self setFrame:[_bridge visibleFrame]];
|
|
|
|
if (_autoresizingMask == CPWindowNotSizable)
|
|
return;
|
|
|
|
var frame = [_bridge contentBounds],
|
|
newFrame = CGRectMakeCopy(_frame),
|
|
dX = (CGRectGetWidth(frame) - aSize.width) /
|
|
(((_autoresizingMask & CPWindowMinXMargin) ? 1 : 0) + (_autoresizingMask & CPWindowWidthSizable ? 1 : 0) + (_autoresizingMask & CPWindowMaxXMargin ? 1 : 0)),
|
|
dY = (CGRectGetHeight(frame) - aSize.height) /
|
|
((_autoresizingMask & CPWindowMinYMargin ? 1 : 0) + (_autoresizingMask & CPWindowHeightSizable ? 1 : 0) + (_autoresizingMask & CPWindowMaxYMargin ? 1 : 0));
|
|
|
|
if (_autoresizingMask & CPWindowMinXMargin)
|
|
newFrame.origin.x += dX;
|
|
if (_autoresizingMask & CPWindowWidthSizable)
|
|
newFrame.size.width += dX;
|
|
|
|
if (_autoresizingMask & CPWindowMinYMargin)
|
|
newFrame.origin.y += dY;
|
|
if (_autoresizingMask & CPWindowHeightSizable)
|
|
newFrame.size.height += dY;
|
|
|
|
[self setFrame:newFrame];
|
|
}
|
|
|
|
- (void)setAutoresizingMask:(unsigned)anAutoresizingMask
|
|
{
|
|
_autoresizingMask = anAutoresizingMask;
|
|
}
|
|
|
|
- (unsigned)autoresizingMask
|
|
{
|
|
return _autoresizingMask;
|
|
}
|
|
|
|
- (CGPoint)convertBaseToBridge:(CGPoint)aPoint
|
|
{
|
|
var origin = [self frame].origin;
|
|
|
|
return CGPointMake(aPoint.x + origin.x, aPoint.y + origin.y);
|
|
}
|
|
|
|
- (CGPoint)convertBridgeToBase:(CGPoint)aPoint
|
|
{
|
|
var origin = [self frame].origin;
|
|
|
|
return CGPointMake(aPoint.x - origin.x, aPoint.y - origin.y);
|
|
}
|
|
|
|
// Undo and Redo Support
|
|
|
|
- (CPUndoManager)undoManager
|
|
{
|
|
if (_delegateRespondsToWindowWillReturnUndoManagerSelector)
|
|
return [_delegate windowWillReturnUndoManager:self];
|
|
|
|
if (!_undoManager)
|
|
_undoManager = [[CPUndoManager alloc] init];
|
|
|
|
return _undoManager;
|
|
}
|
|
|
|
- (void)undo:(id)aSender
|
|
{
|
|
[[self undoManager] undo];
|
|
}
|
|
|
|
- (void)redo:(id)aSender
|
|
{
|
|
[[self undoManager] redo];
|
|
}
|
|
|
|
@end
|
|
|
|
var interpolate = function(fromValue, toValue, progress)
|
|
{
|
|
return fromValue + (toValue - fromValue) * progress;
|
|
}
|
|
|
|
@implementation _CPWindowFrameAnimation : CPAnimation
|
|
{
|
|
CPWindow _window;
|
|
|
|
CGRect _startFrame;
|
|
CGRect _targetFrame;
|
|
}
|
|
|
|
- (id)initWithWindow:(CPWindow)aWindow targetFrame:(CGRect)aTargetFrame
|
|
{
|
|
self = [super initWithDuration:0.2 animationCurve:CPAnimationLinear];
|
|
|
|
if (self)
|
|
{
|
|
_window = aWindow;
|
|
|
|
_targetFrame = CGRectMakeCopy(aTargetFrame);
|
|
_startFrame = CGRectMakeCopy([_window frame]);
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void)startAnimation
|
|
{
|
|
[super startAnimation];
|
|
|
|
_window._isAnimating = YES;
|
|
}
|
|
|
|
- (void)setCurrentProgress:(float)aProgress
|
|
{
|
|
[super setCurrentProgress:aProgress];
|
|
|
|
var value = [self currentValue];
|
|
|
|
if (value == 1.0)
|
|
_window._isAnimating = NO;
|
|
|
|
[_window setFrameOrigin:CGPointMake(interpolate(CGRectGetMinX(_startFrame), CGRectGetMinX(_targetFrame), value), interpolate(CGRectGetMinY(_startFrame), CGRectGetMinY(_targetFrame), value))];
|
|
[_window setFrameSize:CGSizeMake(interpolate(CGRectGetWidth(_startFrame), CGRectGetWidth(_targetFrame), value), interpolate(CGRectGetHeight(_startFrame), CGRectGetHeight(_targetFrame), value))];
|
|
}
|
|
|
|
@end
|
|
|
|
import "CPDragServer.j"
|
|
import "CPDOMWindowBridge.j"
|
|
import "_CPWindowView.j"
|
|
import "CPView.j"
|