mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-09-09 12:17:13 +00:00
394 lines
12 KiB
Plaintext
394 lines
12 KiB
Plaintext
/*
|
|
* _CPAttachedWindow.j
|
|
* AppKit
|
|
*
|
|
* Created by Antoine Mercadal
|
|
* Copyright 2009, Antoine Mercadal
|
|
*
|
|
* 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 "CPButton.j"
|
|
@import "CPColor.j"
|
|
@import "CPImage.j"
|
|
@import "CPImageView.j"
|
|
@import "CPView.j"
|
|
@import "CPWindow.j"
|
|
@import "_CPAttachedWindowView.j"
|
|
|
|
CPClosableOnBlurWindowMask = 1 << 4;
|
|
CPAttachedWhiteWindowMask = 1 << 25;
|
|
CPAttachedBlackWindowMask = 1 << 26;
|
|
|
|
/*! @ingroup appkit
|
|
This is a simple attached window like the one that pops up
|
|
when you double click on a meeting in iCal
|
|
*/
|
|
@implementation _CPAttachedWindow : CPWindow
|
|
{
|
|
id _targetView @accessors(property=targetView);
|
|
BOOL _isClosed;
|
|
BOOL _closeOnBlur;
|
|
|
|
CPButton _closeButton;
|
|
}
|
|
|
|
/*! override default windowView class loader
|
|
@param aStyleMask the window mask
|
|
@return the windowView class
|
|
*/
|
|
|
|
+ (Class)_windowViewClassForStyleMask:(unsigned)aStyleMask
|
|
{
|
|
if (aStyleMask & CPAttachedWhiteWindowMask)
|
|
return _CPAttachedWindowViewWhite;
|
|
|
|
return _CPAttachedWindowViewBlack;
|
|
}
|
|
|
|
|
|
#pragma mark -
|
|
#pragma mark Initialization
|
|
|
|
/*! create and init a CPAttachedWindow with given size of and view
|
|
@param aSize the size of the attached window
|
|
@param aView the target view
|
|
@return ready to use CPAttachedWindow
|
|
*/
|
|
+ (id)attachedWindowWithSize:(CGSize)aSize forView:(CPView)aView
|
|
{
|
|
return [_CPAttachedWindow attachedWindowWithSize:aSize forView:aView styleMask:nil];
|
|
}
|
|
|
|
/*! create and init a CPAttachedWindow with given size of and view
|
|
@param aSize the size of the attached window
|
|
@param aView the target view
|
|
@return ready to use CPAttachedWindow
|
|
@param styleMask the window style mask (combine CPClosableWindowMask, CPAttachedWhiteWindowMask, CPAttachedBlackWindowMask and CPClosableOnBlurWindowMask)
|
|
*/
|
|
+ (id)attachedWindowWithSize:(CGSize)aSize forView:(CPView)aView styleMask:(int)aMask
|
|
{
|
|
var attachedWindow = [[_CPAttachedWindow alloc] initWithContentRect:CPRectMake(0.0, 0.0, aSize.width, aSize.height) styleMask:aMask];
|
|
|
|
[attachedWindow attachToView:aView];
|
|
|
|
return attachedWindow;
|
|
}
|
|
|
|
/*! create and init a CPAttachedWindow with given frame
|
|
@param aFrame the frame of the attached window
|
|
@return ready to use CPAttachedWindow
|
|
*/
|
|
- (id)initWithContentRect:(CGRect)aFrame
|
|
{
|
|
self = [self initWithContentRect:aFrame styleMask:CPAttachedWhiteWindowMask]
|
|
return self;
|
|
}
|
|
|
|
/*! create and init a CPAttachedWindow with given frame
|
|
@param aFrame the frame of the attached window
|
|
@param styleMask the window style mask (combine CPClosableWindowMask, CPAttachedWhiteWindowMask, CPAttachedBlackWindowMask and CPClosableOnBlurWindowMask)
|
|
@return ready to use CPAttachedWindow
|
|
*/
|
|
- (id)initWithContentRect:(CGRect)aFrame styleMask:(unsigned)aStyleMask
|
|
{
|
|
if (self = [super initWithContentRect:aFrame styleMask:aStyleMask])
|
|
{
|
|
_isClosed = NO;
|
|
|
|
var bundle = [CPBundle bundleForClass:[self class]],
|
|
buttonClose = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPAttachedWindow/attached-window-button-close.png"] size:CPSizeMake(15.0, 15.0)],
|
|
buttonClosePressed = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPAttachedWindow/attached-window-button-close-highlighted.png"] size:CPSizeMake(15.0, 15.0)];
|
|
|
|
if (aStyleMask & CPClosableWindowMask)
|
|
{
|
|
_closeButton = [[CPButton alloc] initWithFrame:CPRectMake(8.0, 1.0, 14.0, 14.0)];
|
|
[_closeButton setImageScaling:CPScaleProportionally];
|
|
[_closeButton setBordered:NO];
|
|
[_closeButton setImage:buttonClose]; // this avoid the blinking..
|
|
[_closeButton setValue:buttonClose forThemeAttribute:@"image"];
|
|
[_closeButton setValue:buttonClosePressed forThemeAttribute:@"image" inState:CPThemeStateHighlighted];
|
|
[_closeButton setTarget:self];
|
|
[_closeButton setAction:@selector(close:)];
|
|
[[self contentView] addSubview:_closeButton];
|
|
}
|
|
|
|
_closeOnBlur = (aStyleMask & CPClosableOnBlurWindowMask);
|
|
|
|
[self setLevel:CPStatusWindowLevel];
|
|
[self setMovableByWindowBackground:YES];
|
|
[self setHasShadow:NO];
|
|
|
|
[_windowView setNeedsDisplay:YES];
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark Window actions
|
|
|
|
/*! called when the window is loowing focus and close the window
|
|
if CPClosableOnBlurWindowMask is setted
|
|
*/
|
|
- (void)resignMainWindow
|
|
{
|
|
if (_closeOnBlur && !_isClosed)
|
|
{
|
|
// set a close flag to avoid infinite loop
|
|
_isClosed = YES;
|
|
[self close];
|
|
|
|
if (_delegate && [_delegate respondsToSelector:@selector(didAttachedWindowClose:)])
|
|
[_delegate didAttachedWindowClose:self];
|
|
}
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark Utilities
|
|
|
|
- (CPPoint)computeOrigin:(CPView)aView gravity:(int)gravity
|
|
{
|
|
var frameView = [aView frame],
|
|
currentView = aView,
|
|
origin = [aView frameOrigin],
|
|
nativeRect = [[[CPApp mainWindow] platformWindow] nativeContentRect],
|
|
lastView;
|
|
|
|
// if somebody succeed to use the conversion function of CPView
|
|
// to get this working, please do.
|
|
while (currentView = [currentView superview])
|
|
{
|
|
origin.x += [currentView frameOrigin].x;
|
|
origin.y += [currentView frameOrigin].y;
|
|
lastView = currentView;
|
|
}
|
|
|
|
origin.x += [[lastView window] frame].origin.x;
|
|
origin.y += [[lastView window] frame].origin.y;
|
|
|
|
// take care of the scrolling point
|
|
if ([aView enclosingScrollView])
|
|
{
|
|
var offsetPoint = [[[aView enclosingScrollView] contentView] boundsOrigin];
|
|
origin.x -= offsetPoint.x;
|
|
origin.y -= offsetPoint.y;
|
|
}
|
|
|
|
var originLeft = CPPointCreateCopy(origin),
|
|
originRight = CPPointCreateCopy(origin),
|
|
originTop = CPPointCreateCopy(origin),
|
|
originBottom = CPPointCreateCopy(origin);
|
|
|
|
// CPAttachedWindowGravityRight
|
|
originRight.x += CPRectGetWidth(frameView);
|
|
originRight.y += (CPRectGetHeight(frameView) / 2.0) - (CPRectGetHeight([self frame]) / 2.0);
|
|
|
|
// CPAttachedWindowGravityLeft
|
|
originLeft.x -= CPRectGetWidth([self frame]);
|
|
originLeft.y += (CPRectGetHeight(frameView) / 2.0) - (CPRectGetHeight([self frame]) / 2.0);
|
|
|
|
// CPAttachedWindowGravityBottom
|
|
originBottom.x += CPRectGetWidth(frameView) / 2.0 - CPRectGetWidth([self frame]) / 2.0;
|
|
originBottom.y += CPRectGetHeight(frameView);
|
|
|
|
// CPAttachedWindowGravityTop
|
|
originTop.x += CPRectGetWidth(frameView) / 2.0 - CPRectGetWidth([self frame]) / 2.0;
|
|
originTop.y -= CPRectGetHeight([self frame]);
|
|
|
|
|
|
if (gravity === CPAttachedWindowGravityAuto)
|
|
{
|
|
var frameCopy = CPRectCreateCopy([self frame]);
|
|
|
|
nativeRect.origin.x = 0.0;
|
|
nativeRect.origin.y = 0.0;
|
|
|
|
var tests = [originRight, originLeft, originTop, originBottom];
|
|
|
|
gravity = CPAttachedWindowGravityRight;
|
|
for (var i = 0; i < tests.length; i++)
|
|
{
|
|
frameCopy.origin = tests[i];
|
|
|
|
if (CPRectContainsRect(nativeRect, frameCopy))
|
|
{
|
|
if (CPPointEqualToPoint(tests[i], originRight))
|
|
{
|
|
gravity = CPAttachedWindowGravityRight
|
|
break;
|
|
}
|
|
else if (CPPointEqualToPoint(tests[i], originLeft))
|
|
{
|
|
gravity = CPAttachedWindowGravityLeft
|
|
break;
|
|
}
|
|
else if (CPPointEqualToPoint(tests[i], originTop))
|
|
{
|
|
gravity = CPAttachedWindowGravityUp
|
|
break;
|
|
}
|
|
else if (CPPointEqualToPoint(tests[i], originBottom))
|
|
{
|
|
gravity = CPAttachedWindowGravityDown
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var originToBeReturned;
|
|
switch (gravity)
|
|
{
|
|
case CPAttachedWindowGravityRight:
|
|
originToBeReturned = originRight;
|
|
break;
|
|
case CPAttachedWindowGravityLeft:
|
|
originToBeReturned = originLeft;
|
|
break;
|
|
case CPAttachedWindowGravityDown:
|
|
originToBeReturned = originBottom;
|
|
break;
|
|
case CPAttachedWindowGravityUp:
|
|
originToBeReturned = originTop;
|
|
break;
|
|
}
|
|
|
|
[_windowView setGravity:gravity];
|
|
|
|
var o = originToBeReturned;
|
|
if (o.x < 0)
|
|
{
|
|
[_windowView setGravity:nil];
|
|
o.x = 0;
|
|
}
|
|
if (o.x + CPRectGetWidth([self frame]) > nativeRect.size.width)
|
|
{
|
|
[_windowView setGravity:nil];
|
|
o.x = nativeRect.size.width - CPRectGetWidth([self frame]);
|
|
}
|
|
if (o.y < 0)
|
|
{
|
|
[_windowView setGravity:nil];
|
|
o.y = 0;
|
|
}
|
|
if (o.y + CPRectGetHeight([self frame]) > nativeRect.size.height)
|
|
{
|
|
[_windowView setGravity:nil];
|
|
o.y = nativeRect.size.height - CPRectGetHeight([self frame]);
|
|
}
|
|
|
|
return originToBeReturned;
|
|
}
|
|
|
|
|
|
#pragma mark -
|
|
#pragma mark Notification handlers
|
|
|
|
- (void)_attachedWindowDidMove:(CPNotification)aNotification
|
|
{
|
|
if ([_windowView isMouseDownPressed])
|
|
{
|
|
[_windowView hideCursor];
|
|
[self setLevel:CPNormalWindowLevel];
|
|
[_closeButton setFrameOrigin:CPPointMake(1.0, 1.0)];
|
|
[[CPNotificationCenter defaultCenter] removeObserver:self name:CPWindowDidMoveNotification object:self];
|
|
}
|
|
}
|
|
|
|
|
|
#pragma mark -
|
|
#pragma mark Utilities
|
|
|
|
/*! compute the frame needed to be placed to the given view
|
|
and position the attached window according to this view (gravity will be CPAttachedWindowGravityAuto)
|
|
@param aView the view where CPAttachedWindow must be attached
|
|
*/
|
|
- (void)positionRelativeToView:(CPView)aView
|
|
{
|
|
[self positionRelativeToView:aView gravity:CPAttachedWindowGravityAuto];
|
|
}
|
|
|
|
/*! compute the frame needed to be placed to the given view
|
|
and position the attached window according to this view
|
|
@param aView the view where CPAttachedWindow must be attached
|
|
@param aGravity the gravity to use
|
|
*/
|
|
- (void)positionRelativeToView:(CPView)aView gravity:(int)aGravity
|
|
{
|
|
var frameView = [aView frame],
|
|
posX = frameView.origin.x + CPRectGetWidth(frameView),
|
|
posY = frameView.origin.y + (CPRectGetHeight(frameView) / 2.0) - (CPRectGetHeight([self frame]) / 2.0),
|
|
point = [self computeOrigin:aView gravity:aGravity];
|
|
|
|
point.y = MAX(point.y, 0);
|
|
|
|
[self setFrameOrigin:point];
|
|
[_windowView showCursor];
|
|
[self setLevel:CPStatusWindowLevel];
|
|
[_closeButton setFrameOrigin:CPPointMake(1.0, 1.0)];
|
|
[_windowView setNeedsDisplay:YES];
|
|
[self makeKeyAndOrderFront:nil];
|
|
}
|
|
|
|
/*! set the _targetView and attach the CPAttachedWindow to it
|
|
@param aView the view where CPAttachedWindow must be attached
|
|
*/
|
|
- (void)attachToView:(CPView)aView
|
|
{
|
|
_targetView = aView;
|
|
[self positionRelativeToView:_targetView];
|
|
|
|
[_targetView addObserver:self forKeyPath:@"window.frame" options:nil context:nil];
|
|
}
|
|
|
|
|
|
#pragma mark -
|
|
#pragma mark Actions
|
|
|
|
/*! closes the CPAttachedWindow
|
|
@param sender the sender of the action
|
|
*/
|
|
- (IBAction)close:(id)aSender
|
|
{
|
|
[self close];
|
|
|
|
[_targetView removeObserver:self forKeyPath:@"window.frame"];
|
|
|
|
if (_delegate && [_delegate respondsToSelector:@selector(didAttachedWindowClose:)])
|
|
[_delegate didAttachedWindowClose:self];
|
|
}
|
|
|
|
/*! order front the window as usual and add listener for CPWindowDidMoveNotification
|
|
@param sender the sender of the action
|
|
*/
|
|
- (IBAction)makeKeyAndOrderFront:(is)aSender
|
|
{
|
|
[super makeKeyAndOrderFront:aSender];
|
|
[[CPNotificationCenter defaultCenter] addObserver:self selector:@selector(_attachedWindowDidMove:) name:CPWindowDidMoveNotification object:self];
|
|
}
|
|
|
|
/*! update the CPAttachedWindow frame if a resize event is observed
|
|
|
|
*/
|
|
- (void)observeValueForKeyPath:(CPString)aPath ofObject:(id)anObject change:(CPDictionary)theChange context:(void)aContext
|
|
{
|
|
if ([aPath isEqual:@"window.frame"])
|
|
{
|
|
[self positionRelativeToView:_targetView];
|
|
}
|
|
}
|
|
|
|
@end |