Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4c81c9e21e | ||
|
|
f4912eb23b | ||
|
|
7f85b8b33f | ||
|
|
85c68f78da | ||
|
|
af03085d5f | ||
|
|
b7d747ee17 | ||
|
|
b6f100888a | ||
|
|
3cfd392e3e | ||
|
|
7ed98a8655 | ||
|
|
d78eb4a4e5 | ||
|
|
a3d168b646 | ||
|
|
5018df511b | ||
|
|
09345a9d53 | ||
|
|
8b17cd3cde | ||
|
|
403fcd8fff | ||
|
|
409be4020b | ||
|
|
24801a220c | ||
|
|
19b9f4e1c1 | ||
|
|
48d584df58 | ||
|
|
dd999a29fd | ||
|
|
75fdfb5a0b | ||
|
|
6b4db4e9ae | ||
|
|
2571b24f22 | ||
|
|
855a9eea44 | ||
|
|
189e299592 | ||
|
|
6cbaa35849 | ||
|
|
cb1cfa4d94 | ||
|
|
5bbf893a37 | ||
|
|
3ddbfb5dea | ||
|
|
5352d062d5 | ||
|
|
e15cbfca33 | ||
|
|
51b58f911e | ||
|
|
7acfbb9eed | ||
|
|
4bf9853a25 | ||
|
|
58c6db0a8f | ||
|
|
aed52a7829 | ||
|
|
a1e24a3f95 | ||
|
|
11cff29916 | ||
|
|
f184b9bcc4 | ||
|
|
bab0024e0c | ||
|
|
deed92cff7 | ||
|
|
a9bc949679 | ||
|
|
1411410f4e | ||
|
|
648fbe1b10 | ||
|
|
7042f89620 | ||
|
|
e235fffb5e | ||
|
|
6aadd13450 | ||
|
|
349de098af | ||
|
|
5527e585f6 | ||
|
|
a6e9814dfc | ||
|
|
f01a7d3933 | ||
|
|
d2cd3b899f | ||
|
|
ac2c0d5d84 | ||
|
|
f0d0b512ca | ||
|
|
c859ffd0f7 | ||
|
|
4eb57f3cc5 | ||
|
|
6218bfaf9e | ||
|
|
4d5cdcaeae | ||
|
|
6ad62a6f2f | ||
|
|
37d4b151d7 | ||
|
|
37c1d804f6 | ||
|
|
d200df3e9c | ||
|
|
eb43e416ae | ||
|
|
e6244f71bd | ||
|
|
403277ed06 | ||
|
|
dbe8a59c3c | ||
|
|
a98de54db4 | ||
|
|
7edb7266f2 | ||
|
|
d14e077511 | ||
|
|
211c14e316 | ||
|
|
4fbc140551 | ||
|
|
ee8e65c80d | ||
|
|
1db2e0282a | ||
|
|
2351f73c76 | ||
|
|
20a38c3aac | ||
|
|
bca25d0f5f | ||
|
|
b26853e4d7 | ||
|
|
39049f82fc | ||
|
|
a30250f758 | ||
|
|
629737cda3 | ||
|
|
ec41411675 | ||
|
|
becdf2bfeb | ||
|
|
1e723945f8 | ||
|
|
dfb7599bb6 | ||
|
|
e1d3363ad9 | ||
|
|
c36c750c5d | ||
|
|
c7581c0847 | ||
|
|
936d65291f | ||
|
|
a64030ad93 | ||
|
|
01d60ad4e9 | ||
|
|
06b64c6a87 | ||
|
|
0eff4e2bc0 | ||
|
|
44d6139db2 | ||
|
|
506e3004b7 | ||
|
|
9ce4c7a468 | ||
|
|
2fc724e469 | ||
|
|
afb69a7f03 | ||
|
|
cd2dd9f12d | ||
|
|
29963f74d6 | ||
|
|
565a017ebd | ||
|
|
0b85847524 | ||
|
|
cfc46edfb8 | ||
|
|
f250373673 | ||
|
|
b0acc13da7 | ||
|
|
d705af8965 | ||
|
|
a401dfddff | ||
|
|
f579fd925e | ||
|
|
e6d86eaa9b | ||
|
|
348e45a5cd | ||
|
|
2120e9cf2a | ||
|
|
3002acc895 | ||
|
|
0b3c313280 | ||
|
|
982ab4b4fa | ||
|
|
a0ee2f384b | ||
|
|
56a739655f | ||
|
|
150ee44da0 | ||
|
|
78795e3167 | ||
|
|
b0dd3cd818 | ||
|
|
ef12ec54d7 | ||
|
|
495b774965 | ||
|
|
1381bb5c54 | ||
|
|
f1d4794dd1 | ||
|
|
d58473c270 | ||
|
|
7adf32f36e | ||
|
|
5c3beb4160 | ||
|
|
b82c307007 | ||
|
|
a7010e7598 | ||
|
|
f850cbf780 | ||
|
|
4581fe4f22 | ||
|
|
35df54ae99 | ||
|
|
f8a5f91687 | ||
|
|
4b71557584 | ||
|
|
feda903f2f | ||
|
|
150e264f0d | ||
|
|
b592f6ae92 | ||
|
|
37ba8fb57d | ||
|
|
72f80cdbed | ||
|
|
c070f20669 | ||
|
|
a4d5165a3d | ||
|
|
4fdddfb585 | ||
|
|
0575e96b60 | ||
|
|
d0a8749986 | ||
|
|
44fbf8afe2 | ||
|
|
1eb9202233 | ||
|
|
b7da92d225 | ||
|
|
e4f6d0db1f | ||
|
|
0a8bd9f687 | ||
|
|
f2608998f9 | ||
|
|
ffb08d32ce | ||
|
|
119e776ddc | ||
|
|
652930d5aa | ||
|
|
e3babe97e4 | ||
|
|
72d01865ae | ||
|
|
b9664681a3 | ||
|
|
fd5c545189 | ||
|
|
0639f4b00f | ||
|
|
ab49986238 | ||
|
|
a4fdc50d1d | ||
|
|
ed228b6183 | ||
|
|
e0e675531a | ||
|
|
44e0879224 | ||
|
|
1e6fa93ea2 | ||
|
|
2aaed6bffd | ||
|
|
2d367a8b47 | ||
|
|
52c18abc5e | ||
|
|
595c70ed56 | ||
|
|
9faec8decd | ||
|
|
90dc1f6cf8 | ||
|
|
ba01976151 | ||
|
|
4d7f5a8a09 | ||
|
|
2d01fa6f94 | ||
|
|
970f848b84 | ||
|
|
31feec0465 | ||
|
|
2466c14300 | ||
|
|
49927fcfb7 | ||
|
|
f612683dfb | ||
|
|
11e6ad2153 | ||
|
|
d041c69e0d | ||
|
|
b394052145 | ||
|
|
bce83e45b5 | ||
|
|
77bd8d3a76 | ||
|
|
118ece9f1c | ||
|
|
971956a6ba | ||
|
|
48cd362015 | ||
|
|
b12b1bf662 | ||
|
|
6829d502d7 | ||
|
|
e94ddb4179 |
@@ -1,12 +0,0 @@
|
||||
[submodule "External/narwhal"]
|
||||
path = External/narwhal
|
||||
url = git://github.com/tlrobinson/narwhal.git
|
||||
[submodule "External/jack"]
|
||||
path = External/jack
|
||||
url = git://github.com/tlrobinson/jack.git
|
||||
[submodule "External/browserjs"]
|
||||
path = External/browserjs
|
||||
url = git://github.com/tlrobinson/browserjs.git
|
||||
[submodule "External/ojunit"]
|
||||
path = External/ojunit
|
||||
url = git://github.com/280north/ojunit.git
|
||||
@@ -43,6 +43,7 @@
|
||||
@import "CPCompatibility.j"
|
||||
@import "CPControl.j"
|
||||
@import "CPCookie.j"
|
||||
@import "CPCursor.j"
|
||||
@import "CPDocument.j"
|
||||
@import "CPDocumentController.j"
|
||||
@import "CPEvent.j"
|
||||
|
||||
@@ -197,6 +197,20 @@
|
||||
return indexSet;
|
||||
}
|
||||
|
||||
- (void)setEnabled:(BOOL)isEnabled forItemAtIndex:(CPInteger)anIndex
|
||||
{
|
||||
var itemView = _itemViews[anIndex];
|
||||
if (!itemView)
|
||||
return;
|
||||
|
||||
if (!isEnabled)
|
||||
[self collapseItemAtIndex:anIndex];
|
||||
else
|
||||
[self expandItemAtIndex:anIndex];
|
||||
|
||||
[itemView setEnabled:isEnabled];
|
||||
}
|
||||
|
||||
- (void)_invalidateItemsStartingAtIndex:(CPInteger)anIndex
|
||||
{
|
||||
if (_dirtyItemIndex === CPNotFound)
|
||||
@@ -301,6 +315,12 @@
|
||||
[_headerView setStringValue:aLabel];
|
||||
}
|
||||
|
||||
- (void)setEnabled:(BOOL)isEnabled
|
||||
{
|
||||
if ([_headerView respondsToSelector:@selector(setEnabled:)])
|
||||
[_headerView setEnabled:isEnabled];
|
||||
}
|
||||
|
||||
- (void)setContentView:(CPView)aView
|
||||
{
|
||||
if (_contentView === aView)
|
||||
@@ -312,7 +332,7 @@
|
||||
|
||||
_contentView = aView;
|
||||
|
||||
[_contentView addObserver:self forKeyPath:@"frame" options:0 context:NULL];
|
||||
[_contentView addObserver:self forKeyPath:@"frame" options:CPKeyValueObservingOptionOld | CPKeyValueObservingOptionNew context:NULL];
|
||||
|
||||
[self addSubview:_contentView];
|
||||
|
||||
|
||||
@@ -148,8 +148,8 @@ var CPAlertWarningImage,
|
||||
var button = _buttons[i];
|
||||
|
||||
[button setFrameSize:CGSizeMake([button frame].size.width, (styleMask == CPHUDBackgroundWindowMask) ? 20.0 : 24.0)];
|
||||
[button setBezelStyle:(styleMask & CPHUDBackgroundWindowMask) ? CPHUDBezelStyle : CPRoundedBezelStyle];
|
||||
|
||||
[button setTheme:[CPTheme themeNamed: _windowStyle === CPHUDBackgroundWindowMask ? "Aristo-HUD" : "Aristo"]];
|
||||
|
||||
[[_alertPanel contentView] addSubview:button];
|
||||
}
|
||||
|
||||
@@ -161,6 +161,7 @@ var CPAlertWarningImage,
|
||||
[_messageLabel setFont:[CPFont boldSystemFontOfSize:13.0]];
|
||||
[_messageLabel setLineBreakMode:CPLineBreakByWordWrapping];
|
||||
[_messageLabel setAlignment:CPJustifiedTextAlignment];
|
||||
[_messageLabel setAutoresizingMask:CPViewWidthSizable|CPViewHeightSizable];
|
||||
|
||||
_alertImageView = [[CPImageView alloc] initWithFrame:CGRectMake(15.0, 12.0, 32.0, 32.0)];
|
||||
}
|
||||
@@ -262,7 +263,9 @@ var CPAlertWarningImage,
|
||||
[button setTag:_buttonCount];
|
||||
[button setAction:@selector(_notifyDelegate:)];
|
||||
|
||||
[button setBezelStyle:(_windowStyle == CPHUDBackgroundWindowMask) ? CPHUDBezelStyle : CPRoundRectBezelStyle];
|
||||
[button setTheme:[CPTheme themeNamed: _windowStyle === CPHUDBackgroundWindowMask ? "Aristo-HUD" : "Aristo"]];
|
||||
[button setAutoresizingMask:CPViewMinXMargin|CPViewMinYMargin];
|
||||
|
||||
[[_alertPanel contentView] addSubview:button];
|
||||
|
||||
if (_buttonCount == 0)
|
||||
|
||||
@@ -96,6 +96,10 @@ CPRunContinuesResponse = -1002;
|
||||
CPDictionary _namedArgs;
|
||||
CPArray _args;
|
||||
CPString _fullArgsString;
|
||||
|
||||
CPImage _applicationIconImage;
|
||||
|
||||
CPPanel _aboutPanel;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -250,6 +254,14 @@ CPRunContinuesResponse = -1002;
|
||||
*/
|
||||
- (void)finishLaunching
|
||||
{
|
||||
// At this point we clear the window.status to eliminate Safari's "Cancelled" error message
|
||||
// The message shouldn't be displayed, because only an XHR is cancelled, but it is a usability issue.
|
||||
// We do it here so that applications can change it in willFinish or didFinishLaunching
|
||||
window.status = " ";
|
||||
|
||||
// We also want to set the default cursor on the body, so that buttons and things don't have an iBeam
|
||||
[[CPCursor arrowCursor] set];
|
||||
|
||||
var bundle = [CPBundle mainBundle],
|
||||
types = [bundle objectForInfoDictionaryKey:@"CPBundleDocumentTypes"];
|
||||
|
||||
@@ -275,15 +287,15 @@ CPRunContinuesResponse = -1002;
|
||||
postNotificationName:CPApplicationWillFinishLaunchingNotification
|
||||
object:self];
|
||||
|
||||
var filename = window.cpOpeningFilename && window.cpOpeningFilename(),
|
||||
needsUntitled = !!_documentController;
|
||||
var needsUntitled = !!_documentController,
|
||||
URLStrings = window.cpOpeningURLStrings && window.cpOpeningURLStrings(),
|
||||
index = 0,
|
||||
count = [URLStrings count];
|
||||
|
||||
if ([filename length])
|
||||
{
|
||||
needsUntitled = ![self _openFile:filename];
|
||||
}
|
||||
for (; index < count; ++index)
|
||||
needsUntitled = ![self _openURL:[CPURL URLWithString:URLStrings[index]]] || needsUntitled;
|
||||
|
||||
if (needsUntitled && [_delegate respondsToSelector: @selector(applicationShouldOpenUntitledFile:)])
|
||||
if (needsUntitled && [_delegate respondsToSelector:@selector(applicationShouldOpenUntitledFile:)])
|
||||
needsUntitled = [_delegate applicationShouldOpenUntitledFile:self];
|
||||
|
||||
if (needsUntitled)
|
||||
@@ -302,11 +314,88 @@ CPRunContinuesResponse = -1002;
|
||||
|
||||
- (void)terminate:(id)aSender
|
||||
{
|
||||
[[CPDocumentController sharedDocumentController] closeAllDocumentsWithDelegate:self
|
||||
didCloseAllSelector:@selector(_documentController:didCloseAll:context:)
|
||||
contextInfo:nil];
|
||||
if (![CPPlatform isBrowser])
|
||||
{
|
||||
[[CPDocumentController sharedDocumentController] closeAllDocumentsWithDelegate:self
|
||||
didCloseAllSelector:@selector(_documentController:didCloseAll:context:)
|
||||
contextInfo:nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
[[[self keyWindow] platformWindow] _propagateCurrentDOMEvent:YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setApplicationIconImage:(CPImage)anImage
|
||||
{
|
||||
_applicationIconImage = anImage;
|
||||
}
|
||||
|
||||
- (CPImage)applicationIconImage
|
||||
{
|
||||
if (_applicationIconImage)
|
||||
return _applicationIconImage;
|
||||
|
||||
var imagePath = [[CPBundle mainBundle] objectForInfoDictionaryKey:@"CPApplicationIcon"];
|
||||
if (imagePath)
|
||||
_applicationIconImage = [[CPImage alloc] initWithContentsOfFile:imagePath];
|
||||
|
||||
return _applicationIconImage;
|
||||
}
|
||||
|
||||
- (void)orderFrontStandardAboutPanel:(id)sender
|
||||
{
|
||||
[self orderFrontStandardAboutPanelWithOptions:nil];
|
||||
}
|
||||
|
||||
- (void)orderFrontStandardAboutPanelWithOptions:(CPDictionary)options
|
||||
{
|
||||
if (!_aboutPanel)
|
||||
{
|
||||
var mainInfo = [[CPBundle mainBundle] infoDictionary],
|
||||
applicationTitle = [options objectForKey:"ApplicationName"] || [mainInfo objectForKey:@"CPBundleName"],
|
||||
applicationIcon = [options objectForKey:@"ApplicationIcon"] || [self applicationIconImage],
|
||||
version = [options objectForKey:@"Version"] || [mainInfo objectForKey:@"CPBundleVersion"],
|
||||
applicationVersion = [options objectForKey:@"ApplicationVersion"] || [mainInfo objectForKey:@"CPBundleShortVersionString"],
|
||||
copyright = [options objectForKey:@"Copyright"] || [mainInfo objectForKey:@"CPHumanReadableCopyright"];
|
||||
|
||||
var aboutPanelController = [[CPWindowController alloc] initWithWindowCibName:@"AboutPanel"],
|
||||
aboutPanel = [aboutPanelController window],
|
||||
contentView = [aboutPanel contentView],
|
||||
imageView = [contentView viewWithTag:1],
|
||||
applicationLabel = [contentView viewWithTag:2],
|
||||
versionLabel = [contentView viewWithTag:3],
|
||||
copyrightLabel = [contentView viewWithTag:4],
|
||||
standardPath = [[CPBundle bundleForClass:[self class]] pathForResource:@"standardApplicationIcon.png"];
|
||||
|
||||
// FIXME move this into the CIB eventually
|
||||
[applicationLabel setFont:[CPFont boldSystemFontOfSize:14.0]];
|
||||
[applicationLabel setAlignment:CPCenterTextAlignment];
|
||||
[versionLabel setAlignment:CPCenterTextAlignment];
|
||||
[copyrightLabel setAlignment:CPCenterTextAlignment];
|
||||
|
||||
[imageView setImage:applicationIcon || [[CPImage alloc] initWithContentsOfFile:standardPath
|
||||
size:CGSizeMake(256, 256)]];
|
||||
|
||||
[applicationLabel setStringValue:applicationTitle || ""];
|
||||
|
||||
if (version && applicationVersion)
|
||||
[versionLabel setStringValue:sprintf(@"Version %@ (%@)", applicationVersion, version)];
|
||||
else if (applicationVersion || version)
|
||||
[versionLabel setStringValue:sprintf(@"Version %@", applicationVersion || version)];
|
||||
else
|
||||
[versionLabel setStringValue:@""];
|
||||
|
||||
[copyrightLabel setStringValue:copyright || ""];
|
||||
[aboutPanel center];
|
||||
|
||||
_aboutPanel = aboutPanel;
|
||||
}
|
||||
|
||||
[_aboutPanel orderFront:self];
|
||||
}
|
||||
|
||||
|
||||
- (void)_documentController:(NSDocumentController *)docController didCloseAll:(BOOL)didCloseAll context:(Object)info
|
||||
{
|
||||
// callback method for terminate:
|
||||
@@ -333,6 +422,11 @@ CPRunContinuesResponse = -1002;
|
||||
[CPPlatform activateIgnoringOtherApps:shouldIgnoreOtherApps];
|
||||
}
|
||||
|
||||
- (void)hideOtherApplications:(id)aSender
|
||||
{
|
||||
[CPPlatform hideOtherApplications:self];
|
||||
}
|
||||
|
||||
/*!
|
||||
Calls \c -finishLaunching method which results in starting
|
||||
the main event loop.
|
||||
@@ -457,13 +551,28 @@ CPRunContinuesResponse = -1002;
|
||||
{
|
||||
_currentEvent = anEvent;
|
||||
|
||||
var willPropagate = [[[anEvent window] platformWindow] _willPropagateCurrentDOMEvent];
|
||||
|
||||
// temporarily pretend we won't propagate the event. we'll restore the saved value later
|
||||
// we do this outside the if so that changes user code might make in _handleKeyEquiv. are preserved
|
||||
[[[anEvent window] platformWindow] _propagateCurrentDOMEvent:NO];
|
||||
|
||||
// Check if this is a candidate for key equivalent...
|
||||
if ([anEvent _couldBeKeyEquivalent] && [self _handleKeyEquivalent:anEvent])
|
||||
{
|
||||
[[[anEvent window] platformWindow] _propagateCurrentDOMEvent:NO];
|
||||
var characters = [anEvent characters],
|
||||
modifierFlags = [anEvent modifierFlags];
|
||||
|
||||
// Unconditionally propagate on these keys to solve browser copy paste bugs
|
||||
if ((characters == "c" || characters == "x" || characters == "v") && (modifierFlags & CPPlatformActionKeyMask))
|
||||
[[[anEvent window] platformWindow] _propagateCurrentDOMEvent:YES];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// if we make it this far, then restore the original willPropagate value
|
||||
[[[anEvent window] platformWindow] _propagateCurrentDOMEvent:willPropagate];
|
||||
|
||||
if (_eventListeners.length)
|
||||
{
|
||||
if (_eventListeners[_eventListeners.length - 1]._mask & (1 << [anEvent type]))
|
||||
@@ -515,6 +624,11 @@ CPRunContinuesResponse = -1002;
|
||||
return _windows;
|
||||
}
|
||||
|
||||
- (void)hide:(id)aSender
|
||||
{
|
||||
[CPPlatform hide:self];
|
||||
}
|
||||
|
||||
// Accessing the Main Menu
|
||||
/*!
|
||||
Returns the application's main menu
|
||||
@@ -549,16 +663,6 @@ CPRunContinuesResponse = -1002;
|
||||
[[CPColorPanel sharedColorPanel] orderFront:self];
|
||||
}
|
||||
|
||||
- (void)orderFrontStandardAboutPanel:(id)aSender
|
||||
{
|
||||
[self orderFrontStandardAboutPanelWithOptions:nil];
|
||||
}
|
||||
|
||||
- (void)orderFrontStandardAboutPanelWithOptions:(CPDictionary)aDictionary
|
||||
{
|
||||
// FIXME: Implement.
|
||||
}
|
||||
|
||||
// Posting Actions
|
||||
/*!
|
||||
Tries to perform the action with an argument. Performs
|
||||
@@ -597,7 +701,7 @@ CPRunContinuesResponse = -1002;
|
||||
- (BOOL)sendAction:(SEL)anAction to:(id)aTarget from:(id)aSender
|
||||
{
|
||||
var target = [self targetForAction:anAction to:aTarget from:aSender];
|
||||
|
||||
|
||||
if (!target)
|
||||
return NO;
|
||||
|
||||
@@ -834,12 +938,18 @@ CPRunContinuesResponse = -1002;
|
||||
return _namedArgs;
|
||||
}
|
||||
|
||||
- (BOOL)_openFile:(CPString)aFilename
|
||||
- (BOOL)_openURL:(CPURL)aURL
|
||||
{
|
||||
if (_delegate && [_delegate respondsToSelector:@selector(application:openFile:)])
|
||||
return [_delegate application:self openFile:aFilename];
|
||||
else
|
||||
return [_documentController openDocumentWithContentsOfURL:aFilename display:YES error:NULL];
|
||||
{
|
||||
CPLog.warn("application:openFile: is deprecated, use application:openURL: instead.");
|
||||
return [_delegate application:self openFile:[aURL absoluteString]];
|
||||
}
|
||||
|
||||
if (_delegate && [_delegate respondsToSelector:@selector(application:openURL:)])
|
||||
return [_delegate application:self openURL:aURL];
|
||||
|
||||
return !![_documentController openDocumentWithContentsOfURL:aURL display:YES error:NULL];
|
||||
}
|
||||
|
||||
- (void)_didResignActive
|
||||
@@ -988,4 +1098,9 @@ var _CPAppBootstrapperActions = nil;
|
||||
[self performActions];
|
||||
}
|
||||
|
||||
+ (void)reset
|
||||
{
|
||||
_CPAppBootstrapperActions = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -192,8 +192,8 @@ CPButtonStateMixed = CPThemeState("mixed");
|
||||
|
||||
_allowsMixedState = aFlag;
|
||||
|
||||
if (!_allowsMixedState)
|
||||
[self unsetThemeState:CPButtonStateMixed];
|
||||
if (!_allowsMixedState && [self state] === CPMixedState)
|
||||
[self setState:CPOnState];
|
||||
}
|
||||
|
||||
- (void)setObjectValue:(id)anObjectValue
|
||||
|
||||
@@ -321,7 +321,7 @@
|
||||
*/
|
||||
- (CPIndexSet)selectionIndexes
|
||||
{
|
||||
return _selectionIndexes;
|
||||
return [_selectionIndexes copy];
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
|
||||
@@ -140,7 +140,7 @@ CPColorPickerViewHeight = 370;
|
||||
Sets the color of the panel, and updates the picker. Also posts a \c CPColorPanelDidChangeNotification.
|
||||
*/
|
||||
- (void)setColor:(CPColor)aColor
|
||||
{
|
||||
{
|
||||
_color = aColor;
|
||||
[_previewView setBackgroundColor: _color];
|
||||
|
||||
@@ -518,9 +518,10 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
||||
|
||||
- (void)mouseUp:(CPEvent)anEvent
|
||||
{
|
||||
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil],
|
||||
bounds = [self bounds];
|
||||
|
||||
if(point.x > [self bounds].size.width - 1 || point.x < 1)
|
||||
if(!CGRectContainsPoint(bounds, point) || point.x > [self bounds].size.width - 1 || point.x < 1)
|
||||
return NO;
|
||||
|
||||
[_colorPanel setColor: [self colorAtIndex:FLOOR(point.x / 13)] updatePicker: YES];
|
||||
@@ -563,7 +564,7 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
||||
- (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType
|
||||
{
|
||||
if(aType == CPColorDragType)
|
||||
[aPasteboard setData:_dragColor forType:aType];
|
||||
[aPasteboard setData:[CPKeyedArchiver archivedDataWithRootObject:_dragColor] forType:aType];
|
||||
}
|
||||
|
||||
- (void)performDragOperation:(id <CPDraggingInfo>)aSender
|
||||
@@ -575,7 +576,7 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
||||
if(![pasteboard availableTypeFromArray:[CPColorDragType]] || location.x > [self bounds].size.width - 1 || location.x < 1)
|
||||
return NO;
|
||||
|
||||
[self setColor:[pasteboard dataForType:CPColorDragType] atIndex: FLOOR(location.x / 13)];
|
||||
[self setColor:[CPKeyedUnarchiver unarchiveObjectWithData:[pasteboard dataForType:CPColorDragType]] atIndex: FLOOR(location.x / 13)];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -612,8 +613,8 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
||||
if(![pasteboard availableTypeFromArray:[CPColorDragType]])
|
||||
return NO;
|
||||
|
||||
var color = [pasteboard dataForType:CPColorDragType];
|
||||
[_colorPanel setColor: color updatePicker: YES];
|
||||
var color = [CPKeyedUnarchiver unarchiveObjectWithData:[pasteboard dataForType:CPColorDragType]];
|
||||
[_colorPanel setColor:color updatePicker:YES];
|
||||
}
|
||||
|
||||
- (BOOL)isOpaque
|
||||
@@ -625,7 +626,7 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
||||
{
|
||||
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||
|
||||
[[CPPasteboard pasteboardWithName:CPDragPboard] declareTypes:[CPArray arrayWithObject:CPColorDragType] owner:self];
|
||||
[[CPPasteboard pasteboardWithName:CPDragPboard] declareTypes:[CPColorDragType] owner:self];
|
||||
|
||||
var bounds = CPRectMake(0, 0, 15, 15);
|
||||
|
||||
@@ -650,7 +651,7 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
||||
- (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType
|
||||
{
|
||||
if(aType == CPColorDragType)
|
||||
[aPasteboard setData:[self backgroundColor] forType:aType];
|
||||
[aPasteboard setData:[CPKeyedArchiver archivedDataWithRootObject:[self backgroundColor]] forType:aType];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -31,6 +31,11 @@ CPKHTMLBrowserEngine = 3;
|
||||
CPOperaBrowserEngine = 4;
|
||||
CPWebKitBrowserEngine = 5;
|
||||
|
||||
// Operating Systems
|
||||
CPMacOperatingSystem = 0;
|
||||
CPWindowsOperatingSystem = 1;
|
||||
CPOtherOperatingSystem = 2;
|
||||
|
||||
// Features
|
||||
CPCSSRGBAFeature = 1 << 5;
|
||||
|
||||
@@ -183,11 +188,20 @@ function CPFeatureIsCompatible(aFeature)
|
||||
|
||||
function CPBrowserIsEngine(anEngine)
|
||||
{
|
||||
return PLATFORM_ENGINE == anEngine;
|
||||
return PLATFORM_ENGINE === anEngine;
|
||||
}
|
||||
|
||||
if (USER_AGENT.indexOf("Mac") != -1)
|
||||
function CPBrowserIsOperatingSystem(anOperatingSystem)
|
||||
{
|
||||
return OPERATING_SYSTEM === anOperatingSystem;
|
||||
}
|
||||
|
||||
OPERATING_SYSTEM = CPOtherOperatingSystem;
|
||||
|
||||
if (USER_AGENT.indexOf("Mac") !== -1)
|
||||
{
|
||||
OPERATING_SYSTEM = CPMacOperatingSystem;
|
||||
|
||||
CPPlatformActionKeyMask = CPCommandKeyMask;
|
||||
|
||||
CPUndoKeyEquivalent = @"Z";
|
||||
@@ -198,6 +212,9 @@ if (USER_AGENT.indexOf("Mac") != -1)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (USER_AGENT.indexOf("Windows") !== -1)
|
||||
OPERATING_SYSTEM = CPWindowsOperatingSystem;
|
||||
|
||||
CPPlatformActionKeyMask = CPControlKeyMask;
|
||||
|
||||
CPUndoKeyEquivalent = @"Z";
|
||||
|
||||
@@ -572,7 +572,9 @@ var CPControlValueKey = "CPControlValueKey",
|
||||
|
||||
CPControlTargetKey = "CPControlTargetKey",
|
||||
CPControlActionKey = "CPControlActionKey",
|
||||
CPControlSendActionOnKey = "CPControlSendActionOnKey";
|
||||
CPControlSendActionOnKey = "CPControlSendActionOnKey",
|
||||
|
||||
CPControlSendsActionOnEndEditingKey = "CPControlSendsActionOnEndEditingKey";
|
||||
|
||||
var __Deprecated__CPImageViewImageKey = @"CPImageViewImageKey";
|
||||
|
||||
@@ -595,6 +597,7 @@ var __Deprecated__CPImageViewImageKey = @"CPImageViewImageKey";
|
||||
[self setAction:[aCoder decodeObjectForKey:CPControlActionKey]];
|
||||
|
||||
[self sendActionOn:[aCoder decodeIntForKey:CPControlSendActionOnKey]];
|
||||
[self setSendsActionOnEndEditing:[aCoder decodeBoolForKey:CPControlSendsActionOnEndEditingKey]];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -608,6 +611,9 @@ var __Deprecated__CPImageViewImageKey = @"CPImageViewImageKey";
|
||||
{
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
if (_sendsActionOnEndEditing)
|
||||
[aCoder encodeBool:_sendsActionOnEndEditing forKey:CPControlSendsActionOnEndEditingKey];
|
||||
|
||||
if (_value !== nil)
|
||||
[aCoder encodeObject:_value forKey:CPControlValueKey];
|
||||
|
||||
|
||||
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
Testing browsers:
|
||||
WebKit r50235 (Safari 4.0.3+)
|
||||
FireFox 3.5
|
||||
Opera 10.0.0
|
||||
|
||||
Implemented class methods:
|
||||
Webkit : All
|
||||
IE : All
|
||||
FireFox Win : All
|
||||
FireFox Mac : All except closedHandCursor disappearingItemCursor // Firefox mac does not support url cursors
|
||||
Opera : All except resizeLeftRightCursor resizeUpDownCursor operationNotAllowedCursor dragCopyCursor dragLinkCursor contextualMenuCursor openHandCursor closedHandCursor disappearingItemCursor // Opera does not support url cursors so these won't work with images
|
||||
*/
|
||||
|
||||
var currentCursor = nil,
|
||||
cursorStack = [],
|
||||
cursors = {},
|
||||
cursorURLFormat = nil;
|
||||
|
||||
@implementation CPCursor : CPObject
|
||||
{
|
||||
CPString _cssString;
|
||||
BOOL _isSetOnMouseEntered @accessors(readwrite, getter=isSetOnMouseEntered, setter=setOnMouseEntered:);
|
||||
BOOL _isSetOnMouseExited @accessors(readwrite, getter=isSetOnMouseExited, setter=setOnMouseExited:);
|
||||
}
|
||||
|
||||
+ (CPCursor)currentCursor
|
||||
{
|
||||
return currentCursor;
|
||||
}
|
||||
|
||||
- (id)initWithCSSString:(CPString)aString
|
||||
{
|
||||
if (self = [super init])
|
||||
_cssString = aString;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (CPCursor)cursorWithCSSString:(CPString)cssString
|
||||
{
|
||||
var cursor = cursors[cssString];
|
||||
|
||||
if (typeof cursor == 'undefined')
|
||||
{
|
||||
cursor = [[CPCursor alloc] initWithCSSString:cssString];
|
||||
cursors[cssString] = cursor;
|
||||
}
|
||||
|
||||
return cursor;
|
||||
}
|
||||
|
||||
+ (CPCursor)cursorWithImageNamed:(CPString)imageName
|
||||
{
|
||||
if (!cursorURLFormat)
|
||||
{
|
||||
cursorURLFormat = @"url(" + [[CPBundle bundleForClass:self] resourcePath] + @"/CPCursor/%@.cur)";
|
||||
|
||||
if (CPBrowserIsEngine(CPGeckoBrowserEngine))
|
||||
cursorURLFormat += ", default";
|
||||
}
|
||||
|
||||
var url = [CPString stringWithFormat:cursorURLFormat, imageName];
|
||||
|
||||
return [[CPCursor alloc] initWithCSSString:url];
|
||||
}
|
||||
|
||||
- (CPString)_cssString
|
||||
{
|
||||
return _cssString;
|
||||
}
|
||||
|
||||
+ (CPCursor)arrowCursor
|
||||
{
|
||||
return [CPCursor cursorWithCSSString:@"default"]; // WebKit | FF | opera | IE
|
||||
}
|
||||
|
||||
+ (CPCursor)crosshairCursor
|
||||
{
|
||||
return [CPCursor cursorWithCSSString:@"crosshair"]; // WebKit | FF | opera | IE
|
||||
}
|
||||
|
||||
+ (CPCursor)IBeamCursor
|
||||
{
|
||||
return [CPCursor cursorWithCSSString:@"text"]; // WebKit | FF | opera | IE
|
||||
}
|
||||
|
||||
+ (CPCursor)pointingHandCursor
|
||||
{
|
||||
return [CPCursor cursorWithCSSString:@"pointer"]; // WebKit | FF | opera | IE
|
||||
}
|
||||
|
||||
+ (CPCursor)resizeDownCursor
|
||||
{
|
||||
if (CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
||||
return [CPCursor cursorWithImageNamed:CPStringFromSelector(_cmd)];
|
||||
|
||||
return [CPCursor cursorWithCSSString:"s-resize"]; // WebKit | FF | opera
|
||||
}
|
||||
|
||||
+ (CPCursor)resizeUpCursor
|
||||
{
|
||||
if (CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
||||
return [CPCursor cursorWithImageNamed:CPStringFromSelector(_cmd)];
|
||||
|
||||
return [CPCursor cursorWithCSSString:@"n-resize"]; // WebKit | FF | opera
|
||||
}
|
||||
|
||||
+ (CPCursor)resizeLeftCursor
|
||||
{
|
||||
if (CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
||||
return [CPCursor cursorWithImageNamed:CPStringFromSelector(_cmd)];
|
||||
|
||||
return [CPCursor cursorWithCSSString:@"w-resize"]; // WebKit | FF | opera
|
||||
}
|
||||
|
||||
+ (CPCursor)resizeRightCursor
|
||||
{
|
||||
if (CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
||||
return [CPCursor cursorWithImageNamed:CPStringFromSelector(_cmd)];
|
||||
|
||||
return [CPCursor cursorWithCSSString:@"e-resize"]; // WebKit | FF | opera
|
||||
}
|
||||
|
||||
+ (CPCursor)resizeLeftRightCursor
|
||||
{
|
||||
return [CPCursor cursorWithCSSString:@"col-resize"]; // WebKit | FF | IE
|
||||
}
|
||||
|
||||
+ (CPCursor)resizeUpDownCursor
|
||||
{
|
||||
return [CPCursor cursorWithCSSString:@"row-resize"]; // WebKit | FF | IE
|
||||
}
|
||||
|
||||
+ (CPCursor)operationNotAllowedCursor
|
||||
{
|
||||
return [CPCursor cursorWithCSSString:@"not-allowed"]; // WebKit | FF | IE
|
||||
}
|
||||
|
||||
+ (CPCursor)dragCopyCursor
|
||||
{
|
||||
if (CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
||||
return [CPCursor cursorWithImageNamed:CPStringFromSelector(_cmd)];
|
||||
|
||||
return [CPCursor cursorWithCSSString:@"copy"]; // WebKit | FF
|
||||
}
|
||||
|
||||
+ (CPCursor)dragLinkCursor
|
||||
{
|
||||
if (CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
||||
return [CPCursor cursorWithImageNamed:CPStringFromSelector(_cmd)];
|
||||
|
||||
return [CPCursor cursorWithCSSString:@"alias"]; // WebKit | FF
|
||||
}
|
||||
|
||||
+ (CPCursor)contextualMenuCursor
|
||||
{
|
||||
if (CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
||||
return [CPCursor cursorWithImageNamed:CPStringFromSelector(_cmd)];
|
||||
|
||||
return [CPCursor cursorWithCSSString:@"context-menu"]; // WebKit | FF Mac . Not impl FF Win.
|
||||
}
|
||||
|
||||
+ (CPCursor)openHandCursor
|
||||
{
|
||||
if (CPBrowserIsEngine(CPWebKitBrowserEngine))
|
||||
return [CPCursor cursorWithCSSString:@"-webkit-grab"];
|
||||
else if (CPBrowserIsEngine(CPGeckoBrowserEngine) || CPBrowserIsEngine(CPOperaBrowserEngine))
|
||||
return [CPCursor cursorWithCSSString:@"move"];
|
||||
|
||||
return [CPCursor cursorWithImageNamed:CPStringFromSelector(_cmd)]; // WebKit only. move in FFMac|Opera
|
||||
}
|
||||
|
||||
+ (CPCursor)closedHandCursor
|
||||
{
|
||||
if (CPBrowserIsEngine(CPWebKitBrowserEngine))
|
||||
return [CPCursor cursorWithCSSString:@"-webkit-grabbing"];
|
||||
|
||||
return [CPCursor cursorWithImageNamed:CPStringFromSelector(_cmd)]; // WebKit only
|
||||
}
|
||||
|
||||
+ (CPCursor)disappearingItemCursor
|
||||
{
|
||||
return [CPCursor cursorWithImageNamed:CPStringFromSelector(_cmd)]; // None
|
||||
}
|
||||
|
||||
+ (void)hide
|
||||
{
|
||||
document.body.style.cursor = 'none'; // Not supported in IE
|
||||
}
|
||||
|
||||
+ (void)unhide
|
||||
{
|
||||
document.body.style.cursor = [currentCursor _cssString];
|
||||
}
|
||||
|
||||
+ (void)setHiddenUntilMouseMoves:(BOOL)flag
|
||||
{
|
||||
if (flag)
|
||||
[CPCursor hide];
|
||||
else
|
||||
[CPCursor unhide];
|
||||
}
|
||||
|
||||
- (id)initWithImage:(CPImage)image hotSpot:(CPPoint)hotSpot
|
||||
{
|
||||
return [self initWithCSSString:"url(" + [image filename] + ")"];
|
||||
}
|
||||
|
||||
- (void)mouseEntered:(CPEvent)event
|
||||
{
|
||||
}
|
||||
|
||||
- (void)mouseExited:(CPEvent)event
|
||||
{
|
||||
}
|
||||
|
||||
- (void)set
|
||||
{
|
||||
document.body.style.cursor = _cssString;
|
||||
currentCursor = self;
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
var platformWindows = [[CPPlatformWindow visiblePlatformWindows] allObjects];
|
||||
for (var i = 0, count = [platformWindows count]; i < count; i++)
|
||||
platformWindows[i]._DOMWindow.document.body.style.cursor = _cssString;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
- (void)push
|
||||
{
|
||||
currentCursor = cursorStack.push(self);
|
||||
}
|
||||
|
||||
- (void)pop
|
||||
{
|
||||
[CPCursor pop];
|
||||
}
|
||||
|
||||
+ (void)pop
|
||||
{
|
||||
if (cursorStack.length > 1)
|
||||
{
|
||||
cursorStack.pop();
|
||||
currentCursor = cursorStack[cursorStack.length - 1];
|
||||
}
|
||||
}
|
||||
|
||||
- (id)initWithCoder:(CPCoder)coder
|
||||
{
|
||||
if (self = [super init])
|
||||
_cssString = [coder decodeObjectForKey:@"CPCursorNameKey"];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)coder
|
||||
{
|
||||
[coder encodeObject:_cssString forKey:@"CPCursorNameKey"];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -280,7 +280,11 @@ var CPDocumentUntitledCount = 0;
|
||||
else if (viewController)
|
||||
{
|
||||
var view = [viewController view],
|
||||
theWindow = [[CPWindow alloc] initWithContentRect:[view frame] styleMask:CPTitledWindowMask | CPClosableWindowMask | CPMiniaturizableWindowMask | CPResizableWindowMask];
|
||||
viewFrame = [view frame];
|
||||
|
||||
viewFrame.origin = CGPointMake(50, 50);
|
||||
|
||||
var theWindow = [[CPWindow alloc] initWithContentRect:viewFrame styleMask:CPTitledWindowMask | CPClosableWindowMask | CPMiniaturizableWindowMask | CPResizableWindowMask];
|
||||
|
||||
windowController = [[CPWindowController alloc] initWithWindow:theWindow];
|
||||
}
|
||||
|
||||
@@ -146,6 +146,8 @@ var CPSharedDocumentController = nil;
|
||||
|
||||
result = [self makeDocumentWithContentsOfURL:anAbsoluteURL ofType:type delegate:self didReadSelector:@selector(document:didRead:contextInfo:) contextInfo:[CPDictionary dictionaryWithObject:shouldDisplay forKey:@"shouldDisplay"]];
|
||||
|
||||
[self addDocument:result];
|
||||
|
||||
if (result)
|
||||
[self noteNewRecentDocument:result];
|
||||
}
|
||||
@@ -207,7 +209,6 @@ var CPSharedDocumentController = nil;
|
||||
if (!didRead)
|
||||
return;
|
||||
|
||||
[self addDocument:aDocument];
|
||||
[aDocument makeWindowControllers];
|
||||
|
||||
if ([aContextInfo objectForKey:@"shouldDisplay"])
|
||||
|
||||
@@ -310,7 +310,7 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
|
||||
_draggingOffset = _CGSizeMake(mouseDownEventLocation.x - viewLocation.x, mouseDownEventLocation.y - viewLocation.y);
|
||||
}
|
||||
else
|
||||
_draggingOffset = _CGSizeMakerZero();
|
||||
_draggingOffset = _CGSizeMakeZero();
|
||||
|
||||
if ([CPPlatform isBrowser])
|
||||
[_draggedWindow setPlatformWindow:[aWindow platformWindow]];
|
||||
|
||||
@@ -285,6 +285,17 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
return _CGPointMakeCopy(_location);
|
||||
}
|
||||
|
||||
- (CGPoint)globalLocation
|
||||
{
|
||||
var theWindow = [self window],
|
||||
location = [self locationInWindow];
|
||||
|
||||
if (theWindow)
|
||||
return [theWindow convertBaseToGlobal:location];
|
||||
|
||||
return location;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns event information as a bit mask
|
||||
*/
|
||||
|
||||
@@ -234,9 +234,9 @@ function CPRectContainsPoint(aRect, aPoint)
|
||||
@param possibleInner the CGRect to test if it fits inside \c possibleOuter.
|
||||
@return BOOL \c YES if \c possibleInner fits inside \c possibleOuter.
|
||||
*/
|
||||
function CPRectContainsRect(lhsRect, rhsRect)
|
||||
function CPRectContainsRect(possibleOuter, possibleInner)
|
||||
{
|
||||
return CPRectEqualToRect(CPUnionRect(lhsRect, rhsRect), rhsRect);
|
||||
return CGRectContainsRect(possibleOuter, possibleInner);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
@@ -0,0 +1,556 @@
|
||||
|
||||
#include "../CoreGraphics/CGGeometry.h"
|
||||
|
||||
@import "_CPMenuWindow.j"
|
||||
|
||||
|
||||
var MENUBAR_HEIGHT = 28.0,
|
||||
MENUBAR_MARGIN = 10.0,
|
||||
MENUBAR_LEFT_MARGIN = 10.0,
|
||||
MENUBAR_RIGHT_MARGIN = 10.0;
|
||||
|
||||
var _CPMenuBarWindowBackgroundColor = nil,
|
||||
_CPMenuBarWindowFont = nil;
|
||||
|
||||
@implementation _CPMenuBarWindow : CPPanel
|
||||
{
|
||||
CPMenu _menu;
|
||||
CPView _highlightView;
|
||||
CPArray _menuItemViews;
|
||||
|
||||
CPMenuItem _trackingMenuItem;
|
||||
|
||||
CPImageView _iconImageView;
|
||||
CPTextField _titleField;
|
||||
|
||||
CPColor _textColor;
|
||||
CPColor _titleColor;
|
||||
|
||||
CPColor _textShadowColor;
|
||||
CPColor _titleShadowColor;
|
||||
|
||||
CPColor _highlightColor;
|
||||
CPColor _highlightTextColor;
|
||||
CPColor _highlightTextShadowColor;
|
||||
}
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
if (self != [_CPMenuBarWindow class])
|
||||
return;
|
||||
|
||||
var bundle = [CPBundle bundleForClass:self];
|
||||
|
||||
_CPMenuBarWindowFont = [CPFont boldSystemFontOfSize:12.0];
|
||||
}
|
||||
|
||||
+ (CPFont)font
|
||||
{
|
||||
return _CPMenuBarWindowFont;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
// This only shows up in browser land, so don't bother calculating metrics in desktop.
|
||||
var contentRect = [[CPPlatformWindow primaryPlatformWindow] contentBounds];
|
||||
|
||||
contentRect.size.height = MENUBAR_HEIGHT;
|
||||
|
||||
self = [super initWithContentRect:contentRect styleMask:CPBorderlessWindowMask];
|
||||
|
||||
if (self)
|
||||
{
|
||||
// FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/39-dont-allow-windows-to-go-above-menubar
|
||||
[self setLevel:-1];//CPTornOffMenuWindowLevel];
|
||||
[self setAutoresizingMask:CPWindowWidthSizable];
|
||||
|
||||
var contentView = [self contentView];
|
||||
|
||||
[contentView setAutoresizesSubviews:NO];
|
||||
|
||||
[self setBecomesKeyOnlyIfNeeded:YES];
|
||||
|
||||
//
|
||||
_iconImageView = [[CPImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, 16.0, 16.0)];
|
||||
|
||||
[contentView addSubview:_iconImageView];
|
||||
|
||||
_titleField = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];
|
||||
|
||||
[_titleField setFont:[CPFont boldSystemFontOfSize:13.0]];
|
||||
[_titleField setAlignment:CPCenterTextAlignment];
|
||||
[_titleField setTextShadowOffset:CGSizeMake(0, 1)];
|
||||
|
||||
[contentView addSubview:_titleField];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setTitle:(CPString)aTitle
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
var bundleName = [[CPBundle mainBundle] objectForInfoDictionaryKey:@"CPBundleName"];
|
||||
|
||||
if (![bundleName length])
|
||||
document.title = aTitle;
|
||||
else if ([aTitle length])
|
||||
document.title = aTitle + @" - " + bundleName;
|
||||
else
|
||||
document.title = bundleName;
|
||||
#endif
|
||||
|
||||
[_titleField setStringValue:aTitle];
|
||||
[_titleField sizeToFit];
|
||||
|
||||
[self tile];
|
||||
}
|
||||
|
||||
- (void)setIconImage:(CPImage)anImage
|
||||
{
|
||||
[_iconImageView setImage:anImage];
|
||||
[_iconImageView setHidden:anImage == nil];
|
||||
|
||||
[self tile];
|
||||
}
|
||||
|
||||
- (void)setIconImageAlphaValue:(float)anAlphaValue
|
||||
{
|
||||
[_iconImageView setAlphaValue:anAlphaValue];
|
||||
}
|
||||
|
||||
- (void)setColor:(CPColor)aColor
|
||||
{
|
||||
if (!aColor)
|
||||
{
|
||||
if (!_CPMenuBarWindowBackgroundColor)
|
||||
_CPMenuBarWindowBackgroundColor = [CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[_CPMenuBarWindow class]] pathForResource:@"_CPMenuBarWindow/_CPMenuBarWindowBackground.png"] size:CGSizeMake(1.0, 28.0)]];
|
||||
|
||||
[[self contentView] setBackgroundColor:_CPMenuBarWindowBackgroundColor];
|
||||
}
|
||||
else
|
||||
[[self contentView] setBackgroundColor:aColor];
|
||||
}
|
||||
|
||||
- (void)setTextColor:(CPColor)aColor
|
||||
{
|
||||
if (_textColor == aColor)
|
||||
return;
|
||||
|
||||
_textColor = aColor;
|
||||
|
||||
[_menuItemViews makeObjectsPerformSelector:@selector(setTextColor:) withObject:_textColor];
|
||||
}
|
||||
|
||||
- (void)setTitleColor:(CPColor)aColor
|
||||
{
|
||||
if (_titleColor == aColor)
|
||||
return;
|
||||
|
||||
_titleColor = aColor;
|
||||
|
||||
[_titleField setTextColor:aColor ? aColor : [CPColor blackColor]];
|
||||
}
|
||||
|
||||
- (void)setTextShadowColor:(CPColor)aColor
|
||||
{
|
||||
if (_textShadowColor == aColor)
|
||||
return;
|
||||
|
||||
_textShadowColor = aColor;
|
||||
|
||||
[_menuItemViews makeObjectsPerformSelector:@selector(setTextShadowColor:) withObject:_textShadowColor];
|
||||
}
|
||||
|
||||
- (void)setTitleShadowColor:(CPColor)aColor
|
||||
{
|
||||
if (_titleShadowColor == aColor)
|
||||
return;
|
||||
|
||||
_titleShadowColor = aColor;
|
||||
|
||||
[_titleField setTextShadowColor:aColor ? aColor : [CPColor whiteColor]];
|
||||
}
|
||||
|
||||
- (void)setHighlightColor:(CPColor)aColor
|
||||
{
|
||||
if (_highlightColor == aColor)
|
||||
return;
|
||||
|
||||
_highlightColor = aColor;
|
||||
}
|
||||
|
||||
- (void)setHighlightTextColor:(CPColor)aColor
|
||||
{
|
||||
if (_highlightTextColor == aColor)
|
||||
return;
|
||||
|
||||
_highlightTextColor = aColor;
|
||||
|
||||
// [_menuItemViews makeObjectsPerformSelector:@selector(setActivateColor:) withObject:_highlightTextColor];
|
||||
}
|
||||
|
||||
- (void)setHighlightTextShadowColor:(CPColor)aColor
|
||||
{
|
||||
if (_highlightTextShadowColor == aColor)
|
||||
return;
|
||||
|
||||
_highlightTextShadowColor = aColor;
|
||||
|
||||
// [_menuItemViews makeObjectsPerformSelector:@selector(setActivateShadowColor:) withObject:_highlightTextShadowColor];
|
||||
}
|
||||
|
||||
- (void)setMenu:(CPMenu)aMenu
|
||||
{
|
||||
if (_menu == aMenu)
|
||||
return;
|
||||
|
||||
var defaultCenter = [CPNotificationCenter defaultCenter];
|
||||
|
||||
if (_menu)
|
||||
{
|
||||
[defaultCenter
|
||||
removeObserver:self
|
||||
name:CPMenuDidAddItemNotification
|
||||
object:_menu];
|
||||
|
||||
[defaultCenter
|
||||
removeObserver:self
|
||||
name:CPMenuDidChangeItemNotification
|
||||
object:_menu];
|
||||
|
||||
[defaultCenter
|
||||
removeObserver:self
|
||||
name:CPMenuDidRemoveItemNotification
|
||||
object:_menu];
|
||||
|
||||
var items = [_menu itemArray],
|
||||
count = items.length;
|
||||
|
||||
while (count--)
|
||||
[[items[count] _menuItemView] removeFromSuperview];
|
||||
}
|
||||
|
||||
_menu = aMenu;
|
||||
|
||||
if (_menu)
|
||||
{
|
||||
[defaultCenter
|
||||
addObserver:self
|
||||
selector:@selector(menuDidAddItem:)
|
||||
name:CPMenuDidAddItemNotification
|
||||
object:_menu];
|
||||
|
||||
[defaultCenter
|
||||
addObserver:self
|
||||
selector:@selector(menuDidChangeItem:)
|
||||
name:CPMenuDidChangeItemNotification
|
||||
object:_menu];
|
||||
|
||||
[defaultCenter
|
||||
addObserver:self
|
||||
selector:@selector(menuDidRemoveItem:)
|
||||
name:CPMenuDidRemoveItemNotification
|
||||
object:_menu];
|
||||
}
|
||||
|
||||
_menuItemViews = [];
|
||||
|
||||
var contentView = [self contentView],
|
||||
items = [_menu itemArray],
|
||||
count = items.length;
|
||||
|
||||
for (index = 0; index < count; ++index)
|
||||
{
|
||||
var item = items[index],
|
||||
menuItemView = [item _menuItemView];
|
||||
|
||||
_menuItemViews.push(menuItemView);
|
||||
|
||||
[menuItemView setTextColor:_textColor];
|
||||
[menuItemView setHidden:[item isHidden]];
|
||||
|
||||
[menuItemView synchronizeWithMenuItem];
|
||||
|
||||
[contentView addSubview:menuItemView];
|
||||
}
|
||||
|
||||
[self tile];
|
||||
}
|
||||
|
||||
- (void)menuDidChangeItem:(CPNotification)aNotification
|
||||
{
|
||||
var menuItem = [_menu itemAtIndex:[[aNotification userInfo] objectForKey:@"CPMenuItemIndex"]],
|
||||
menuItemView = [menuItem _menuItemView];
|
||||
|
||||
[menuItemView setHidden:[menuItem isHidden]];
|
||||
[menuItemView synchronizeWithMenuItem];
|
||||
|
||||
[self tile];
|
||||
}
|
||||
|
||||
- (void)menuDidAddItem:(CPNotification)aNotification
|
||||
{
|
||||
var index = [[aNotification userInfo] objectForKey:@"CPMenuItemIndex"],
|
||||
menuItem = [_menu itemAtIndex:index],
|
||||
menuItemView = [menuItem _menuItemView];
|
||||
|
||||
[_menuItemViews insertObject:menuItemView atIndex:index];
|
||||
|
||||
[menuItemView setTextColor:_textColor];
|
||||
[menuItemView setHidden:[menuItem isHidden]];
|
||||
|
||||
[menuItemView synchronizeWithMenuItem];
|
||||
|
||||
[[self contentView] addSubview:menuItemView];
|
||||
|
||||
[self tile];
|
||||
}
|
||||
|
||||
- (void)menuDidRemoveItem:(CPNotification)aNotification
|
||||
{
|
||||
var index = [[aNotification userInfo] objectForKey:@"CPMenuItemIndex"],
|
||||
menuItemView = [_menuItemViews objectAtIndex:index];
|
||||
|
||||
[_menuItemViews removeObjectAtIndex:index];
|
||||
|
||||
[menuItemView removeFromSuperview];
|
||||
|
||||
[self tile];
|
||||
}
|
||||
|
||||
- (void)mouseDown:(CPEvent)anEvent
|
||||
{
|
||||
var constraintRect = CGRectInset([[self platformWindow] visibleFrame], 5.0, 0.0);
|
||||
|
||||
constraintRect.size.height -= 5.0;
|
||||
|
||||
[[_CPMenuManager sharedMenuManager]
|
||||
beginTracking:anEvent
|
||||
menuContainer:self
|
||||
constraintRect:constraintRect
|
||||
callback:function(aMenuContainer, aMenu)
|
||||
{
|
||||
[aMenu _performActionOfHighlightedItemChain];
|
||||
[aMenu _highlightItemAtIndex:CPNotFound];
|
||||
}];
|
||||
}
|
||||
|
||||
- (CPFont)font
|
||||
{
|
||||
[CPFont systemFontOfSize:12.0];
|
||||
}
|
||||
|
||||
- (void)tile
|
||||
{
|
||||
var items = [_menu itemArray],
|
||||
index = 0,
|
||||
count = items.length,
|
||||
|
||||
x = MENUBAR_LEFT_MARGIN,
|
||||
y = 0.0,
|
||||
isLeftAligned = YES;
|
||||
|
||||
for (; index < count; ++index)
|
||||
{
|
||||
var item = items[index];
|
||||
|
||||
if ([item isSeparatorItem])
|
||||
{
|
||||
x = CGRectGetWidth([self frame]) - MENUBAR_RIGHT_MARGIN;
|
||||
isLeftAligned = NO;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ([item isHidden])
|
||||
continue;
|
||||
|
||||
var menuItemView = [item _menuItemView],
|
||||
frame = [menuItemView frame];
|
||||
|
||||
if (isLeftAligned)
|
||||
{
|
||||
[menuItemView setFrame:CGRectMake(x, 0.0, CGRectGetWidth(frame), MENUBAR_HEIGHT)];
|
||||
|
||||
x += CGRectGetWidth([menuItemView frame]);
|
||||
}
|
||||
else
|
||||
{
|
||||
[menuItemView setFrame:CGRectMake(x - CGRectGetWidth(frame), 0.0, CGRectGetWidth(frame), MENUBAR_HEIGHT)];
|
||||
|
||||
x = CGRectGetMinX([menuItemView frame]);
|
||||
}
|
||||
}
|
||||
|
||||
var bounds = [[self contentView] bounds],
|
||||
titleFrame = [_titleField frame];
|
||||
|
||||
if ([_iconImageView isHidden])
|
||||
[_titleField setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - CGRectGetWidth(titleFrame)) / 2.0, (CGRectGetHeight(bounds) - CGRectGetHeight(titleFrame)) / 2.0)];
|
||||
else
|
||||
{
|
||||
var iconFrame = [_iconImageView frame],
|
||||
iconWidth = CGRectGetWidth(iconFrame),
|
||||
totalWidth = iconWidth + CGRectGetWidth(titleFrame);
|
||||
|
||||
[_iconImageView setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - totalWidth) / 2.0, (CGRectGetHeight(bounds) - CGRectGetHeight(iconFrame)) / 2.0)];
|
||||
[_titleField setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - totalWidth) / 2.0 + iconWidth, (CGRectGetHeight(bounds) - CGRectGetHeight(titleFrame)) / 2.0)];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setFrame:(CGRect)aRect display:(BOOL)shouldDisplay animate:(BOOL)shouldAnimate
|
||||
{
|
||||
var size = [self frame].size;
|
||||
|
||||
[super setFrame:aRect display:shouldDisplay animate:shouldAnimate];
|
||||
|
||||
if (!_CGSizeEqualToSize(size, aRect.size))
|
||||
[self tile];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPMenuBarWindow (CPMenuContainer)
|
||||
|
||||
- (BOOL)isMenuBar
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (CGRect)globalFrame
|
||||
{
|
||||
return [self frame];
|
||||
}
|
||||
|
||||
- (_CPManagerScrollingState)scrollingStateForPoint:(CGPoint)aGlobalLocation
|
||||
{
|
||||
return _CPMenuManagerScrollingStateNone;
|
||||
}
|
||||
|
||||
- (CPInteger)itemIndexAtPoint:(CGPoint)aPoint
|
||||
{
|
||||
var items = [_menu itemArray],
|
||||
index = items.length;
|
||||
|
||||
while (index--)
|
||||
{
|
||||
var item = items[index];
|
||||
|
||||
if ([item isHidden] || [item isSeparatorItem])
|
||||
continue;
|
||||
|
||||
if (CGRectContainsPoint([self rectForItemAtIndex:index], aPoint))
|
||||
return index;
|
||||
}
|
||||
|
||||
return CPNotFound;
|
||||
}
|
||||
|
||||
- (CGRect)rectForItemAtIndex:(CPInteger)anIndex
|
||||
{
|
||||
var menuItem = [_menu itemAtIndex:anIndex === CPNotFound ? 0 : anIndex];
|
||||
|
||||
return [[menuItem _menuItemView] frame];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPMenuBarView : _CPMenuView
|
||||
{
|
||||
}
|
||||
|
||||
- (CGRect)rectForItemAtIndex:(int)anIndex
|
||||
{
|
||||
return [_menuItemViews[anIndex === CPNotFound ? 0 : anIndex] frame];
|
||||
}
|
||||
|
||||
- (int)itemIndexAtPoint:(CGPoint)aPoint
|
||||
{
|
||||
var bounds = [self bounds];
|
||||
|
||||
if (!CGRectContainsPoint(bounds, aPoint))
|
||||
return CPNotFound;
|
||||
|
||||
var x = aPoint.x,
|
||||
low = 0,
|
||||
high = _visibleMenuItemInfos.length - 1;
|
||||
|
||||
while (low <= high)
|
||||
{
|
||||
var middle = FLOOR(low + (high - low) / 2),
|
||||
info = _visibleMenuItemInfos[middle],
|
||||
frame = [info.view frame];
|
||||
|
||||
if (x < CGRectGetMinX(frame))
|
||||
high = middle - 1;
|
||||
|
||||
else if (x > CGRectGetMaxX(frame))
|
||||
low = middle + 1;
|
||||
|
||||
else
|
||||
return info.index;
|
||||
}
|
||||
|
||||
return CPNotFound;
|
||||
}
|
||||
|
||||
- (void)tile
|
||||
{
|
||||
var items = [_menu itemArray],
|
||||
index = 0,
|
||||
count = items.length,
|
||||
|
||||
x = MENUBAR_LEFT_MARGIN,
|
||||
y = 0.0,
|
||||
isLeftAligned = YES;
|
||||
|
||||
for (; index < count; ++index)
|
||||
{
|
||||
var item = items[index];
|
||||
|
||||
if ([item isSeparatorItem])
|
||||
{
|
||||
x = CGRectGetWidth([self frame]) - MENUBAR_RIGHT_MARGIN;
|
||||
isLeftAligned = NO;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ([item isHidden])
|
||||
continue;
|
||||
|
||||
var menuItemView = [item _menuItemView],
|
||||
frame = [menuItemView frame];
|
||||
|
||||
if (isLeftAligned)
|
||||
{
|
||||
[menuItemView setFrameOrigin:CGPointMake(x, (MENUBAR_HEIGHT - 1.0 - CGRectGetHeight(frame)) / 2.0)];
|
||||
|
||||
x += CGRectGetWidth([menuItemView frame]) + MENUBAR_MARGIN;
|
||||
}
|
||||
else
|
||||
{
|
||||
[menuItemView setFrameOrigin:CGPointMake(x - CGRectGetWidth(frame), (MENUBAR_HEIGHT - 1.0 - CGRectGetHeight(frame)) / 2.0)];
|
||||
|
||||
x = CGRectGetMinX([menuItemView frame]) - MENUBAR_MARGIN;
|
||||
}
|
||||
}
|
||||
|
||||
var bounds = [[self contentView] bounds],
|
||||
titleFrame = [_titleField frame];
|
||||
|
||||
if ([_iconImageView isHidden])
|
||||
[_titleField setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - CGRectGetWidth(titleFrame)) / 2.0, (CGRectGetHeight(bounds) - CGRectGetHeight(titleFrame)) / 2.0)];
|
||||
else
|
||||
{
|
||||
var iconFrame = [_iconImageView frame],
|
||||
iconWidth = CGRectGetWidth(iconFrame),
|
||||
totalWidth = iconWidth + CGRectGetWidth(titleFrame);
|
||||
|
||||
[_iconImageView setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - totalWidth) / 2.0, (CGRectGetHeight(bounds) - CGRectGetHeight(iconFrame)) / 2.0)];
|
||||
[_titleField setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - totalWidth) / 2.0 + iconWidth, (CGRectGetHeight(bounds) - CGRectGetHeight(titleFrame)) / 2.0)];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,322 @@
|
||||
|
||||
#include "../CoreGraphics/CGGeometry.h"
|
||||
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
|
||||
_CPMenuManagerScrollingStateUp = -1,
|
||||
_CPMenuManagerScrollingStateDown = 1,
|
||||
_CPMenuManagerScrollingStateNone = 0;
|
||||
|
||||
var STICKY_TIME_INTERVAL = 500;
|
||||
|
||||
var SharedMenuManager = nil;
|
||||
|
||||
@implementation _CPMenuManager: CPObject
|
||||
{
|
||||
CPTimeInterval _startTime;
|
||||
int _scrollingState;
|
||||
CGPoint _lastGlobalLocation;
|
||||
|
||||
CPView _lastMouseOverMenuView;
|
||||
|
||||
CGRect _constraintRect;
|
||||
CPMutableArray _menuContainerStack;
|
||||
|
||||
Function _trackingCallback;
|
||||
}
|
||||
|
||||
+ (_CPMenuManager)sharedMenuManager
|
||||
{
|
||||
if (!SharedMenuManager)
|
||||
SharedMenuManager = [[_CPMenuManager alloc] init];
|
||||
|
||||
return SharedMenuManager;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if (SharedMenuManager)
|
||||
return SharedMenuManager;
|
||||
|
||||
return [super init];
|
||||
}
|
||||
|
||||
- (id)trackingMenuContainer
|
||||
{
|
||||
return _menuContainerStack[0];
|
||||
}
|
||||
|
||||
- (CPMenu)trackingMenu
|
||||
{
|
||||
return [[self trackingMenuContainer] menu];
|
||||
}
|
||||
|
||||
- (void)beginTracking:(CPEvent)anEvent
|
||||
menuContainer:(id)aMenuContainer
|
||||
constraintRect:(CGRect)aRect
|
||||
callback:(Function)aCallback
|
||||
{
|
||||
CPApp._activeMenu = [aMenuContainer menu];
|
||||
|
||||
_startTime = [anEvent timestamp];//new Date();
|
||||
_scrollingState = _CPMenuManagerScrollingStateNone;
|
||||
|
||||
_constraintRect = aRect;
|
||||
_trackingCallback = aCallback;
|
||||
_menuContainerStack = [aMenuContainer];
|
||||
|
||||
[self trackEvent:anEvent];
|
||||
}
|
||||
|
||||
- (void)trackEvent:(CPEvent)anEvent
|
||||
{
|
||||
var type = [anEvent type],
|
||||
trackingMenu = [self trackingMenu];
|
||||
|
||||
// Close Menu Event.
|
||||
if (type === CPAppKitDefined)
|
||||
{
|
||||
// Stop all periodic events at this point.
|
||||
[CPEvent stopPeriodicEvents];
|
||||
|
||||
// Get the highlighted item from the original menu.
|
||||
var highlightedItem = [trackingMenu highlightedItem];
|
||||
|
||||
// Hide all submenus.
|
||||
[self showMenu:nil fromMenu:trackingMenu atPoint:nil];
|
||||
|
||||
var delegate = [trackingMenu delegate];
|
||||
|
||||
if ([delegate respondsToSelector:@selector(menuDidClose:)])
|
||||
[delegate menuDidClose:trackingMenu];
|
||||
|
||||
if (_trackingCallback)
|
||||
_trackingCallback([self trackingMenuContainer], trackingMenu);
|
||||
|
||||
[[CPNotificationCenter defaultCenter]
|
||||
postNotificationName:CPMenuDidEndTrackingNotification
|
||||
object:trackingMenu];
|
||||
|
||||
CPApp._activeMenu = nil;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
[CPApp setTarget:self selector:@selector(trackEvent:) forNextEventMatchingMask:CPPeriodicMask | CPMouseMovedMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask | CPAppKitDefinedMask untilDate:nil inMode:nil dequeue:YES];
|
||||
|
||||
// Periodic events don't have a valid location.
|
||||
var globalLocation = type === CPPeriodic ? _lastGlobalLocation : [anEvent globalLocation];
|
||||
|
||||
// Remember this for the next periodic event.
|
||||
_lastGlobalLocation = globalLocation;
|
||||
|
||||
// Find which menu window the mouse is currently on top of
|
||||
var activeMenuContainer = [self menuContainerForPoint:globalLocation],
|
||||
activeMenu = [activeMenuContainer menu],
|
||||
menuLocation = [activeMenuContainer convertGlobalToBase:globalLocation];
|
||||
|
||||
// Find out the item the mouse is currently on top of
|
||||
var activeItemIndex = activeMenuContainer ? [activeMenuContainer itemIndexAtPoint:menuLocation] : CPNotFound,
|
||||
activeItem = activeItemIndex !== CPNotFound ? [activeMenu itemAtIndex:activeItemIndex] : nil;
|
||||
|
||||
// If the item isn't enabled its as if we clicked on nothing.
|
||||
if (![activeItem isEnabled])
|
||||
{
|
||||
activeItemIndex = CPNotFound;
|
||||
activeItem = nil;
|
||||
}
|
||||
|
||||
var mouseOverMenuView = [activeItem view];
|
||||
|
||||
if (type === CPPeriodic)
|
||||
{
|
||||
if (_scrollingState === _CPMenuManagerScrollingStateUp)
|
||||
[activeMenuContainer scrollUp];
|
||||
|
||||
else if (_scrollingState === _CPMenuManagerScrollingStateDown)
|
||||
[activeMenuContainer scrollDown];
|
||||
}
|
||||
|
||||
// If we're over a custom menu view...
|
||||
if (mouseOverMenuView)
|
||||
{
|
||||
if (!_lastMouseOverMenuView)
|
||||
[activeMenu _highlightItemAtIndex:CPNotFound];
|
||||
|
||||
if (_lastMouseOverMenuView != mouseOverMenuView)
|
||||
{
|
||||
[mouseOverMenuView mouseExited:anEvent];
|
||||
// FIXME: Possibly multiple of these?
|
||||
[_lastMouseOverMenuView mouseEntered:anEvent];
|
||||
|
||||
_lastMouseOverMenuView = mouseOverMenuView;
|
||||
}
|
||||
|
||||
var menuContainerWindow = activeMenuContainer;
|
||||
|
||||
if (![menuContainerWindow isKindOfClass:[CPWindow class]])
|
||||
menuContainerWindow = [menuContainerWindow window];
|
||||
|
||||
[menuContainerWindow
|
||||
sendEvent:[CPEvent mouseEventWithType:type location:menuLocation modifierFlags:[anEvent modifierFlags]
|
||||
timestamp:[anEvent timestamp]
|
||||
windowNumber:menuContainerWindow
|
||||
context:nil
|
||||
eventNumber:0
|
||||
clickCount:[anEvent clickCount]
|
||||
pressure:[anEvent pressure]]];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_lastMouseOverMenuView)
|
||||
{
|
||||
[_lastMouseOverMenuView mouseExited:anEvent];
|
||||
_lastMouseOverMenuView = nil;
|
||||
}
|
||||
|
||||
[activeMenu _highlightItemAtIndex:activeItemIndex];
|
||||
|
||||
if (type === CPMouseMoved || type === CPLeftMouseDragged || type === CPLeftMouseDown)
|
||||
{
|
||||
var oldScrollingState = _scrollingState;
|
||||
|
||||
_scrollingState = [activeMenuContainer scrollingStateForPoint:globalLocation];
|
||||
|
||||
if (_scrollingState !== oldScrollingState)
|
||||
{
|
||||
if (_scrollingState === _CPMenuManagerScrollingStateNone)
|
||||
[CPEvent stopPeriodicEvents];
|
||||
|
||||
else if (oldScrollingState === _CPMenuManagerScrollingStateNone)
|
||||
[CPEvent startPeriodicEventsAfterDelay:0.0 withPeriod:0.04];
|
||||
}
|
||||
}
|
||||
else if (type === CPLeftMouseUp && ([anEvent timestamp] - _startTime > STICKY_TIME_INTERVAL))
|
||||
[trackingMenu cancelTracking];
|
||||
}
|
||||
|
||||
// If the item has a submenu, show it.
|
||||
if ([activeItem hasSubmenu])// && [activeItem action] === @selector(submenuAction:))
|
||||
{
|
||||
var activeItemRect = [activeMenuContainer rectForItemAtIndex:activeItemIndex];
|
||||
|
||||
if ([activeMenuContainer isMenuBar])
|
||||
var newMenuOrigin = CGPointMake(CGRectGetMinX(activeItemRect), CGRectGetMaxY(activeItemRect));
|
||||
else
|
||||
var newMenuOrigin = CGPointMake(CGRectGetMaxX(activeItemRect), CGRectGetMinY(activeItemRect));
|
||||
|
||||
newMenuOrigin = [activeMenuContainer convertBaseToGlobal:newMenuOrigin];
|
||||
|
||||
[self showMenu:[activeItem submenu] fromMenu:[activeItem menu] atPoint:newMenuOrigin];
|
||||
}
|
||||
|
||||
// This handles both the case where we've moved away from the menu, and where
|
||||
// we've moved to an item without a submenu.
|
||||
else
|
||||
[self showMenu:nil fromMenu:activeMenu atPoint:CGPointMakeZero()];
|
||||
}
|
||||
|
||||
|
||||
- (id)menuContainerForPoint:(float)aGlobalLocation
|
||||
{
|
||||
var count = [_menuContainerStack count],
|
||||
firstMenuContainer = _menuContainerStack[0];
|
||||
|
||||
// Trivial case.
|
||||
if (count === 1)
|
||||
return firstMenuContainer;
|
||||
|
||||
var firstMenuWindowIndex = 0,
|
||||
lastMenuWindowIndex = count - 1;
|
||||
|
||||
// If the first menu container is a bar, then it has to fall strictly within its bounds.
|
||||
if ([firstMenuContainer isMenuBar])
|
||||
{
|
||||
if (CGRectContainsPoint([firstMenuContainer globalFrame], aGlobalLocation))
|
||||
return firstMenuContainer;
|
||||
|
||||
firstMenuWindowIndex = 1;
|
||||
}
|
||||
|
||||
var index = count,
|
||||
x = aGlobalLocation.x,
|
||||
closerDeltaX = Infinity,
|
||||
closerMenuContainer = nil;
|
||||
|
||||
while (index-- > firstMenuWindowIndex)
|
||||
{
|
||||
var menuContainer = _menuContainerStack[index],
|
||||
menuContainerFrame = [menuContainer globalFrame],
|
||||
menuContainerMinX = _CGRectGetMinX(menuContainerFrame),
|
||||
menuContainerMaxX = _CGRectGetMaxX(menuContainerFrame);
|
||||
|
||||
// If within the x bounds of this menu container, return it.
|
||||
if (x < menuContainerMaxX && x >= menuContainerMinX)
|
||||
return menuContainer;
|
||||
|
||||
// If this is either the first or last menu *window*, check to see how close we are to it.
|
||||
if (index === firstMenuWindowIndex || index === lastMenuWindowIndex)
|
||||
{
|
||||
var deltaX = ABS(x < menuContainerMinX ? menuContainerMinX - x : menuContainerMaxX - x);
|
||||
|
||||
if (deltaX < closerDeltaX)
|
||||
{
|
||||
closerMenuContainer = menuContainer;
|
||||
closerDeltaX = deltaX;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return closerMenuContainer;
|
||||
}
|
||||
|
||||
- (void)showMenu:(CPMenu)newMenu fromMenu:(CPMenu)baseMenu atPoint:(CGPoint)aGlobalLocation
|
||||
{
|
||||
var count = _menuContainerStack.length,
|
||||
index = count;
|
||||
|
||||
// Hide all menus up to the base menu...
|
||||
while (index--)
|
||||
{
|
||||
var menuContainer = _menuContainerStack[index],
|
||||
menu = [menuContainer menu];
|
||||
|
||||
// If we reach the base menu, or this menu is already being shown, break.
|
||||
if (menu === baseMenu)
|
||||
break;
|
||||
|
||||
// If this menu is already being shown, unhighlight and return.
|
||||
if (menu === newMenu)//&& [menu supermenu] === baseMenu)
|
||||
return [newMenu _highlightItemAtIndex:CPNotFound];
|
||||
|
||||
[menuContainer orderOut:self];
|
||||
[menuContainer setMenu:nil];
|
||||
|
||||
[_CPMenuWindow poolMenuWindow:menuContainer];
|
||||
[_menuContainerStack removeObjectAtIndex:index];
|
||||
}
|
||||
|
||||
if (!newMenu)
|
||||
return;
|
||||
|
||||
// Unhighlight any previously highlighted item.
|
||||
[newMenu _highlightItemAtIndex:CPNotFound];
|
||||
|
||||
var menuWindow = [_CPMenuWindow menuWindowWithMenu:newMenu font:[_menuContainerStack[0] font]];
|
||||
|
||||
[_menuContainerStack addObject:menuWindow];
|
||||
|
||||
[menuWindow setConstraintRect:_constraintRect];
|
||||
|
||||
// If our parent menu is a menu bar...
|
||||
if (baseMenu === [self trackingMenu] && [[self trackingMenuContainer] isMenuBar])
|
||||
[menuWindow setBackgroundStyle:_CPMenuWindowMenuBarBackgroundStyle];
|
||||
else
|
||||
[menuWindow setBackgroundStyle:_CPMenuWindowPopUpBackgroundStyle];
|
||||
|
||||
[menuWindow setFrameOrigin:aGlobalLocation];
|
||||
[menuWindow orderFront:self];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,548 @@
|
||||
|
||||
#include "../CoreGraphics/CGGeometry.h"
|
||||
|
||||
|
||||
var _CPMenuWindowPool = [],
|
||||
_CPMenuWindowPoolCapacity = 5,
|
||||
|
||||
_CPMenuWindowBackgroundColors = [];
|
||||
|
||||
_CPMenuWindowMenuBarBackgroundStyle = 0;
|
||||
_CPMenuWindowPopUpBackgroundStyle = 1;
|
||||
_CPMenuWindowAttachedMenuBackgroundStyle = 2;
|
||||
|
||||
var STICKY_TIME_INTERVAL = 500,
|
||||
|
||||
TOP_MARGIN = 5.0,
|
||||
LEFT_MARGIN = 1.0,
|
||||
RIGHT_MARGIN = 1.0,
|
||||
BOTTOM_MARGIN = 5.0,
|
||||
|
||||
SCROLL_INDICATOR_HEIGHT = 16.0;
|
||||
|
||||
/*
|
||||
@ignore
|
||||
*/
|
||||
@implementation _CPMenuWindow : CPWindow
|
||||
{
|
||||
_CPMenuView _menuView;
|
||||
CPClipView _menuClipView;
|
||||
|
||||
CPImageView _moreAboveView;
|
||||
CPImageView _moreBelowView;
|
||||
|
||||
CGRect _unconstrainedFrame;
|
||||
CGRect _constraintRect;
|
||||
}
|
||||
|
||||
+ (id)menuWindowWithMenu:(CPMenu)aMenu font:(CPFont)aFont
|
||||
{
|
||||
var menuWindow = nil;
|
||||
|
||||
if (_CPMenuWindowPool.length)
|
||||
{
|
||||
menuWindow = _CPMenuWindowPool.pop();
|
||||
|
||||
// Do this so that coordinates will be accurate.
|
||||
[menuWindow setFrameOrigin:CGPointMakeZero()];
|
||||
}
|
||||
else
|
||||
menuWindow = [[_CPMenuWindow alloc] init];
|
||||
|
||||
[menuWindow setFont:aFont];
|
||||
[menuWindow setMenu:aMenu];
|
||||
[menuWindow setMinWidth:[aMenu minimumWidth]];
|
||||
|
||||
return menuWindow;
|
||||
}
|
||||
|
||||
+ (void)poolMenuWindow:(_CPMenuWindow)aMenuWindow
|
||||
{
|
||||
if (!aMenuWindow || _CPMenuWindowPool.length >= _CPMenuWindowPoolCapacity)
|
||||
return;
|
||||
|
||||
_CPMenuWindowPool.push(aMenuWindow);
|
||||
}
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
if (self != [_CPMenuWindow class])
|
||||
return;
|
||||
|
||||
var bundle = [CPBundle bundleForClass:self];
|
||||
|
||||
_CPMenuWindowMoreAboveImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowMoreAbove.png"] size:CGSizeMake(38.0, 18.0)];
|
||||
_CPMenuWindowMoreBelowImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowMoreBelow.png"] size:CGSizeMake(38.0, 18.0)];
|
||||
}
|
||||
|
||||
- (id)initWithContentRect:(CGRect)aRect styleMask:(unsigned)aStyleMask
|
||||
{
|
||||
_constraintRect = _CGRectMakeZero();
|
||||
_unconstrainedFrame = _CGRectMakeZero();
|
||||
|
||||
self = [super initWithContentRect:aRect styleMask:CPBorderlessWindowMask];
|
||||
|
||||
if (self)
|
||||
{
|
||||
[self setLevel:CPPopUpMenuWindowLevel];
|
||||
[self setHasShadow:YES];
|
||||
[self setShadowStyle:CPMenuWindowShadowStyle];
|
||||
[self setAcceptsMouseMovedEvents:YES];
|
||||
|
||||
var contentView = [self contentView];
|
||||
|
||||
_menuView = [[_CPMenuView alloc] initWithFrame:CGRectMakeZero()];
|
||||
|
||||
_menuClipView = [[CPClipView alloc] initWithFrame:CGRectMake(LEFT_MARGIN, TOP_MARGIN, 0.0, 0.0)];
|
||||
[_menuClipView setDocumentView:_menuView];
|
||||
|
||||
[contentView addSubview:_menuClipView];
|
||||
|
||||
_moreAboveView = [[CPImageView alloc] initWithFrame:CGRectMakeZero()];
|
||||
|
||||
[_moreAboveView setImage:_CPMenuWindowMoreAboveImage];
|
||||
[_moreAboveView setFrameSize:[_CPMenuWindowMoreAboveImage size]];
|
||||
|
||||
[contentView addSubview:_moreAboveView];
|
||||
|
||||
_moreBelowView = [[CPImageView alloc] initWithFrame:CGRectMakeZero()];
|
||||
|
||||
[_moreBelowView setImage:_CPMenuWindowMoreBelowImage];
|
||||
[_moreBelowView setFrameSize:[_CPMenuWindowMoreBelowImage size]];
|
||||
|
||||
[contentView addSubview:_moreBelowView];
|
||||
|
||||
[self setShadowStyle:CPWindowShadowStyleMenu];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (float)_standardLeftMargin
|
||||
{
|
||||
return LEFT_MARGIN;
|
||||
}
|
||||
|
||||
- (void)setFont:(CPFont)aFont
|
||||
{
|
||||
[_menuView setFont:aFont];
|
||||
}
|
||||
|
||||
- (CPFont)font
|
||||
{
|
||||
return [_menuView font];
|
||||
}
|
||||
|
||||
+ (CPColor)backgroundColorForBackgroundStyle:(_CPMenuWindowBackgroundStyle)aBackgroundStyle
|
||||
{
|
||||
var color = _CPMenuWindowBackgroundColors[aBackgroundStyle];
|
||||
|
||||
if (!color)
|
||||
{
|
||||
var bundle = [CPBundle bundleForClass:[self class]];
|
||||
|
||||
if (aBackgroundStyle == _CPMenuWindowPopUpBackgroundStyle)
|
||||
color = [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices:
|
||||
[
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowRounded0.png"] size:CGSizeMake(4.0, 4.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow1.png"] size:CGSizeMake(1.0, 4.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowRounded2.png"] size:CGSizeMake(4.0, 4.0)],
|
||||
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow3.png"] size:CGSizeMake(4.0, 1.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow4.png"] size:CGSizeMake(1.0, 1.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow5.png"] size:CGSizeMake(4.0, 1.0)],
|
||||
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowRounded6.png"] size:CGSizeMake(4.0, 4.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow7.png"] size:CGSizeMake(1.0, 4.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowRounded8.png"] size:CGSizeMake(4.0, 4.0)]
|
||||
]]];
|
||||
|
||||
else if (aBackgroundStyle == _CPMenuWindowMenuBarBackgroundStyle)
|
||||
color = [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices:
|
||||
[
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow3.png"] size:CGSizeMake(4.0, 0.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow4.png"] size:CGSizeMake(1.0, 0.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow5.png"] size:CGSizeMake(4.0, 0.0)],
|
||||
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow3.png"] size:CGSizeMake(4.0, 1.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow4.png"] size:CGSizeMake(1.0, 1.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow5.png"] size:CGSizeMake(4.0, 1.0)],
|
||||
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowRounded6.png"] size:CGSizeMake(4.0, 4.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow7.png"] size:CGSizeMake(1.0, 4.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowRounded8.png"] size:CGSizeMake(4.0, 4.0)]
|
||||
]]];
|
||||
|
||||
_CPMenuWindowBackgroundColors[aBackgroundStyle] = color;
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
- (void)setBackgroundStyle:(_CPMenuWindowBackgroundStyle)aBackgroundStyle
|
||||
{
|
||||
[self setBackgroundColor:[[self class] backgroundColorForBackgroundStyle:aBackgroundStyle]];
|
||||
}
|
||||
|
||||
- (void)setMenu:(CPMenu)aMenu
|
||||
{
|
||||
[aMenu _setMenuWindow:self];
|
||||
[_menuView setMenu:aMenu];
|
||||
|
||||
var menuViewSize = [_menuView frame].size;
|
||||
|
||||
[self setFrameSize:CGSizeMake(LEFT_MARGIN + menuViewSize.width + RIGHT_MARGIN, TOP_MARGIN + menuViewSize.height + BOTTOM_MARGIN)];
|
||||
|
||||
[_menuView scrollPoint:CGPointMake(0.0, 0.0)];
|
||||
[_menuClipView setFrame:CGRectMake(LEFT_MARGIN, TOP_MARGIN, menuViewSize.width, menuViewSize.height)];
|
||||
}
|
||||
|
||||
- (void)setMinWidth:(float)aWidth
|
||||
{
|
||||
var size = [self unconstrainedFrame].size;
|
||||
|
||||
[self setFrameSize:CGSizeMake(MAX(size.width, aWidth), size.height)];
|
||||
}
|
||||
|
||||
- (CPMenu)menu
|
||||
{
|
||||
return [_menuView menu];
|
||||
}
|
||||
|
||||
- (void)orderFront:(id)aSender
|
||||
{
|
||||
[self setFrame:_unconstrainedFrame];
|
||||
|
||||
[super orderFront:aSender];
|
||||
}
|
||||
|
||||
- (void)setConstraintRect:(CGRect)aRect
|
||||
{
|
||||
_constraintRect = aRect;
|
||||
|
||||
[self setFrame:_unconstrainedFrame];
|
||||
}
|
||||
|
||||
- (CGRect)unconstrainedFrame
|
||||
{
|
||||
return _CGRectMakeCopy(_unconstrainedFrame);
|
||||
}
|
||||
|
||||
// We need this because if not this will call setFrame: with -frame instead of -unconstrainedFrame, turning
|
||||
// the constrained frame into the unconstrained frame.
|
||||
- (void)setFrameOrigin:(CGPoint)aPoint
|
||||
{
|
||||
[super setFrame:_CGRectMake(aPoint.x, aPoint.y, _CGRectGetWidth(_unconstrainedFrame), _CGRectGetHeight(_unconstrainedFrame))];
|
||||
}
|
||||
|
||||
- (void)setFrameSize:(CGSize)aSize
|
||||
{
|
||||
[super setFrame:_CGRectMake(_CGRectGetMinX(_unconstrainedFrame), _CGRectGetMinY(_unconstrainedFrame), aSize.width, aSize.height)];
|
||||
}
|
||||
|
||||
- (void)setFrame:(CGRect)aFrame display:(BOOL)shouldDisplay animate:(BOOL)shouldAnimate
|
||||
{
|
||||
// FIXME: There are integral window issues with platform windows.
|
||||
// FIXME: This gets called far too often.
|
||||
_unconstrainedFrame = _CGRectMakeCopy(aFrame);
|
||||
|
||||
var constrainedFrame = CGRectIntersection(_unconstrainedFrame, _constraintRect);
|
||||
|
||||
// We don't want to simply intersect the visible frame and the unconstrained frame.
|
||||
// We should be allowing as much of the width to fit as possible (pushing back and forward).
|
||||
constrainedFrame.origin.x = CGRectGetMinX(_unconstrainedFrame);
|
||||
constrainedFrame.size.width = CGRectGetWidth(_unconstrainedFrame);
|
||||
|
||||
if (CGRectGetWidth(constrainedFrame) > CGRectGetWidth(_constraintRect))
|
||||
constrainedFrame.size.width = CGRectGetWidth(_constraintRect);
|
||||
|
||||
if (CGRectGetMaxX(constrainedFrame) > CGRectGetMaxX(_constraintRect))
|
||||
constrainedFrame.origin.x -= CGRectGetMaxX(constrainedFrame) - CGRectGetMaxX(_constraintRect);
|
||||
|
||||
if (CGRectGetMinX(constrainedFrame) < CGRectGetMinX(_constraintRect))
|
||||
constrainedFrame.origin.x = CGRectGetMinX(_constraintRect);
|
||||
|
||||
[super setFrame:constrainedFrame display:shouldDisplay animate:shouldAnimate];
|
||||
|
||||
// This needs to happen before changing the frame.
|
||||
var menuViewOrigin = CGPointMake(CGRectGetMinX(aFrame) + LEFT_MARGIN, CGRectGetMinY(aFrame) + TOP_MARGIN),
|
||||
moreAbove = menuViewOrigin.y < CGRectGetMinY(constrainedFrame) + TOP_MARGIN,
|
||||
moreBelow = menuViewOrigin.y + CGRectGetHeight([_menuView frame]) > CGRectGetMaxY(constrainedFrame) - BOTTOM_MARGIN,
|
||||
|
||||
topMargin = TOP_MARGIN,
|
||||
bottomMargin = BOTTOM_MARGIN,
|
||||
|
||||
contentView = [self contentView],
|
||||
bounds = [contentView bounds];
|
||||
|
||||
if (moreAbove)
|
||||
{
|
||||
topMargin += SCROLL_INDICATOR_HEIGHT;
|
||||
|
||||
var frame = [_moreAboveView frame];
|
||||
|
||||
[_moreAboveView setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - CGRectGetWidth(frame)) / 2.0, (TOP_MARGIN + SCROLL_INDICATOR_HEIGHT - CGRectGetHeight(frame)) / 2.0)];
|
||||
}
|
||||
|
||||
[_moreAboveView setHidden:!moreAbove];
|
||||
|
||||
if (moreBelow)
|
||||
{
|
||||
bottomMargin += SCROLL_INDICATOR_HEIGHT;
|
||||
|
||||
[_moreBelowView setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - CGRectGetWidth([_moreBelowView frame])) / 2.0, CGRectGetHeight(bounds) - SCROLL_INDICATOR_HEIGHT - BOTTOM_MARGIN)];
|
||||
}
|
||||
|
||||
[_moreBelowView setHidden:!moreBelow];
|
||||
|
||||
var clipFrame = CGRectMake(LEFT_MARGIN, topMargin, CGRectGetWidth(constrainedFrame) - LEFT_MARGIN - RIGHT_MARGIN, CGRectGetHeight(constrainedFrame) - topMargin - bottomMargin)
|
||||
|
||||
[_menuClipView setFrame:clipFrame];
|
||||
[_menuView setFrameSize:CGSizeMake(CGRectGetWidth(clipFrame), CGRectGetHeight([_menuView frame]))];
|
||||
|
||||
[_menuView scrollPoint:CGPointMake(0.0, [self convertBaseToGlobal:clipFrame.origin].y - menuViewOrigin.y)];
|
||||
}
|
||||
|
||||
- (BOOL)hasMinimumNumberOfVisibleItems
|
||||
{
|
||||
var visibleRect = [_menuView visibleRect];
|
||||
|
||||
// Clearly if the entire view isn't visible the minimum won't be visible.
|
||||
if (CGRectIsEmpty(visibleRect))
|
||||
return NO;
|
||||
|
||||
var numberOfUnhiddenItems = [_menuView numberOfUnhiddenItems],
|
||||
minimumNumberOfVisibleItems = MIN(numberOfUnhiddenItems, 3),
|
||||
count = 0,
|
||||
index = [_menuView itemIndexAtPoint:[_menuView convertPoint:[_menuClipView frame].origin fromView:nil]];
|
||||
|
||||
for (; index < numberOfUnhiddenItems && count < minimumNumberOfVisibleItems; ++index)
|
||||
{
|
||||
var itemRect = [_menuView rectForUnhiddenItemAtIndex:index],
|
||||
visibleItemRect = CGRectIntersection(visibleRect, itemRect);
|
||||
|
||||
// As soon as we get to the first unhidden item that is no longer visible, stop.
|
||||
if (CGRectIsEmpty(visibleItemRect))
|
||||
break;
|
||||
|
||||
// If the item is *completely* visible, count it.
|
||||
if (CGRectEqualToRect(visibleItemRect, itemRect))
|
||||
++count;
|
||||
}
|
||||
|
||||
return count >= minimumNumberOfVisibleItems;
|
||||
}
|
||||
|
||||
- (BOOL)canScrollUp
|
||||
{
|
||||
return ![_moreAboveView isHidden];
|
||||
}
|
||||
|
||||
- (BOOL)canScrollDown
|
||||
{
|
||||
return ![_moreBelowView isHidden];
|
||||
}
|
||||
|
||||
- (BOOL)canScroll
|
||||
{
|
||||
return [self canScrollUp] || [self canScrollDown];
|
||||
}
|
||||
|
||||
- (void)scrollUp
|
||||
{
|
||||
if (CGRectGetMinY(_unconstrainedFrame) >= CGRectGetMinY(_constraintRect))
|
||||
return;
|
||||
|
||||
_unconstrainedFrame.origin.y += 10;
|
||||
|
||||
[self setFrame:_unconstrainedFrame];
|
||||
}
|
||||
|
||||
- (void)scrollDown
|
||||
{
|
||||
if (CGRectGetMaxY(_unconstrainedFrame) <= CGRectGetHeight(_constraintRect))
|
||||
return;
|
||||
|
||||
_unconstrainedFrame.origin.y -= 10;
|
||||
|
||||
[self setFrame:_unconstrainedFrame];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPMenuWindow (CPMenuContainer)
|
||||
|
||||
- (CGRect)globalFrame
|
||||
{
|
||||
return [self frame];
|
||||
}
|
||||
|
||||
- (BOOL)isMenuBar
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (_CPManagerScrollingState)scrollingStateForPoint:(CGPoint)aGlobalLocation
|
||||
{
|
||||
var frame = [self frame];
|
||||
|
||||
if (![self canScroll])
|
||||
return _CPMenuManagerScrollingStateNone;
|
||||
|
||||
// If we're at or above of the top scroll indicator...
|
||||
if (aGlobalLocation.y < CGRectGetMinY(frame) + TOP_MARGIN + SCROLL_INDICATOR_HEIGHT)
|
||||
return _CPMenuManagerScrollingStateUp;
|
||||
|
||||
// If we're at or below the bottom scroll indicator...
|
||||
if (aGlobalLocation.y > CGRectGetMaxY(frame) - BOTTOM_MARGIN - SCROLL_INDICATOR_HEIGHT)
|
||||
return _CPMenuManagerScrollingStateDown;
|
||||
|
||||
return _CPMenuManagerScrollingStateNone;
|
||||
}
|
||||
|
||||
- (float)deltaYForItemAtIndex:(int)anIndex
|
||||
{
|
||||
return TOP_MARGIN + CGRectGetMinY([_menuView rectForItemAtIndex:anIndex]);
|
||||
}
|
||||
|
||||
- (CGPoint)rectForItemAtIndex:(int)anIndex
|
||||
{
|
||||
return [_menuView convertRect:[_menuView rectForItemAtIndex:anIndex] toView:nil];
|
||||
}
|
||||
|
||||
- (int)itemIndexAtPoint:(CGPoint)aPoint
|
||||
{
|
||||
// Don't return indexes of items that aren't visible.
|
||||
if (!CGRectContainsPoint([_menuClipView bounds], [_menuClipView convertPoint:aPoint fromView:nil]))
|
||||
return NO;
|
||||
|
||||
return [_menuView itemIndexAtPoint:[_menuView convertPoint:aPoint fromView:nil]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/*
|
||||
@ignore
|
||||
*/
|
||||
|
||||
@implementation _CPMenuView : CPView
|
||||
{
|
||||
CPArray _menuItemViews;
|
||||
CPArray _visibleMenuItemInfos;
|
||||
|
||||
CPFont _font @accessors(property=font);
|
||||
}
|
||||
|
||||
- (unsigned)numberOfUnhiddenItems
|
||||
{
|
||||
return _visibleMenuItemInfos.length;
|
||||
}
|
||||
|
||||
- (CGRect)rectForUnhiddenItemAtIndex:(int)anIndex
|
||||
{
|
||||
return [self rectForItemAtIndex:_visibleMenuItemInfos[anIndex].index];
|
||||
}
|
||||
|
||||
- (CGRect)rectForItemAtIndex:(int)anIndex
|
||||
{
|
||||
return [_menuItemViews[anIndex === CPNotFound ? 0 : anIndex] frame];
|
||||
}
|
||||
|
||||
- (int)itemIndexAtPoint:(CGPoint)aPoint
|
||||
{
|
||||
var x = aPoint.x,
|
||||
bounds = [self bounds];
|
||||
|
||||
if (x < CGRectGetMinX(bounds) || x > CGRectGetMaxX(bounds))
|
||||
return CPNotFound;
|
||||
|
||||
var y = aPoint.y,
|
||||
low = 0,
|
||||
high = _visibleMenuItemInfos.length - 1;
|
||||
|
||||
while (low <= high)
|
||||
{
|
||||
var middle = FLOOR(low + (high - low) / 2),
|
||||
info = _visibleMenuItemInfos[middle]
|
||||
frame = [info.view frame];
|
||||
|
||||
if (y < CGRectGetMinY(frame))
|
||||
high = middle - 1;
|
||||
|
||||
else if (y > CGRectGetMaxY(frame))
|
||||
low = middle + 1;
|
||||
|
||||
else
|
||||
return info.index;
|
||||
}
|
||||
|
||||
return CPNotFound;
|
||||
}
|
||||
|
||||
- (void)tile
|
||||
{
|
||||
[_menuItemViews makeObjectsPerformSelector:@selector(removeFromSuperview)];
|
||||
|
||||
_menuItemViews = [];
|
||||
_visibleMenuItemInfos = [];
|
||||
|
||||
var menu = [self menu];
|
||||
|
||||
if (!menu)
|
||||
return;
|
||||
|
||||
var items = [menu itemArray],
|
||||
index = 0,
|
||||
count = [items count],
|
||||
maxWidth = 0,
|
||||
y = 0,
|
||||
showsStateColumn = [menu showsStateColumn];
|
||||
|
||||
for (; index < count; ++index)
|
||||
{
|
||||
var item = items[index],
|
||||
view = [item _menuItemView];
|
||||
|
||||
_menuItemViews.push(view);
|
||||
|
||||
if ([item isHidden])
|
||||
continue;
|
||||
|
||||
_visibleMenuItemInfos.push({ view:view, index:index });
|
||||
|
||||
[view setFont:_font];
|
||||
[view setShowsStateColumn:showsStateColumn];
|
||||
[view synchronizeWithMenuItem];
|
||||
|
||||
[view setFrameOrigin:CGPointMake(0.0, y)];
|
||||
|
||||
[self addSubview:view];
|
||||
|
||||
var size = [view minSize],
|
||||
width = size.width;
|
||||
|
||||
if (maxWidth < width)
|
||||
maxWidth = width;
|
||||
|
||||
y += size.height;
|
||||
}
|
||||
|
||||
for (index = 0; index < count; ++index)
|
||||
{
|
||||
var view = _menuItemViews[index];
|
||||
|
||||
[view setFrameSize:CGSizeMake(maxWidth, CGRectGetHeight([view frame]))];
|
||||
}
|
||||
|
||||
[self setAutoresizesSubviews:NO];
|
||||
[self setFrameSize:CGSizeMake(maxWidth, y)];
|
||||
[self setAutoresizesSubviews:YES];
|
||||
}
|
||||
|
||||
- (void)setMenu:(CPMenu)aMenu
|
||||
{
|
||||
[super setMenu:aMenu];
|
||||
[self tile];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -24,9 +24,10 @@
|
||||
@import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPString.j>
|
||||
|
||||
@import <AppKit/CPImage.j>
|
||||
@import <AppKit/CPMenu.j>
|
||||
@import <AppKit/CPView.j>
|
||||
@import "CPImage.j"
|
||||
@import "CPMenu.j"
|
||||
@import "CPView.j"
|
||||
@import "_CPMenuItemView.j"
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@@ -108,7 +109,9 @@
|
||||
|
||||
_keyEquivalent = aKeyEquivalent || @"";
|
||||
_keyEquivalentModifierMask = CPPlatformActionKeyMask;
|
||||
|
||||
|
||||
_indentationLevel = 0;
|
||||
|
||||
_mnemonicLocation = CPNotFound;
|
||||
}
|
||||
|
||||
@@ -448,20 +451,32 @@ CPOffState
|
||||
*/
|
||||
- (void)setSubmenu:(CPMenu)aMenu
|
||||
{
|
||||
var supermenu = [_submenu supermenu];
|
||||
|
||||
if (supermenu == self)
|
||||
if (_submenu === aMenu)
|
||||
return;
|
||||
|
||||
|
||||
var supermenu = [_submenu supermenu];
|
||||
|
||||
if (supermenu)
|
||||
return alert("bad");
|
||||
|
||||
[_submenu setSupermenu:_menu];
|
||||
|
||||
[CPException raise:CPInvalidArgumentException
|
||||
reason: @"Can't add submenu \"" + [aMenu title] + "\" to item \"" + [self title] + "\", because it is already submenu of \"" + [[aMenu supermenu] title] + "\""];
|
||||
|
||||
_submenu = aMenu;
|
||||
|
||||
|
||||
if (_submenu)
|
||||
{
|
||||
[_submenu setSupermenu:_menu];
|
||||
|
||||
[self setTarget:_menu];
|
||||
[self setAction:@selector(submenuAction:)];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self setTarget:nil];
|
||||
[self setAction:NULL];
|
||||
}
|
||||
|
||||
[_menuItemView setDirty];
|
||||
|
||||
|
||||
[_menu itemChanged:self];
|
||||
}
|
||||
|
||||
@@ -570,6 +585,43 @@ CPControlKeyMask
|
||||
return _keyEquivalentModifierMask;
|
||||
}
|
||||
|
||||
- (CPString)keyEquivalentStringRepresentation
|
||||
{
|
||||
if (![_keyEquivalent length])
|
||||
return @"";
|
||||
|
||||
var string = _keyEquivalent.toUpperCase(),
|
||||
needsShift = _keyEquivalentModifierMask & CPShiftKeyMask || string === _keyEquivalent;
|
||||
|
||||
if (CPBrowserIsOperatingSystem(CPMacOperatingSystem))
|
||||
{
|
||||
if (_keyEquivalentModifierMask & CPCommandKeyMask)
|
||||
string = "⌘" + string;
|
||||
|
||||
if (needsShift)
|
||||
string = "⇧" + string;
|
||||
|
||||
if (_keyEquivalentModifierMask & CPAlternateKeyMask)
|
||||
string = "⌥" + string;
|
||||
|
||||
if (_keyEquivalentModifierMask & CPControlKeyMask)
|
||||
string = "^" + string;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (needsShift)
|
||||
string = "Shift-" + string;
|
||||
|
||||
if (_keyEquivalentModifierMask & CPAlternateKeyMask)
|
||||
string = "Alt-" + string;
|
||||
|
||||
if (_keyEquivalentModifierMask & CPControlKeyMask || _keyEquivalentModifierMask & CPCommandKeyMask)
|
||||
string = "Ctrl-" + string;
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
// Managing Mnemonics
|
||||
/*!
|
||||
Sets the index of the mnemonic character in the title. The character
|
||||
@@ -643,7 +695,7 @@ CPControlKeyMask
|
||||
- (void)setIndentationLevel:(unsigned)aLevel
|
||||
{
|
||||
if (aLevel < 0)
|
||||
[CPException raise:CPInvalidArgumentException reason:"setIndentationLevel: argument must be greater than 0."];
|
||||
[CPException raise:CPInvalidArgumentException reason:"setIndentationLevel: argument must be greater than or equal to 0."];
|
||||
|
||||
_indentationLevel = MIN(15, aLevel);
|
||||
}
|
||||
@@ -742,6 +794,11 @@ CPControlKeyMask
|
||||
return _menuItemView;
|
||||
}
|
||||
|
||||
- (BOOL)_isSelectable
|
||||
{
|
||||
return ![self submenu] || [self action] !== @selector(submenuAction:) || [self target] !== [self menu];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey",
|
||||
@@ -765,6 +822,8 @@ var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey",
|
||||
CPMenuItemKeyEquivalentKey = @"CPMenuItemKeyEquivalentKey",
|
||||
CPMenuItemKeyEquivalentModifierMaskKey = @"CPMenuItemKeyEquivalentModifierMaskKey",
|
||||
|
||||
CPMenuItemIndentationLevelKey = @"CPMenuItemIndentationLevelKey",
|
||||
|
||||
CPMenuItemRepresentedObjectKey = @"CPMenuItemRepresentedObjectKey",
|
||||
CPMenuItemViewKey = @"CPMenuItemViewKey";
|
||||
|
||||
@@ -804,8 +863,9 @@ var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey",
|
||||
// CPImage _offStateImage;
|
||||
// CPImage _mixedStateImage;
|
||||
|
||||
_submenu = DEFAULT_VALUE(CPMenuItemSubmenuKey, nil);
|
||||
// This order matters because setSubmenu: needs _menu to be around.
|
||||
_menu = DEFAULT_VALUE(CPMenuItemMenuKey, nil);
|
||||
[self setSubmenu:DEFAULT_VALUE(CPMenuItemSubmenuKey, nil)];
|
||||
|
||||
_keyEquivalent = [aCoder decodeObjectForKey:CPMenuItemKeyEquivalentKey] || @"";
|
||||
_keyEquivalentModifierMask = [aCoder decodeObjectForKey:CPMenuItemKeyEquivalentModifierMaskKey] || 0;
|
||||
@@ -813,7 +873,9 @@ var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey",
|
||||
// int _mnemonicLocation;
|
||||
|
||||
// BOOL _isAlternate;
|
||||
// int _indentationLevel;
|
||||
|
||||
// Default is 0.
|
||||
[self setIndentationLevel:[aCoder decodeIntForKey:CPMenuItemIndentationLevelKey] || 0];
|
||||
|
||||
// CPString _toolTip;
|
||||
|
||||
@@ -856,423 +918,11 @@ var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey",
|
||||
if (_keyEquivalentModifierMask)
|
||||
[aCoder encodeObject:_keyEquivalentModifierMask forKey:CPMenuItemKeyEquivalentModifierMaskKey];
|
||||
|
||||
if (_indentationLevel > 0)
|
||||
[aCoder encodeInt:_indentationLevel forKey:CPMenuItemIndentationLevelKey];
|
||||
|
||||
ENCODE_IFNOT(CPMenuItemRepresentedObjectKey, _representedObject, nil);
|
||||
ENCODE_IFNOT(CPMenuItemViewKey, _view, nil);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var LEFT_MARGIN = 3.0,
|
||||
RIGHT_MARGIN = 16.0,
|
||||
STATE_COLUMN_WIDTH = 14.0,
|
||||
INDENTATION_WIDTH = 17.0,
|
||||
VERTICAL_MARGIN = 4.0;
|
||||
|
||||
var _CPMenuItemSelectionColor = nil,
|
||||
_CPMenuItemTextShadowColor = nil,
|
||||
|
||||
_CPMenuItemDefaultStateImages = [],
|
||||
_CPMenuItemDefaultStateHighlightedImages = [];
|
||||
|
||||
/*
|
||||
@ignore
|
||||
*/
|
||||
@implementation _CPMenuItemView : CPView
|
||||
{
|
||||
CPMenuItem _menuItem;
|
||||
|
||||
CPFont _font;
|
||||
CPColor _textColor;
|
||||
CPColor _textShadowColor;
|
||||
CPColor _activateColor;
|
||||
CPColor _activateShadowColor;
|
||||
|
||||
CGSize _minSize;
|
||||
BOOL _isDirty;
|
||||
BOOL _showsStateColumn;
|
||||
BOOL _belongsToMenuBar;
|
||||
|
||||
CPImageView _stateView;
|
||||
_CPImageAndTextView _imageAndTextView;
|
||||
CPView _submenuView;
|
||||
}
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
if (self != [_CPMenuItemView class])
|
||||
return;
|
||||
|
||||
_CPMenuItemSelectionColor = [CPColor colorWithCalibratedRed:95.0 / 255.0 green:131.0 / 255.0 blue:185.0 / 255.0 alpha:1.0];
|
||||
_CPMenuItemTextShadowColor = [CPColor colorWithCalibratedRed:26.0 / 255.0 green: 73.0 / 255.0 blue:109.0 / 255.0 alpha:1.0]
|
||||
|
||||
var bundle = [CPBundle bundleForClass:self];
|
||||
|
||||
_CPMenuItemDefaultStateImages[CPOffState] = nil;
|
||||
_CPMenuItemDefaultStateHighlightedImages[CPOffState] = nil;
|
||||
|
||||
_CPMenuItemDefaultStateImages[CPOnState] = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPMenuItem/CPMenuItemOnState.png"] size:CGSizeMake(14.0, 14.0)];
|
||||
_CPMenuItemDefaultStateHighlightedImages[CPOnState] = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPMenuItem/CPMenuItemOnStateHighlighted.png"] size:CGSizeMake(14.0, 14.0)];
|
||||
|
||||
_CPMenuItemDefaultStateImages[CPMixedState] = nil;
|
||||
_CPMenuItemDefaultStateHighlightedImages[CPMixedState] = nil;
|
||||
}
|
||||
|
||||
+ (float)leftMargin
|
||||
{
|
||||
return LEFT_MARGIN + STATE_COLUMN_WIDTH;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame forMenuItem:(CPMenuItem)aMenuItem
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_menuItem = aMenuItem;
|
||||
_showsStateColumn = YES;
|
||||
_isDirty = YES;
|
||||
|
||||
[self setAutoresizingMask:CPViewWidthSizable];
|
||||
|
||||
[self synchronizeWithMenuItem];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CGSize)minSize
|
||||
{
|
||||
return _minSize;
|
||||
}
|
||||
|
||||
- (void)setDirty
|
||||
{
|
||||
_isDirty = YES;
|
||||
}
|
||||
|
||||
- (void)synchronizeWithMenuItem
|
||||
{
|
||||
if (!_isDirty)
|
||||
return;
|
||||
|
||||
_isDirty = NO;
|
||||
|
||||
var view = [_menuItem view];
|
||||
|
||||
if ([_menuItem isSeparatorItem])
|
||||
{
|
||||
var line = [[CPView alloc] initWithFrame:CGRectMake(0.0, 5.0, 10.0, 1.0)];
|
||||
|
||||
view = [[CPView alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, 10.0)];
|
||||
|
||||
[view setAutoresizingMask:CPViewWidthSizable];
|
||||
[line setAutoresizingMask:CPViewWidthSizable];
|
||||
|
||||
[line setBackgroundColor:[CPColor lightGrayColor]];
|
||||
|
||||
[view addSubview:line];
|
||||
}
|
||||
|
||||
if (view)
|
||||
{
|
||||
[_imageAndTextView removeFromSuperview];
|
||||
_imageAndTextView = nil;
|
||||
|
||||
[_stateView removeFromSuperview];
|
||||
_stateView = nil;
|
||||
|
||||
[_submenuView removeFromSuperview];
|
||||
_submenuView = nil;
|
||||
|
||||
_minSize = [view frame].size;
|
||||
|
||||
[self setFrameSize:_minSize];
|
||||
|
||||
[self addSubview:view];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// State Column
|
||||
var x = _belongsToMenuBar ? 0.0 : (LEFT_MARGIN + [_menuItem indentationLevel] * INDENTATION_WIDTH);
|
||||
|
||||
if (_showsStateColumn)
|
||||
{
|
||||
if (!_stateView)
|
||||
{
|
||||
_stateView = [[CPImageView alloc] initWithFrame:CGRectMake(x, (CGRectGetHeight([self frame]) - STATE_COLUMN_WIDTH) / 2.0, STATE_COLUMN_WIDTH, STATE_COLUMN_WIDTH)];
|
||||
|
||||
[_stateView setAutoresizingMask:CPViewMinYMargin | CPViewMaxYMargin];
|
||||
|
||||
[self addSubview:_stateView];
|
||||
}
|
||||
|
||||
var state = [_menuItem state];
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case CPOffState:
|
||||
case CPOnState:
|
||||
case CPMixedState: [_stateView setImage:_CPMenuItemDefaultStateImages[state]];
|
||||
break;
|
||||
|
||||
default: [_stateView setImage:nil];
|
||||
}
|
||||
|
||||
x += STATE_COLUMN_WIDTH;
|
||||
}
|
||||
else
|
||||
{
|
||||
[_stateView removeFromSuperview];
|
||||
|
||||
_stateView = nil;
|
||||
}
|
||||
|
||||
// Image and Title
|
||||
|
||||
if (!_imageAndTextView)
|
||||
{
|
||||
_imageAndTextView = [[_CPImageAndTextView alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, 0.0)];
|
||||
|
||||
[_imageAndTextView setImagePosition:CPImageLeft];
|
||||
[_imageAndTextView setTextShadowOffset:CGSizeMake(0.0, 1.0)];
|
||||
|
||||
[self addSubview:_imageAndTextView];
|
||||
}
|
||||
|
||||
var font = [_menuItem font];
|
||||
|
||||
if (!font)
|
||||
font = _font;
|
||||
|
||||
[_imageAndTextView setFont:font];
|
||||
[_imageAndTextView setVerticalAlignment:CPCenterVerticalTextAlignment];
|
||||
[_imageAndTextView setImage:[_menuItem image]];
|
||||
[_imageAndTextView setText:[_menuItem title]];
|
||||
[_imageAndTextView setTextColor:[self textColor]];
|
||||
[_imageAndTextView setTextShadowColor:[self textShadowColor]];
|
||||
[_imageAndTextView setTextShadowOffset:CGSizeMake(0, 1)];
|
||||
[_imageAndTextView setFrameOrigin:CGPointMake(x, VERTICAL_MARGIN)];
|
||||
[_imageAndTextView sizeToFit];
|
||||
|
||||
var frame = [_imageAndTextView frame];
|
||||
|
||||
// frame.size.height += 1.0;
|
||||
// [_imageAndTextView setFrame:frame];
|
||||
|
||||
frame.size.height += 2 * VERTICAL_MARGIN;
|
||||
|
||||
x += CGRectGetWidth(frame);
|
||||
|
||||
// Submenu Arrow
|
||||
if ([_menuItem hasSubmenu])
|
||||
{
|
||||
x += 3.0;
|
||||
|
||||
if (!_submenuView)
|
||||
{
|
||||
_submenuView = [[_CPMenuItemArrowView alloc] initWithFrame:CGRectMake(0.0, 0.0, 10.0, 10.0)];
|
||||
|
||||
[self addSubview:_submenuView];
|
||||
}
|
||||
|
||||
[_submenuView setHidden:NO];
|
||||
[_submenuView setColor:_belongsToMenuBar ? [self textColor] : nil];
|
||||
[_submenuView setFrameOrigin:CGPointMake(x, (CGRectGetHeight(frame) - 10.0) / 2.0)];
|
||||
|
||||
x += 10.0;
|
||||
}
|
||||
else
|
||||
[_submenuView setHidden:YES];
|
||||
|
||||
_minSize = CGSizeMake(x + (_belongsToMenuBar ? 0.0 : RIGHT_MARGIN) + 3.0, CGRectGetHeight(frame));
|
||||
|
||||
[self setFrameSize:_minSize];
|
||||
}
|
||||
|
||||
- (CGFloat)overlapOffsetWidth
|
||||
{
|
||||
return LEFT_MARGIN + ([[_menuItem menu] showsStateColumn] ? STATE_COLUMN_WIDTH : 0.0);
|
||||
}
|
||||
|
||||
- (void)setShowsStateColumn:(BOOL)shouldShowStateColumn
|
||||
{
|
||||
_showsStateColumn = shouldShowStateColumn;
|
||||
}
|
||||
|
||||
- (void)setBelongsToMenuBar:(BOOL)shouldBelongToMenuBar
|
||||
{
|
||||
_belongsToMenuBar = shouldBelongToMenuBar;
|
||||
}
|
||||
|
||||
- (void)highlight:(BOOL)shouldHighlight
|
||||
{
|
||||
// ASSERT(![_menuItem view]);
|
||||
|
||||
if (_belongsToMenuBar)
|
||||
[_imageAndTextView setImage:shouldHighlight ? [_menuItem alternateImage] : [_menuItem image]];
|
||||
|
||||
else if ([_menuItem isEnabled])
|
||||
{
|
||||
if (shouldHighlight)
|
||||
{
|
||||
[self setBackgroundColor:_CPMenuItemSelectionColor];
|
||||
|
||||
[_imageAndTextView setTextColor:[CPColor whiteColor]];
|
||||
[_imageAndTextView setTextShadowColor:_CPMenuItemTextShadowColor];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self setBackgroundColor:nil];
|
||||
|
||||
[_imageAndTextView setTextColor:[self textColor]];
|
||||
[_imageAndTextView setTextShadowColor:[self textShadowColor]];
|
||||
}
|
||||
|
||||
var state = [_menuItem state];
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case CPOffState:
|
||||
case CPOnState:
|
||||
case CPMixedState: [_stateView setImage:shouldHighlight ? _CPMenuItemDefaultStateHighlightedImages[state] : _CPMenuItemDefaultStateImages[state]];
|
||||
break;
|
||||
|
||||
default: [_stateView setImage:nil];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)activate:(BOOL)shouldActivate
|
||||
{
|
||||
[_imageAndTextView setImage:[_menuItem image]];
|
||||
|
||||
if (shouldActivate)
|
||||
{
|
||||
[_imageAndTextView setTextColor:[self activateColor] || [CPColor whiteColor]];
|
||||
[_imageAndTextView setTextShadowColor:[self activateShadowColor] || [CPColor blackColor]];
|
||||
[_submenuView setColor:[self activateColor] || [CPColor whiteColor]];
|
||||
}
|
||||
else
|
||||
{
|
||||
[_imageAndTextView setTextColor:[self textColor]];
|
||||
[_imageAndTextView setTextShadowColor:[self textShadowColor]];
|
||||
[_submenuView setColor:[self textColor]];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)eventOnSubmenu:(CPEvent)anEvent
|
||||
{
|
||||
if (![_menuItem hasSubmenu])
|
||||
return NO;
|
||||
|
||||
return CGRectContainsPoint([_submenuView frame], [self convertPoint:[anEvent locationInWindow] fromView:nil]);
|
||||
}
|
||||
|
||||
- (BOOL)isHidden
|
||||
{
|
||||
return [_menuItem isHidden];
|
||||
}
|
||||
|
||||
- (CPMenuItem)menuItem
|
||||
{
|
||||
return _menuItem;
|
||||
}
|
||||
|
||||
- (void)setFont:(CPFont)aFont
|
||||
{
|
||||
if (_font == aFont)
|
||||
return;
|
||||
|
||||
_font = aFont;
|
||||
|
||||
[self setDirty];
|
||||
}
|
||||
|
||||
- (void)setTextColor:(CPColor)aColor
|
||||
{
|
||||
if (_textColor == aColor)
|
||||
return;
|
||||
|
||||
_textColor = aColor;
|
||||
|
||||
[_imageAndTextView setTextColor:[self textColor]];
|
||||
[_submenuView setColor:[self textColor]];
|
||||
}
|
||||
|
||||
- (CPColor)textColor
|
||||
{
|
||||
return [_menuItem isEnabled] ? (_textColor ? _textColor : [CPColor colorWithCalibratedRed:70.0 / 255.0 green:69.0 / 255.0 blue:69.0 / 255.0 alpha:1.0]) : [CPColor lightGrayColor];
|
||||
}
|
||||
|
||||
- (void)setTextShadowColor:(CPColor)aColor
|
||||
{
|
||||
if (_textShadowColor == aColor)
|
||||
return;
|
||||
|
||||
_textShadowColor = aColor;
|
||||
|
||||
[_imageAndTextView setTextShadowColor:[self textShadowColor]];
|
||||
//[_submenuView setColor:[self textColor]];
|
||||
}
|
||||
|
||||
- (CPColor)textShadowColor
|
||||
{
|
||||
return [_menuItem isEnabled] ? (_textShadowColor ? _textShadowColor : [CPColor colorWithWhite:1.0 alpha:0.8]) : [CPColor colorWithWhite:0.8 alpha:0.8];
|
||||
}
|
||||
|
||||
- (void)setActivateColor:(CPColor)aColor
|
||||
{
|
||||
_activateColor = aColor;
|
||||
}
|
||||
|
||||
- (CPColor)activateColor
|
||||
{
|
||||
return _activateColor;
|
||||
}
|
||||
|
||||
- (void)setActivateShadowColor:(CPColor)aColor
|
||||
{
|
||||
_activateShadowColor = aColor;
|
||||
}
|
||||
|
||||
- (CPColor)activateShadowColor
|
||||
{
|
||||
return _activateShadowColor;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPMenuItemArrowView : CPView
|
||||
{
|
||||
CPColor _color;
|
||||
}
|
||||
|
||||
- (void)setColor:(CPColor)aColor
|
||||
{
|
||||
if (_color == aColor)
|
||||
return;
|
||||
|
||||
_color = aColor;
|
||||
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)aRect
|
||||
{
|
||||
var context = [[CPGraphicsContext currentContext] graphicsPort];
|
||||
|
||||
CGContextBeginPath(context);
|
||||
|
||||
CGContextMoveToPoint(context, 1.0, 4.0);
|
||||
CGContextAddLineToPoint(context, 9.0, 4.0);
|
||||
CGContextAddLineToPoint(context, 5.0, 8.0);
|
||||
CGContextAddLineToPoint(context, 1.0, 4.0);
|
||||
|
||||
CGContextClosePath(context);
|
||||
|
||||
CGContextSetFillColor(context, _color);
|
||||
CGContextFillPath(context);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,232 @@
|
||||
var HORIZONTAL_MARGIN = 8.0,
|
||||
SUBMENU_INDICATOR_MARGIN = 3.0,
|
||||
VERTICAL_MARGIN = 4.0;
|
||||
|
||||
var SelectionColor = nil,
|
||||
SUBMENU_INDICATOR_COLOR = nil,
|
||||
_CPMenuItemSelectionColor = nil,
|
||||
_CPMenuItemTextShadowColor = nil,
|
||||
|
||||
_CPMenuItemDefaultStateImages = [],
|
||||
_CPMenuItemDefaultStateHighlightedImages = [];
|
||||
|
||||
@implementation _CPMenuItemMenuBarView : CPView
|
||||
{
|
||||
CPMenuItem _menuItem @accessors(property=menuItem);
|
||||
|
||||
CPFont _font;
|
||||
CPColor _textColor;
|
||||
CPColor _textShadowColor;
|
||||
|
||||
BOOL _isDirty;
|
||||
|
||||
_CPImageAndTextView _imageAndTextView;
|
||||
CPView _submenuIndicatorView;
|
||||
}
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
if (self !== [_CPMenuItemMenuBarView class])
|
||||
return;
|
||||
|
||||
var bundle = [CPBundle bundleForClass:self];
|
||||
|
||||
SelectionColor = [CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuBarWindow/_CPMenuBarWindowBackgroundSelected.png"] size:CGSizeMake(1.0, 28.0)]];
|
||||
|
||||
SUBMENU_INDICATOR_COLOR = [CPColor grayColor];
|
||||
|
||||
_CPMenuItemSelectionColor = [CPColor colorWithCalibratedRed:95.0 / 255.0 green:131.0 / 255.0 blue:185.0 / 255.0 alpha:1.0];
|
||||
_CPMenuItemTextShadowColor = [CPColor colorWithCalibratedRed:26.0 / 255.0 green: 73.0 / 255.0 blue:109.0 / 255.0 alpha:1.0];
|
||||
|
||||
_CPMenuItemDefaultStateImages[CPOffState] = nil;
|
||||
_CPMenuItemDefaultStateHighlightedImages[CPOffState] = nil;
|
||||
|
||||
_CPMenuItemDefaultStateImages[CPOnState] = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPMenuItem/CPMenuItemOnState.png"] size:CGSizeMake(14.0, 14.0)];
|
||||
_CPMenuItemDefaultStateHighlightedImages[CPOnState] = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPMenuItem/CPMenuItemOnStateHighlighted.png"] size:CGSizeMake(14.0, 14.0)];
|
||||
|
||||
_CPMenuItemDefaultStateImages[CPMixedState] = nil;
|
||||
_CPMenuItemDefaultStateHighlightedImages[CPMixedState] = nil;
|
||||
}
|
||||
|
||||
+ (id)view
|
||||
{
|
||||
return [[self alloc] init];
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_imageAndTextView = [[_CPImageAndTextView alloc] initWithFrame:CGRectMake(HORIZONTAL_MARGIN, 0.0, 0.0, 0.0)];
|
||||
|
||||
[_imageAndTextView setImagePosition:CPImageLeft];
|
||||
[_imageAndTextView setTextShadowOffset:CGSizeMake(0.0, 1.0)];
|
||||
[_imageAndTextView setAutoresizingMask:CPViewMinYMargin | CPViewMaxYMargin];
|
||||
|
||||
[self addSubview:_imageAndTextView];
|
||||
|
||||
_submenuIndicatorView = [[_CPMenuItemMenuBarSubmenuIndicatorView alloc] initWithFrame:CGRectMake(0.0, 0.0, 9.0, 6.0)];
|
||||
|
||||
[_submenuIndicatorView setAutoresizingMask:CPViewMinYMargin | CPViewMaxYMargin];
|
||||
|
||||
[self addSubview:_submenuIndicatorView];
|
||||
|
||||
[self setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CPColor)textColor
|
||||
{
|
||||
if (![_menuItem isEnabled])
|
||||
return [CPColor lightGrayColor];
|
||||
|
||||
return _textColor || [CPColor colorWithCalibratedRed:70.0 / 255.0 green:69.0 / 255.0 blue:69.0 / 255.0 alpha:1.0];
|
||||
}
|
||||
|
||||
- (CPColor)textShadowColor
|
||||
{
|
||||
if (![_menuItem isEnabled])
|
||||
return [CPColor colorWithWhite:0.8 alpha:0.8];
|
||||
|
||||
return _textShadowColor || [CPColor colorWithWhite:1.0 alpha:0.8];
|
||||
}
|
||||
|
||||
- (void)update
|
||||
{
|
||||
var x = HORIZONTAL_MARGIN,
|
||||
height = 0.0;
|
||||
|
||||
[_imageAndTextView setFont:[_menuItem font] || [_CPMenuBarWindow font]];
|
||||
[_imageAndTextView setVerticalAlignment:CPCenterVerticalTextAlignment];
|
||||
[_imageAndTextView setImage:[_menuItem image]];
|
||||
[_imageAndTextView setText:[_menuItem title]];
|
||||
[_imageAndTextView setTextColor:[self textColor]];
|
||||
[_imageAndTextView setTextShadowColor:[self textShadowColor]];
|
||||
[_imageAndTextView setTextShadowOffset:CGSizeMake(0.0, 1.0)];
|
||||
[_imageAndTextView sizeToFit];
|
||||
|
||||
var imageAndTextViewFrame = [_imageAndTextView frame];
|
||||
|
||||
imageAndTextViewFrame.origin.x = x;
|
||||
x += CGRectGetWidth(imageAndTextViewFrame);
|
||||
height = MAX(height, CGRectGetHeight(imageAndTextViewFrame));
|
||||
|
||||
var hasSubmenuIndicator = [_menuItem hasSubmenu] && [_menuItem action];
|
||||
|
||||
if (hasSubmenuIndicator)
|
||||
{
|
||||
[_submenuIndicatorView setHidden:NO];
|
||||
[_submenuIndicatorView setColor:[self textColor]];
|
||||
[_submenuIndicatorView setShadowColor:[self textShadowColor]];
|
||||
|
||||
var submenuViewFrame = [_submenuIndicatorView frame];
|
||||
|
||||
submenuViewFrame.origin.x = x + SUBMENU_INDICATOR_MARGIN;
|
||||
|
||||
x = CGRectGetMaxX(submenuViewFrame);
|
||||
height = MAX(height, CGRectGetHeight(submenuViewFrame));
|
||||
}
|
||||
else
|
||||
[_submenuIndicatorView setHidden:YES];
|
||||
|
||||
height += 2.0 * VERTICAL_MARGIN;
|
||||
|
||||
imageAndTextViewFrame.origin.y = FLOOR((height - CGRectGetHeight(imageAndTextViewFrame)) / 2.0);
|
||||
[_imageAndTextView setFrame:imageAndTextViewFrame];
|
||||
|
||||
if (hasSubmenuIndicator)
|
||||
{
|
||||
submenuViewFrame.origin.y = FLOOR((height - CGRectGetHeight(submenuViewFrame)) / 2.0) + 1.0;
|
||||
[_submenuIndicatorView setFrame:submenuViewFrame];
|
||||
}
|
||||
|
||||
_minSize = CGSizeMake(x + HORIZONTAL_MARGIN, height);
|
||||
|
||||
[self setAutoresizesSubviews:NO];
|
||||
[self setFrameSize:_minSize];
|
||||
[self setAutoresizesSubviews:YES];
|
||||
}
|
||||
|
||||
- (void)highlight:(BOOL)shouldHighlight
|
||||
{
|
||||
// FIXME: This should probably be even throw.
|
||||
if (![_menuItem isEnabled])
|
||||
return;
|
||||
|
||||
if (shouldHighlight)
|
||||
{
|
||||
[self setBackgroundColor:SelectionColor];
|
||||
|
||||
[_imageAndTextView setTextColor:[CPColor whiteColor]];
|
||||
[_imageAndTextView setTextShadowColor:_CPMenuItemTextShadowColor];
|
||||
|
||||
[_submenuIndicatorView setColor:[CPColor whiteColor]];
|
||||
[_submenuIndicatorView setShadowColor:[CPColor colorWithWhite:0.1 alpha:0.7]];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self setBackgroundColor:nil];
|
||||
|
||||
[_imageAndTextView setTextColor:[self textColor]];
|
||||
[_imageAndTextView setTextShadowColor:[self textShadowColor]];
|
||||
|
||||
[_submenuIndicatorView setColor:[self textColor]];
|
||||
[_submenuIndicatorView setShadowColor:[self textShadowColor]];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPMenuItemMenuBarSubmenuIndicatorView : CPView
|
||||
{
|
||||
CPColor _color;
|
||||
CPColor _shadowColor;
|
||||
}
|
||||
|
||||
- (void)setColor:(CPColor)aColor
|
||||
{
|
||||
if (_color === aColor)
|
||||
return;
|
||||
|
||||
_color = aColor;
|
||||
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (void)setShadowColor:(CPColor)aColor
|
||||
{
|
||||
if (_shadowColor === aColor)
|
||||
return;
|
||||
|
||||
_shadowColor = aColor;
|
||||
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)aRect
|
||||
{
|
||||
var context = [[CPGraphicsContext currentContext] graphicsPort],
|
||||
bounds = [self bounds];
|
||||
|
||||
bounds.size.height -= 1.0;
|
||||
bounds.size.width -= 2.0;
|
||||
bounds.origin.x += 1.0;
|
||||
|
||||
CGContextBeginPath(context);
|
||||
|
||||
CGContextMoveToPoint(context, CGRectGetMinX(bounds), CGRectGetMinY(bounds));
|
||||
CGContextAddLineToPoint(context, CGRectGetMaxX(bounds), CGRectGetMinY(bounds));
|
||||
CGContextAddLineToPoint(context, CGRectGetMidX(bounds), CGRectGetMaxY(bounds));
|
||||
|
||||
CGContextClosePath(context);
|
||||
|
||||
CGContextSetShadowWithColor(context, CGSizeMake(0.0, 1.0), 1.1, _shadowColor);
|
||||
CGContextSetFillColor(context, _color);
|
||||
CGContextFillPath(context);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,38 @@
|
||||
|
||||
@import <AppKit/CPView.j>
|
||||
|
||||
|
||||
@implementation _CPMenuItemSeparatorView : CPView
|
||||
{
|
||||
}
|
||||
|
||||
+ (id)view
|
||||
{
|
||||
return [[self alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, 10.0)];
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
if (self)
|
||||
[self setAutoresizingMask:CPViewWidthSizable];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)aRect
|
||||
{
|
||||
var context = [[CPGraphicsContext currentContext] graphicsPort],
|
||||
bounds = [self bounds];
|
||||
|
||||
CGContextBeginPath(context);
|
||||
|
||||
CGContextMoveToPoint(context, CGRectGetMinX(bounds), FLOOR(CGRectGetMidY(bounds)) - 0.5);
|
||||
CGContextAddLineToPoint(context, CGRectGetMaxX(bounds), FLOOR(CGRectGetMidY(bounds)) - 0.5);
|
||||
|
||||
CGContextSetStrokeColor(context, [CPColor lightGrayColor]);
|
||||
CGContextStrokePath(context);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,298 @@
|
||||
var LEFT_MARGIN = 3.0,
|
||||
RIGHT_MARGIN = 14.0 + 3.0,
|
||||
STATE_COLUMN_WIDTH = 14.0,
|
||||
INDENTATION_WIDTH = 17.0,
|
||||
VERTICAL_MARGIN = 4.0,
|
||||
|
||||
RIGHT_COLUMNS_MARGIN = 30.0,
|
||||
KEY_EQUIVALENT_MARGIN = 10.0;
|
||||
|
||||
var SUBMENU_INDICATOR_COLOR = nil,
|
||||
_CPMenuItemSelectionColor = nil,
|
||||
_CPMenuItemTextShadowColor = nil,
|
||||
|
||||
_CPMenuItemDefaultStateImages = [],
|
||||
_CPMenuItemDefaultStateHighlightedImages = [];
|
||||
|
||||
@implementation _CPMenuItemStandardView : CPView
|
||||
{
|
||||
CPMenuItem _menuItem @accessors(property=menuItem);
|
||||
|
||||
CPFont _font;
|
||||
CPColor _textColor;
|
||||
CPColor _textShadowColor;
|
||||
|
||||
CGSize _minSize @accessors(readonly, property=minSize);
|
||||
BOOL _isDirty;
|
||||
|
||||
CPImageView _stateView;
|
||||
_CPImageAndTextView _imageAndTextView;
|
||||
_CPImageAndTextView _keyEquivalentView;
|
||||
CPView _submenuIndicatorView;
|
||||
}
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
if (self !== [_CPMenuItemStandardView class])
|
||||
return;
|
||||
|
||||
SUBMENU_INDICATOR_COLOR = [CPColor grayColor];
|
||||
|
||||
_CPMenuItemSelectionColor = [CPColor colorWithCalibratedRed:95.0 / 255.0 green:131.0 / 255.0 blue:185.0 / 255.0 alpha:1.0];
|
||||
_CPMenuItemTextShadowColor = [CPColor colorWithCalibratedRed:26.0 / 255.0 green: 73.0 / 255.0 blue:109.0 / 255.0 alpha:1.0]
|
||||
|
||||
var bundle = [CPBundle bundleForClass:self];
|
||||
|
||||
_CPMenuItemDefaultStateImages[CPOffState] = nil;
|
||||
_CPMenuItemDefaultStateHighlightedImages[CPOffState] = nil;
|
||||
|
||||
_CPMenuItemDefaultStateImages[CPOnState] = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPMenuItem/CPMenuItemOnState.png"] size:CGSizeMake(14.0, 14.0)];
|
||||
_CPMenuItemDefaultStateHighlightedImages[CPOnState] = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPMenuItem/CPMenuItemOnStateHighlighted.png"] size:CGSizeMake(14.0, 14.0)];
|
||||
|
||||
_CPMenuItemDefaultStateImages[CPMixedState] = nil;
|
||||
_CPMenuItemDefaultStateHighlightedImages[CPMixedState] = nil;
|
||||
}
|
||||
|
||||
+ (id)view
|
||||
{
|
||||
return [[self alloc] init];
|
||||
}
|
||||
|
||||
+ (float)_standardLeftMargin
|
||||
{
|
||||
return LEFT_MARGIN + STATE_COLUMN_WIDTH;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_stateView = [[CPImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, 0.0)];
|
||||
|
||||
[_stateView setImageScaling:CPScaleNone];
|
||||
|
||||
[self addSubview:_stateView];
|
||||
|
||||
_imageAndTextView = [[_CPImageAndTextView alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, 0.0)];
|
||||
|
||||
[_imageAndTextView setImagePosition:CPImageLeft];
|
||||
[_imageAndTextView setTextShadowOffset:CGSizeMake(0.0, 1.0)];
|
||||
|
||||
[self addSubview:_imageAndTextView];
|
||||
|
||||
_keyEquivalentView = [[_CPImageAndTextView alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, 0.0)];
|
||||
|
||||
[_keyEquivalentView setImagePosition:CPNoImage];
|
||||
[_keyEquivalentView setTextShadowOffset:CGSizeMake(0.0, 1.0)];
|
||||
[_keyEquivalentView setAutoresizingMask:CPViewMinXMargin];
|
||||
|
||||
[self addSubview:_keyEquivalentView];
|
||||
|
||||
_submenuIndicatorView = [[_CPMenuItemSubmenuIndicatorView alloc] initWithFrame:CGRectMake(0.0, 0.0, 8.0, 10.0)];
|
||||
|
||||
[_submenuIndicatorView setColor:SUBMENU_INDICATOR_COLOR];
|
||||
[_submenuIndicatorView setAutoresizingMask:CPViewMinXMargin];
|
||||
|
||||
[self addSubview:_submenuIndicatorView];
|
||||
|
||||
[self setAutoresizingMask:CPViewWidthSizable];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CPColor)textColor
|
||||
{
|
||||
if (![_menuItem isEnabled])
|
||||
return [CPColor lightGrayColor];
|
||||
|
||||
return _textColor || [CPColor colorWithCalibratedRed:70.0 / 255.0 green:69.0 / 255.0 blue:69.0 / 255.0 alpha:1.0];
|
||||
}
|
||||
|
||||
- (CPColor)textShadowColor
|
||||
{
|
||||
if (![_menuItem isEnabled])
|
||||
return nil;
|
||||
|
||||
return _textShadowColor || [CPColor colorWithWhite:1.0 alpha:0.8];
|
||||
}
|
||||
|
||||
- (void)setFont:(CPFont)aFont
|
||||
{
|
||||
_font = aFont;
|
||||
}
|
||||
|
||||
- (void)update
|
||||
{
|
||||
var x = LEFT_MARGIN + [_menuItem indentationLevel] * INDENTATION_WIDTH,
|
||||
height = 0.0,
|
||||
hasStateColumn = [[_menuItem menu] showsStateColumn];
|
||||
|
||||
if (hasStateColumn)
|
||||
{
|
||||
[_stateView setHidden:NO];
|
||||
[_stateView setImage:_CPMenuItemDefaultStateImages[[_menuItem state]] || nil];
|
||||
|
||||
x += STATE_COLUMN_WIDTH;
|
||||
}
|
||||
else
|
||||
[_stateView setHidden:YES];
|
||||
|
||||
[_imageAndTextView setFont:[_menuItem font] || _font];
|
||||
[_imageAndTextView setVerticalAlignment:CPCenterVerticalTextAlignment];
|
||||
[_imageAndTextView setImage:[_menuItem image]];
|
||||
[_imageAndTextView setText:[_menuItem title]];
|
||||
[_imageAndTextView setTextColor:[self textColor]];
|
||||
[_imageAndTextView setTextShadowColor:[self textShadowColor]];
|
||||
[_imageAndTextView setTextShadowOffset:CGSizeMake(0.0, 1.0)];
|
||||
[_imageAndTextView sizeToFit];
|
||||
|
||||
var imageAndTextViewFrame = [_imageAndTextView frame];
|
||||
|
||||
imageAndTextViewFrame.origin.x = x;
|
||||
x += CGRectGetWidth(imageAndTextViewFrame);
|
||||
height = MAX(height, CGRectGetHeight(imageAndTextViewFrame));
|
||||
|
||||
var hasKeyEquivalent = !![_menuItem keyEquivalent],
|
||||
hasSubmenu = [_menuItem hasSubmenu];
|
||||
|
||||
if (hasKeyEquivalent || hasSubmenu)
|
||||
x += RIGHT_COLUMNS_MARGIN;
|
||||
|
||||
if (hasKeyEquivalent)
|
||||
{
|
||||
[_keyEquivalentView setFont:[_menuItem font] || _font];
|
||||
[_keyEquivalentView setVerticalAlignment:CPCenterVerticalTextAlignment];
|
||||
[_keyEquivalentView setImage:[_menuItem image]];
|
||||
[_keyEquivalentView setText:[_menuItem keyEquivalentStringRepresentation]];
|
||||
[_keyEquivalentView setTextColor:[self textColor]];
|
||||
[_keyEquivalentView setTextShadowColor:[self textShadowColor]];
|
||||
[_keyEquivalentView setTextShadowOffset:CGSizeMake(0, 1)];
|
||||
[_keyEquivalentView setFrameOrigin:CGPointMake(x, VERTICAL_MARGIN)];
|
||||
[_keyEquivalentView sizeToFit];
|
||||
|
||||
var keyEquivalentViewFrame = [_keyEquivalentView frame];
|
||||
|
||||
keyEquivalentViewFrame.origin.x = x;
|
||||
x += CGRectGetWidth(keyEquivalentViewFrame);
|
||||
height = MAX(height, CGRectGetHeight(keyEquivalentViewFrame));
|
||||
|
||||
if (hasSubmenu)
|
||||
x += RIGHT_COLUMNS_MARGIN;
|
||||
}
|
||||
else
|
||||
[_keyEquivalentView setHidden:YES];
|
||||
|
||||
if (hasSubmenu)
|
||||
{
|
||||
[_submenuIndicatorView setHidden:NO];
|
||||
|
||||
var submenuViewFrame = [_submenuIndicatorView frame];
|
||||
|
||||
submenuViewFrame.origin.x = x;
|
||||
|
||||
x += CGRectGetWidth(submenuViewFrame);
|
||||
height = MAX(height, CGRectGetHeight(submenuViewFrame));
|
||||
}
|
||||
else
|
||||
[_submenuIndicatorView setHidden:YES];
|
||||
|
||||
height += 2.0 * VERTICAL_MARGIN;
|
||||
|
||||
imageAndTextViewFrame.origin.y = FLOOR((height - CGRectGetHeight(imageAndTextViewFrame)) / 2.0);
|
||||
[_imageAndTextView setFrame:imageAndTextViewFrame];
|
||||
|
||||
if (hasStateColumn)
|
||||
[_stateView setFrameSize:CGSizeMake(STATE_COLUMN_WIDTH, height)];
|
||||
|
||||
if (hasKeyEquivalent)
|
||||
{
|
||||
keyEquivalentViewFrame.origin.y = FLOOR((height - CGRectGetHeight(keyEquivalentViewFrame)) / 2.0);
|
||||
[_keyEquivalentView setFrame:keyEquivalentViewFrame];
|
||||
}
|
||||
|
||||
if (hasSubmenu)
|
||||
{
|
||||
submenuViewFrame.origin.y = FLOOR((height - CGRectGetHeight(submenuViewFrame)) / 2.0);
|
||||
[_submenuIndicatorView setFrame:submenuViewFrame];
|
||||
}
|
||||
|
||||
_minSize = CGSizeMake(x + RIGHT_MARGIN, height);
|
||||
|
||||
[self setAutoresizesSubviews:NO];
|
||||
[self setFrameSize:_minSize];
|
||||
[self setAutoresizesSubviews:YES];
|
||||
}
|
||||
|
||||
- (void)highlight:(BOOL)shouldHighlight
|
||||
{
|
||||
// FIXME: This should probably be even throw.
|
||||
if (![_menuItem isEnabled])
|
||||
return;
|
||||
|
||||
if (shouldHighlight)
|
||||
{
|
||||
[self setBackgroundColor:_CPMenuItemSelectionColor];
|
||||
|
||||
[_imageAndTextView setTextColor:[CPColor whiteColor]];
|
||||
[_keyEquivalentView setTextColor:[CPColor whiteColor]];
|
||||
[_submenuIndicatorView setColor:[CPColor whiteColor]];
|
||||
|
||||
[_imageAndTextView setTextShadowColor:_CPMenuItemTextShadowColor];
|
||||
[_keyEquivalentView setTextShadowColor:_CPMenuItemTextShadowColor];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self setBackgroundColor:nil];
|
||||
|
||||
[_imageAndTextView setTextColor:[self textColor]];
|
||||
[_keyEquivalentView setTextColor:[self textColor]];
|
||||
[_submenuIndicatorView setColor:SUBMENU_INDICATOR_COLOR];
|
||||
|
||||
[_imageAndTextView setTextShadowColor:[self textShadowColor]];
|
||||
[_keyEquivalentView setTextShadowColor:[self textShadowColor]];
|
||||
}
|
||||
|
||||
if (shouldHighlight)
|
||||
[_stateView setImage:_CPMenuItemDefaultStateHighlightedImages[[_menuItem state]] || nil];
|
||||
else
|
||||
[_stateView setImage:_CPMenuItemDefaultStateImages[[_menuItem state]] || nil];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPMenuItemSubmenuIndicatorView : CPView
|
||||
{
|
||||
CPColor _color;
|
||||
}
|
||||
|
||||
- (void)setColor:(CPColor)aColor
|
||||
{
|
||||
if (_color === aColor)
|
||||
return;
|
||||
|
||||
_color = aColor;
|
||||
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)aRect
|
||||
{
|
||||
var context = [[CPGraphicsContext currentContext] graphicsPort],
|
||||
bounds = [self bounds];
|
||||
|
||||
CGContextBeginPath(context);
|
||||
|
||||
CGContextMoveToPoint(context, CGRectGetMinX(bounds), CGRectGetMinY(bounds));
|
||||
CGContextAddLineToPoint(context, CGRectGetMaxX(bounds), CGRectGetMidY(bounds));
|
||||
CGContextAddLineToPoint(context, CGRectGetMinX(bounds), CGRectGetMaxY(bounds));
|
||||
|
||||
CGContextClosePath(context);
|
||||
|
||||
CGContextSetFillColor(context, _color);
|
||||
CGContextFillPath(context);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,273 @@
|
||||
|
||||
@import "_CPMenuItemSeparatorView.j"
|
||||
@import "_CPMenuItemStandardView.j"
|
||||
@import "_CPMenuItemMenuBarView.j"
|
||||
|
||||
|
||||
var LEFT_MARGIN = 3.0,
|
||||
RIGHT_MARGIN = 16.0,
|
||||
STATE_COLUMN_WIDTH = 14.0,
|
||||
INDENTATION_WIDTH = 17.0,
|
||||
VERTICAL_MARGIN = 4.0;
|
||||
|
||||
var _CPMenuItemSelectionColor = nil,
|
||||
_CPMenuItemTextShadowColor = nil,
|
||||
|
||||
_CPMenuItemDefaultStateImages = [],
|
||||
_CPMenuItemDefaultStateHighlightedImages = [];
|
||||
|
||||
/*
|
||||
@ignore
|
||||
*/
|
||||
@implementation _CPMenuItemView : CPView
|
||||
{
|
||||
CPMenuItem _menuItem;
|
||||
CPView _view;
|
||||
|
||||
CPFont _font;
|
||||
CPColor _textColor;
|
||||
CPColor _textShadowColor;
|
||||
|
||||
CGSize _minSize;
|
||||
BOOL _isDirty;
|
||||
BOOL _showsStateColumn;
|
||||
|
||||
CPImageView _stateView;
|
||||
_CPImageAndTextView _imageAndTextView;
|
||||
CPView _submenuView;
|
||||
}
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
if (self != [_CPMenuItemView class])
|
||||
return;
|
||||
|
||||
_CPMenuItemSelectionColor = [CPColor colorWithCalibratedRed:95.0 / 255.0 green:131.0 / 255.0 blue:185.0 / 255.0 alpha:1.0];
|
||||
_CPMenuItemTextShadowColor = [CPColor colorWithCalibratedRed:26.0 / 255.0 green: 73.0 / 255.0 blue:109.0 / 255.0 alpha:1.0]
|
||||
|
||||
var bundle = [CPBundle bundleForClass:self];
|
||||
|
||||
_CPMenuItemDefaultStateImages[CPOffState] = nil;
|
||||
_CPMenuItemDefaultStateHighlightedImages[CPOffState] = nil;
|
||||
|
||||
_CPMenuItemDefaultStateImages[CPOnState] = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPMenuItem/CPMenuItemOnState.png"] size:CGSizeMake(14.0, 14.0)];
|
||||
_CPMenuItemDefaultStateHighlightedImages[CPOnState] = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPMenuItem/CPMenuItemOnStateHighlighted.png"] size:CGSizeMake(14.0, 14.0)];
|
||||
|
||||
_CPMenuItemDefaultStateImages[CPMixedState] = nil;
|
||||
_CPMenuItemDefaultStateHighlightedImages[CPMixedState] = nil;
|
||||
}
|
||||
|
||||
+ (float)leftMargin
|
||||
{
|
||||
return LEFT_MARGIN + STATE_COLUMN_WIDTH;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame forMenuItem:(CPMenuItem)aMenuItem
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_menuItem = aMenuItem;
|
||||
_showsStateColumn = YES;
|
||||
_isDirty = YES;
|
||||
|
||||
[self setAutoresizingMask:CPViewWidthSizable];
|
||||
|
||||
[self synchronizeWithMenuItem];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CGSize)minSize
|
||||
{
|
||||
return _minSize;
|
||||
}
|
||||
|
||||
- (void)setDirty
|
||||
{
|
||||
_isDirty = YES;
|
||||
}
|
||||
|
||||
- (void)synchronizeWithMenuItem
|
||||
{
|
||||
var menuItemView = [_menuItem view];
|
||||
|
||||
if ([_menuItem isSeparatorItem])
|
||||
{
|
||||
if (![_view isKindOfClass:[_CPMenuItemSeparatorView class]])
|
||||
{
|
||||
[_view removeFromSuperview];
|
||||
_view = [_CPMenuItemSeparatorView view];
|
||||
}
|
||||
}
|
||||
else if (menuItemView)
|
||||
{
|
||||
if (_view !== menuItemView)
|
||||
{
|
||||
[_view removeFromSuperview];
|
||||
_view = menuItemView;
|
||||
}
|
||||
}
|
||||
|
||||
else if ([_menuItem menu] == [CPApp mainMenu])
|
||||
{
|
||||
if (![_view isKindOfClass:[_CPMenuItemMenuBarView class]])
|
||||
{
|
||||
[_view removeFromSuperview];
|
||||
_view = [_CPMenuItemMenuBarView view];
|
||||
}
|
||||
|
||||
[_view setMenuItem:_menuItem];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (![_view isKindOfClass:[_CPMenuItemStandardView class]])
|
||||
{
|
||||
[_view removeFromSuperview];
|
||||
_view = [_CPMenuItemStandardView view];
|
||||
}
|
||||
|
||||
[_view setMenuItem:_menuItem];
|
||||
}
|
||||
|
||||
if ([_view superview] !== self)
|
||||
[self addSubview:_view];
|
||||
|
||||
if ([_view respondsToSelector:@selector(update)])
|
||||
[_view update];
|
||||
|
||||
_minSize = [_view frame].size;
|
||||
[self setAutoresizesSubviews:NO];
|
||||
[self setFrameSize:_minSize];
|
||||
[self setAutoresizesSubviews:YES];
|
||||
}
|
||||
|
||||
- (void)setShowsStateColumn:(BOOL)shouldShowStateColumn
|
||||
{
|
||||
_showsStateColumn = shouldShowStateColumn;
|
||||
}
|
||||
|
||||
- (void)highlight:(BOOL)shouldHighlight
|
||||
{
|
||||
if ([_view respondsToSelector:@selector(highlight:)])
|
||||
[_view highlight:shouldHighlight];
|
||||
}
|
||||
|
||||
- (void)activate:(BOOL)shouldActivate
|
||||
{
|
||||
[_imageAndTextView setImage:[_menuItem image]];
|
||||
|
||||
if (shouldActivate)
|
||||
{
|
||||
[_imageAndTextView setTextColor:[self activateColor] || [CPColor whiteColor]];
|
||||
[_imageAndTextView setTextShadowColor:[self activateShadowColor] || [CPColor blackColor]];
|
||||
[_submenuView setColor:[self activateColor] || [CPColor whiteColor]];
|
||||
}
|
||||
else
|
||||
{
|
||||
[_imageAndTextView setTextColor:[self textColor]];
|
||||
[_imageAndTextView setTextShadowColor:[self textShadowColor]];
|
||||
[_submenuView setColor:[self textColor]];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)eventOnSubmenu:(CPEvent)anEvent
|
||||
{
|
||||
if (![_menuItem hasSubmenu])
|
||||
return NO;
|
||||
|
||||
return CGRectContainsPoint([_submenuView frame], [self convertPoint:[anEvent locationInWindow] fromView:nil]);
|
||||
}
|
||||
|
||||
- (BOOL)isHidden
|
||||
{
|
||||
return [_menuItem isHidden];
|
||||
}
|
||||
|
||||
- (CPMenuItem)menuItem
|
||||
{
|
||||
return _menuItem;
|
||||
}
|
||||
|
||||
- (void)setFont:(CPFont)aFont
|
||||
{
|
||||
if (_font === aFont)
|
||||
return;
|
||||
|
||||
_font = aFont;
|
||||
|
||||
if ([_view respondsToSelector:@selector(setFont:)])
|
||||
[_view setFont:aFont];
|
||||
|
||||
[self setDirty];
|
||||
}
|
||||
|
||||
- (void)setTextColor:(CPColor)aColor
|
||||
{
|
||||
if (_textColor == aColor)
|
||||
return;
|
||||
|
||||
_textColor = aColor;
|
||||
|
||||
[_imageAndTextView setTextColor:[self textColor]];
|
||||
[_submenuView setColor:[self textColor]];
|
||||
}
|
||||
|
||||
- (CPColor)textColor
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)setTextShadowColor:(CPColor)aColor
|
||||
{
|
||||
if (_textShadowColor == aColor)
|
||||
return;
|
||||
|
||||
_textShadowColor = aColor;
|
||||
|
||||
[_imageAndTextView setTextShadowColor:[self textShadowColor]];
|
||||
//[_submenuView setColor:[self textColor]];
|
||||
}
|
||||
|
||||
- (CPColor)textShadowColor
|
||||
{
|
||||
return [_menuItem isEnabled] ? (_textShadowColor ? _textShadowColor : [CPColor colorWithWhite:1.0 alpha:0.8]) : [CPColor colorWithWhite:0.8 alpha:0.8];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPMenuItemArrowView : CPView
|
||||
{
|
||||
CPColor _color;
|
||||
}
|
||||
|
||||
- (void)setColor:(CPColor)aColor
|
||||
{
|
||||
if (_color == aColor)
|
||||
return;
|
||||
|
||||
_color = aColor;
|
||||
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)aRect
|
||||
{
|
||||
var context = [[CPGraphicsContext currentContext] graphicsPort];
|
||||
|
||||
CGContextBeginPath(context);
|
||||
|
||||
CGContextMoveToPoint(context, 1.0, 4.0);
|
||||
CGContextAddLineToPoint(context, 9.0, 4.0);
|
||||
CGContextAddLineToPoint(context, 5.0, 8.0);
|
||||
CGContextAddLineToPoint(context, 1.0, 4.0);
|
||||
|
||||
CGContextClosePath(context);
|
||||
|
||||
CGContextSetFillColor(context, _color);
|
||||
CGContextFillPath(context);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -235,7 +235,7 @@ var CPOutlineViewDataSource_outlineView_setObjectValue_forTableColumn_byItem_
|
||||
return _itemsForRows[aRow] || nil;
|
||||
}
|
||||
|
||||
- (CPInteger)rowForItem:(id)aItem
|
||||
- (CPInteger)rowForItem:(id)anItem
|
||||
{
|
||||
if (!anItem)
|
||||
return _rootItemInfo.row;
|
||||
|
||||
@@ -43,11 +43,14 @@ CPURLPboardType = @"CPURLPboardType";
|
||||
CPImagesPboardType = @"CPImagesPboardType";
|
||||
CPVideosPboardType = @"CPVideosPboardType";
|
||||
|
||||
UTF8PboardType = @"public.utf8-plain-text";
|
||||
|
||||
// Deprecated
|
||||
CPImagePboardType = @"CPImagePboardType";
|
||||
|
||||
|
||||
var CPPasteboards = nil;
|
||||
var CPPasteboards = nil,
|
||||
supportsNativePasteboard = NO;
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@@ -63,6 +66,8 @@ var CPPasteboards = nil;
|
||||
|
||||
unsigned _changeCount;
|
||||
CPString _stateUID;
|
||||
|
||||
WebScriptObject _nativePasteboard;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -76,6 +81,9 @@ var CPPasteboards = nil;
|
||||
[self setVersion:1.0];
|
||||
|
||||
CPPasteboards = [CPDictionary dictionary];
|
||||
|
||||
if (typeof window.cpPasteboardWithName !== "undefined")
|
||||
supportsNativePasteboard = YES;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -94,7 +102,7 @@ var CPPasteboards = nil;
|
||||
+ (id)pasteboardWithName:(CPString)aName
|
||||
{
|
||||
var pasteboard = [CPPasteboards objectForKey:aName];
|
||||
|
||||
|
||||
if (pasteboard)
|
||||
return pasteboard;
|
||||
|
||||
@@ -118,6 +126,12 @@ var CPPasteboards = nil;
|
||||
_provided = [CPDictionary dictionary];
|
||||
|
||||
_changeCount = 0;
|
||||
|
||||
if (supportsNativePasteboard)
|
||||
{
|
||||
_nativePasteboard = window.cpPasteboardWithName(aName);
|
||||
[self _synchronizePasteboard];
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -146,7 +160,16 @@ var CPPasteboards = nil;
|
||||
|
||||
[_owners setObject:anOwner forKey:type];
|
||||
}
|
||||
|
||||
|
||||
if (_nativePasteboard)
|
||||
{
|
||||
var nativeTypes = [types copy];
|
||||
if ([types containsObject:CPStringPboardType])
|
||||
nativeTypes.push(UTF8PboardType);
|
||||
|
||||
_nativePasteboard.addTypes_(nativeTypes);
|
||||
}
|
||||
|
||||
return ++_changeCount;
|
||||
}
|
||||
|
||||
@@ -157,6 +180,12 @@ var CPPasteboards = nil;
|
||||
@return the pasteboard's change count
|
||||
*/
|
||||
- (unsigned)declareTypes:(CPArray)types owner:(id)anOwner
|
||||
{
|
||||
[self _declareTypes:types owner:anOwner updateNativePasteboard:YES];
|
||||
}
|
||||
|
||||
/*! @ignore */
|
||||
- (unsigned)_declareTypes:(CPArray)types owner:(id)anOwner updateNativePasteboard:(BOOL)shouldUpdate
|
||||
{
|
||||
[_types setArray:types];
|
||||
|
||||
@@ -167,7 +196,16 @@ var CPPasteboards = nil;
|
||||
|
||||
while (count--)
|
||||
[_owners setObject:anOwner forKey:_types[count]];
|
||||
|
||||
|
||||
if (_nativePasteboard && shouldUpdate)
|
||||
{
|
||||
var nativeTypes = [types copy];
|
||||
if ([types containsObject:CPStringPboardType])
|
||||
nativeTypes.push(UTF8PboardType);
|
||||
|
||||
_nativePasteboard.declareTypes_(nativeTypes);
|
||||
_changeCount = _nativePasteboard.changeCount();
|
||||
}
|
||||
return ++_changeCount;
|
||||
}
|
||||
|
||||
@@ -180,7 +218,10 @@ var CPPasteboards = nil;
|
||||
- (BOOL)setData:(CPData)aData forType:(CPString)aType
|
||||
{
|
||||
[_provided setObject:aData forKey:aType];
|
||||
|
||||
|
||||
if (aType === CPStringPboardType)
|
||||
[self setData:aData forType:UTF8PboardType];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
@@ -203,7 +244,7 @@ var CPPasteboards = nil;
|
||||
*/
|
||||
- (void)setString:(CPString)aString forType:(CPString)aType
|
||||
{
|
||||
return [self setPropertyList:aString forType:aType];
|
||||
[self setPropertyList:aString forType:aType];
|
||||
}
|
||||
|
||||
// Determining Types
|
||||
@@ -223,6 +264,7 @@ var CPPasteboards = nil;
|
||||
*/
|
||||
- (CPArray)types
|
||||
{
|
||||
[self _synchronizePasteboard];
|
||||
return _types;
|
||||
}
|
||||
|
||||
@@ -251,16 +293,46 @@ var CPPasteboards = nil;
|
||||
|
||||
if (owner)
|
||||
{
|
||||
[owner pasteboard:self provideDataForType:aType];
|
||||
|
||||
++_changeCount;
|
||||
|
||||
[owner pasteboard:self provideDataForType:aType];
|
||||
return [_provided objectForKey:aType];
|
||||
}
|
||||
|
||||
if (aType === CPStringPboardType)
|
||||
return [self dataForType:UTF8PboardType];
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)_synchronizePasteboard
|
||||
{
|
||||
if (_nativePasteboard && _nativePasteboard.changeCount() > _changeCount)
|
||||
{
|
||||
var nativeTypes = [_nativePasteboard.types() copy];
|
||||
if ([nativeTypes containsObject:UTF8PboardType])
|
||||
nativeTypes.push(CPStringPboardType);
|
||||
|
||||
[self _declareTypes:nativeTypes owner:self updateNativePasteboard:NO];
|
||||
|
||||
_changeCount = _nativePasteboard.changeCount();
|
||||
}
|
||||
}
|
||||
|
||||
/*! @ignore
|
||||
method provided for integration with native pasteboard
|
||||
*/
|
||||
- (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType
|
||||
{
|
||||
if (aType === CPStringPboardType)
|
||||
{
|
||||
var string = _nativePasteboard.stringForType_(UTF8PboardType);
|
||||
|
||||
[self setString:string forType:CPStringPboardType];
|
||||
[self setString:string forType:UTF8PboardType];
|
||||
}
|
||||
else
|
||||
[self setString:_nativePasteboard.stringForType_(aType) forType:aType];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the property list for the specified data type
|
||||
@param aType the requested data type
|
||||
|
||||
@@ -638,21 +638,49 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
{
|
||||
if (![self isEnabled] || ![self numberOfItems])
|
||||
return;
|
||||
|
||||
|
||||
[self highlight:YES];
|
||||
|
||||
var menu = [self menu],
|
||||
theWindow = [self window],
|
||||
menuWindow = [_CPMenuWindow menuWindowWithMenu:menu font:[self font]];
|
||||
|
||||
[menuWindow setDelegate:self];
|
||||
[menuWindow setBackgroundStyle:_CPMenuWindowPopUpBackgroundStyle];
|
||||
|
||||
// Pull Down Menus show up directly below their buttons.
|
||||
bounds = [self bounds],
|
||||
minimumWidth = CGRectGetWidth(bounds);
|
||||
|
||||
// FIXME: setFont: should set the font on the menu.
|
||||
[menu setFont:[self font]];
|
||||
|
||||
if ([self pullsDown])
|
||||
var menuOrigin = [theWindow convertBaseToGlobal:[self convertPoint:CGPointMake(0.0, CGRectGetMaxY([self bounds])) toView:nil]];
|
||||
|
||||
// Pop Up Menus attempt to show up "on top" of the selected item.
|
||||
{
|
||||
var positionedItem = nil,
|
||||
location = CGPointMake(0.0, CGRectGetMaxY(bounds));
|
||||
}
|
||||
else
|
||||
{
|
||||
var contentRect = [self contentRectForBounds:bounds],
|
||||
positionedItem = [self selectedItem],
|
||||
standardLeftMargin = [_CPMenuWindow _standardLeftMargin] + [_CPMenuItemStandardView _standardLeftMargin],
|
||||
location = CGPointMake(CGRectGetMinX(contentRect) - standardLeftMargin, 0.0);
|
||||
|
||||
minimumWidth += standardLeftMargin;
|
||||
}
|
||||
|
||||
[menu setMinimumWidth:minimumWidth];
|
||||
|
||||
[menu
|
||||
_popUpMenuPositioningItem:positionedItem
|
||||
atLocation:location
|
||||
topY:CGRectGetMinY(bounds)
|
||||
bottomY:CGRectGetMaxY(bounds)
|
||||
inView:self
|
||||
callback:function(aMenu)
|
||||
{
|
||||
[self highlight:NO];
|
||||
|
||||
var highlightedItem = [aMenu highlightedItem];
|
||||
|
||||
if ([highlightedItem _isSelectable])
|
||||
[self selectItem:highlightedItem];
|
||||
}];
|
||||
/*
|
||||
else
|
||||
{
|
||||
// This is confusing, I KNOW, so let me explain it to you.
|
||||
@@ -660,43 +688,14 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
// 1. So calculate where our content is, then calculate where the menu item is.
|
||||
// 2. Move LEFT by whatever indentation we have (offsetWidths, aka, window margin, item margin, etc).
|
||||
// 3. MOVE UP by the difference in sizes of the content and menu item, this will only work if the content is vertically centered.
|
||||
var contentRect = [self convertRect:[self contentRectForBounds:[self bounds]] toView:nil],
|
||||
var contentRect = [self convertRect:[self contentRectForBounds:bounds] toView:nil],
|
||||
menuOrigin = [theWindow convertBaseToGlobal:contentRect.origin],
|
||||
menuItemRect = [menuWindow rectForItemAtIndex:_selectedIndex];
|
||||
|
||||
|
||||
menuOrigin.x -= CGRectGetMinX(menuItemRect) + [menuWindow overlapOffsetWidth] + [[[menu itemAtIndex:_selectedIndex] _menuItemView] overlapOffsetWidth];
|
||||
menuOrigin.y -= CGRectGetMinY(menuItemRect) + (CGRectGetHeight(menuItemRect) - CGRectGetHeight(contentRect)) / 2.0;
|
||||
}
|
||||
|
||||
[menuWindow setFrameOrigin:menuOrigin];
|
||||
|
||||
var menuMaxX = CGRectGetMaxX([menuWindow frame]),
|
||||
buttonMaxX = [theWindow convertBaseToGlobal:CGPointMake(CGRectGetMaxX([self convertRect:[self bounds] toView:nil]), 0.0)].x;
|
||||
|
||||
if (menuMaxX < buttonMaxX)
|
||||
[menuWindow setMinWidth:CGRectGetWidth([menuWindow frame]) + buttonMaxX - menuMaxX - ([self pullsDown] ? 0.0 : VISIBLE_MARGIN)];
|
||||
|
||||
[menuWindow orderFront:self];
|
||||
[menuWindow beginTrackingWithEvent:anEvent sessionDelegate:self didEndSelector:@selector(menuWindowDidFinishTracking:highlightedItem:)];
|
||||
}
|
||||
|
||||
/*
|
||||
@ignore
|
||||
*/
|
||||
- (void)menuWindowDidFinishTracking:(_CPMenuWindow)aMenuWindow highlightedItem:(CPMenuItem)aMenuItem
|
||||
{
|
||||
[_CPMenuWindow poolMenuWindow:aMenuWindow];
|
||||
|
||||
[self highlight:NO];
|
||||
|
||||
var index = [_menu indexOfItem:aMenuItem];
|
||||
|
||||
if (index == CPNotFound)
|
||||
return;
|
||||
|
||||
[self selectItemAtIndex:index];
|
||||
|
||||
[CPApp sendAction:[aMenuItem action] to:[aMenuItem target] from:aMenuItem];
|
||||
}
|
||||
|
||||
- (void)_popUpItemAction:(id)aSender
|
||||
|
||||
@@ -175,6 +175,9 @@ var CPRadioRadioGroupKey = @"CPRadioRadioGroupKey";
|
||||
{
|
||||
CPSet _radios;
|
||||
CPRadio _selectedRadio;
|
||||
|
||||
id _target @accessors(property=target);
|
||||
SEL _action @accessors(property=action);
|
||||
}
|
||||
|
||||
- (id)init
|
||||
@@ -213,6 +216,8 @@ var CPRadioRadioGroupKey = @"CPRadioRadioGroupKey";
|
||||
|
||||
[_selectedRadio setState:CPOffState];
|
||||
_selectedRadio = aRadio;
|
||||
|
||||
[CPApp sendAction:_action to:_target from:self];
|
||||
}
|
||||
|
||||
- (CPRadio)selectedRadio
|
||||
|
||||
@@ -716,15 +716,16 @@
|
||||
|
||||
@end
|
||||
|
||||
var CPScrollViewContentViewKey = "CPScrollViewContentView",
|
||||
CPScrollViewVLineScrollKey = "CPScrollViewVLineScroll",
|
||||
CPScrollViewHLineScrollKey = "CPScrollViewHLineScroll",
|
||||
CPScrollViewVPageScrollKey = "CPScrollViewVPageScroll",
|
||||
CPScrollViewHPageScrollKey = "CPScrollViewHPageScroll",
|
||||
CPScrollViewHasVScrollerKey = "CPScrollViewHasVScroller",
|
||||
CPScrollViewHasHScrollerKey = "CPScrollViewHasHScroller",
|
||||
CPScrollViewVScrollerKey = "CPScrollViewVScroller",
|
||||
CPScrollViewHScrollerKey = "CPScrollViewHScroller",
|
||||
var CPScrollViewContentViewKey = "CPScrollViewContentView",
|
||||
CPScrollViewHeaderClipViewKey = "CPScrollViewHeaderClipViewKey",
|
||||
CPScrollViewVLineScrollKey = "CPScrollViewVLineScroll",
|
||||
CPScrollViewHLineScrollKey = "CPScrollViewHLineScroll",
|
||||
CPScrollViewVPageScrollKey = "CPScrollViewVPageScroll",
|
||||
CPScrollViewHPageScrollKey = "CPScrollViewHPageScroll",
|
||||
CPScrollViewHasVScrollerKey = "CPScrollViewHasVScroller",
|
||||
CPScrollViewHasHScrollerKey = "CPScrollViewHasHScroller",
|
||||
CPScrollViewVScrollerKey = "CPScrollViewVScroller",
|
||||
CPScrollViewHScrollerKey = "CPScrollViewHScroller",
|
||||
CPScrollViewAutohidesScrollerKey = "CPScrollViewAutohidesScroller";
|
||||
|
||||
@implementation CPScrollView (CPCoding)
|
||||
@@ -740,6 +741,7 @@ var CPScrollViewContentViewKey = "CPScrollViewContentView",
|
||||
_horizontalPageScroll = [aCoder decodeFloatForKey:CPScrollViewHPageScrollKey];
|
||||
|
||||
_contentView = [aCoder decodeObjectForKey:CPScrollViewContentViewKey];
|
||||
_headerClipView = [aCoder decodeObjectForKey:CPScrollViewHeaderClipViewKey];
|
||||
|
||||
_verticalScroller = [aCoder decodeObjectForKey:CPScrollViewVScrollerKey];
|
||||
_horizontalScroller = [aCoder decodeObjectForKey:CPScrollViewHScrollerKey];
|
||||
@@ -760,7 +762,8 @@ var CPScrollViewContentViewKey = "CPScrollViewContentView",
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
[aCoder encodeObject:_contentView forKey:CPScrollViewContentViewKey];
|
||||
|
||||
[aCoder encodeObject:_headerClipView forKey:CPScrollViewHeaderClipViewKey];
|
||||
|
||||
[aCoder encodeObject:_verticalScroller forKey:CPScrollViewVScrollerKey];
|
||||
[aCoder encodeObject:_horizontalScroller forKey:CPScrollViewHScrollerKey];
|
||||
|
||||
|
||||
@@ -89,40 +89,35 @@ var CPSearchFieldSearchImage = nil,
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self != nil)
|
||||
{
|
||||
_recentSearches = [CPArray array];
|
||||
_maximumRecents = 10;
|
||||
_sendsWholeSearchString = NO;
|
||||
_sendsSearchStringImmediately = NO;
|
||||
|
||||
[self setBezeled:YES];
|
||||
[self setBezelStyle:CPTextFieldRoundedBezel];
|
||||
[self setBordered:YES];
|
||||
[self setEditable:YES];
|
||||
[self setDelegate:self];
|
||||
|
||||
_cancelButton = [[CPButton alloc] initWithFrame:CPMakeRect(frame.size.width - 27,(frame.size.height-22)/2,22,22)];
|
||||
[self resetCancelButton];
|
||||
|
||||
|
||||
[_cancelButton setHidden:YES];
|
||||
[_cancelButton setAutoresizingMask:CPViewMinXMargin];
|
||||
[self addSubview:_cancelButton];
|
||||
|
||||
_searchButton = [[CPButton alloc] initWithFrame:CPMakeRect(5,(frame.size.height-25)/2,25,25)];
|
||||
[_searchButton setBezelStyle:CPRegularSquareBezelStyle];
|
||||
[_searchButton setAutoresizingMask:CPViewMaxXMargin]
|
||||
[_searchButton setBordered:NO];
|
||||
[_searchButton setImageScaling:CPScaleToFit];
|
||||
{
|
||||
_recentSearches = [CPArray array];
|
||||
_maximumRecents = 10;
|
||||
_sendsWholeSearchString = NO;
|
||||
_sendsSearchStringImmediately = NO;
|
||||
_recentsAutosaveName = nil;
|
||||
|
||||
[self setBezeled:YES];
|
||||
[self setBezelStyle:CPTextFieldRoundedBezel];
|
||||
[self setBordered:YES];
|
||||
[self setEditable:YES];
|
||||
[self setDelegate:self];
|
||||
|
||||
_cancelButton = [[CPButton alloc] initWithFrame:CPMakeRect(frame.size.width - 27,(frame.size.height-22)/2,22,22)];
|
||||
[self resetCancelButton];
|
||||
[_cancelButton setHidden:YES];
|
||||
[_cancelButton setAutoresizingMask:CPViewMinXMargin];
|
||||
[self addSubview:_cancelButton];
|
||||
|
||||
_searchButton = [[CPButton alloc] initWithFrame:CPMakeRect(5,(frame.size.height-25)/2,25,25)];
|
||||
[self resetSearchButton];
|
||||
[self addSubview:_searchButton];
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
_cancelButton._DOMElement.style.cursor = "default";
|
||||
_searchButton._DOMElement.style.cursor = "default";
|
||||
#endif
|
||||
|
||||
[self setSearchMenuTemplate:[self _searchMenuTemplate]];
|
||||
[self addSubview:_searchButton];
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
@@ -133,11 +128,13 @@ var CPSearchFieldSearchImage = nil,
|
||||
|
||||
[copy setCancelButton:[_cancelButton copy]];
|
||||
[copy setSearchButton:[_searchButton copy]];
|
||||
[copy setrecentsAutosaveName:[_recentsAutosaveName copy]];
|
||||
[copy setSendsWholeSearchString:[_sendsWholeSearchString copy]];
|
||||
[copy setSendsSearchStringImmediately:[_sendsSearchStringImmediately copy]];
|
||||
[copy setMaximumRecents:_maximumRecents];
|
||||
[copy setSearchMenutemplate:[_searchMenuTemplate copy]];
|
||||
if (_recentsAutosaveName)
|
||||
[copy setrecentsAutosaveName:[_recentsAutosaveName copy]];
|
||||
if (_searchMenuTemplate)
|
||||
[copy setSearchMenutemplate:[_searchMenuTemplate copy]];
|
||||
|
||||
return copy;
|
||||
}
|
||||
@@ -172,11 +169,11 @@ var CPSearchFieldSearchImage = nil,
|
||||
target,
|
||||
button = [self searchButton];
|
||||
|
||||
if (_searchMenuTemplate == nil)
|
||||
if (_searchMenuTemplate === nil)
|
||||
{
|
||||
searchButtonImage = CPSearchFieldSearchImage;
|
||||
action = [self action];
|
||||
target = [self target];
|
||||
action = @selector(_sendAction:);
|
||||
target = self;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -185,7 +182,10 @@ var CPSearchFieldSearchImage = nil,
|
||||
target = self;
|
||||
}
|
||||
|
||||
[button setBordered:NO];
|
||||
[button setImageScaling:CPScaleToFit];
|
||||
[button setImage:searchButtonImage];
|
||||
[button setAutoresizingMask:CPViewMaxXMargin];
|
||||
[button setTarget:target];
|
||||
[button setAction:action];
|
||||
}
|
||||
@@ -215,11 +215,11 @@ var CPSearchFieldSearchImage = nil,
|
||||
- (void)resetCancelButton
|
||||
{
|
||||
var button = [self cancelButton];
|
||||
[button setBezelStyle:CPRegularSquareBezelStyle];
|
||||
[button setBordered:NO];
|
||||
[button setImageScaling:CPScaleToFit];
|
||||
[button setImage:CPSearchFieldCancelImage];
|
||||
[button setAlternateImage:CPSearchFieldCancelPressedImage];
|
||||
[button setAutoresizingMask:CPViewMinXMargin];
|
||||
[button setTarget:self];
|
||||
[button setAction:@selector(_searchFieldCancel:)];
|
||||
}
|
||||
@@ -255,7 +255,7 @@ var CPSearchFieldSearchImage = nil,
|
||||
@param rect The current bounding rectangle for the search button.
|
||||
Subclasses can override this method to return a new bounding rectangle for the search button. You might use this method to provide a custom layout for the search field control.
|
||||
*/
|
||||
- (CPRect)searchButtonRectForBounds:(CPRect)rect // fix
|
||||
- (CPRect)searchButtonRectForBounds:(CPRect)rect
|
||||
{
|
||||
return [_searchButton frame];
|
||||
}
|
||||
@@ -290,6 +290,7 @@ var CPSearchFieldSearchImage = nil,
|
||||
_searchMenuTemplate = menu;
|
||||
|
||||
[self resetSearchButton];
|
||||
[self _loadRecentSearchList];
|
||||
[self _updateSearchMenu];
|
||||
}
|
||||
|
||||
@@ -370,10 +371,10 @@ var CPSearchFieldSearchImage = nil,
|
||||
*/
|
||||
- (void)setRecentSearches:(CPArray)searches
|
||||
{
|
||||
var max = MIN([self maximumRecents],[searches count]);
|
||||
var searches = [searches subarrayWithRange:CPMakeRange(0,max)];
|
||||
_recentSearches = searches;
|
||||
var max = MIN([self maximumRecents], [searches count]),
|
||||
searches = [searches subarrayWithRange:CPMakeRange(0, max)];
|
||||
|
||||
_recentSearches = searches;
|
||||
[self _autosaveRecentSearchList];
|
||||
}
|
||||
|
||||
@@ -392,12 +393,13 @@ var CPSearchFieldSearchImage = nil,
|
||||
*/
|
||||
- (void)setRecentsAutosaveName:(CPString)name
|
||||
{
|
||||
if (_recentsAutosaveName != nil)
|
||||
[self _deregisterForAutosaveNotification];
|
||||
|
||||
_recentsAutosaveName = name;
|
||||
|
||||
if(name != nil)
|
||||
if (_recentsAutosaveName != nil)
|
||||
[self _registerForAutosaveNotification];
|
||||
else
|
||||
[self _deregisterForAutosaveNotification];
|
||||
}
|
||||
|
||||
// Private methods and subclassing
|
||||
@@ -425,95 +427,110 @@ var CPSearchFieldSearchImage = nil,
|
||||
|
||||
- (void)_updateCancelButtonVisibility
|
||||
{
|
||||
[_cancelButton setHidden:([[self stringValue] length] == 0)];
|
||||
[_cancelButton setHidden:([[self stringValue] length] === 0)];
|
||||
}
|
||||
|
||||
- (void)controlTextDidChange:(CPNotification)aNotification
|
||||
{
|
||||
if(!_sendsWholeSearchString)
|
||||
if (![self sendsWholeSearchString])
|
||||
{
|
||||
if(_sendsSearchStringImmediately)
|
||||
if ([self sendsSearchStringImmediately])
|
||||
[self _sendPartialString];
|
||||
else
|
||||
{
|
||||
[_partialStringTimer invalidate];
|
||||
var timeInterval = [CPSearchField _keyboardDelayForPartialSearchString:[self stringValue]];
|
||||
|
||||
_partialStringTimer = [CPTimer scheduledTimerWithTimeInterval:timeInterval target:self selector:@selector(_sendPartialString) userInfo:nil repeats:NO];
|
||||
_partialStringTimer = [CPTimer scheduledTimerWithTimeInterval:timeInterval
|
||||
target:self
|
||||
selector:@selector(_sendPartialString)
|
||||
userInfo:nil
|
||||
repeats:NO];
|
||||
}
|
||||
}
|
||||
|
||||
[self _updateCancelButtonVisibility];
|
||||
}
|
||||
|
||||
- (void)_sendAction:(id)sender
|
||||
{
|
||||
[self sendAction:[self action] to:[self target]];
|
||||
}
|
||||
|
||||
- (void)sendAction:(SEL)anAction to:(id)anObject
|
||||
{
|
||||
[super sendAction:anAction to:anObject];
|
||||
|
||||
[_partialStringTimer invalidate];
|
||||
|
||||
var current_value = [self objectValue];
|
||||
if(current_value != nil && current_value != "" && ![_recentSearches containsObject:current_value])
|
||||
{
|
||||
[self _addStringToRecentSearches:current_value];
|
||||
[self _updateSearchMenu];
|
||||
}
|
||||
|
||||
[self _addStringToRecentSearches:[self stringValue]];
|
||||
[self _updateCancelButtonVisibility];
|
||||
}
|
||||
|
||||
- (void)_addStringToRecentSearches:(CPString)string
|
||||
{
|
||||
var newSearches = [CPMutableArray arrayWithArray:_recentSearches];
|
||||
[newSearches addObject:string];
|
||||
[self setRecentSearches:newSearches];
|
||||
if (string === nil || string === @"" || [_recentSearches containsObject:string])
|
||||
return;
|
||||
|
||||
var searches = [CPMutableArray arrayWithArray:_recentSearches];
|
||||
[searches addObject:string];
|
||||
[self setRecentSearches:searches];
|
||||
[self _updateSearchMenu];
|
||||
}
|
||||
|
||||
- (BOOL)trackMouse:(CPEvent)event
|
||||
{
|
||||
var rect;
|
||||
var point;
|
||||
var location = [event locationInWindow];
|
||||
var rect,
|
||||
point,
|
||||
location = [event locationInWindow];
|
||||
|
||||
point = [self convertPoint:location fromView:nil];
|
||||
|
||||
rect = [self searchButtonRectForBounds:[self frame]];
|
||||
|
||||
if (CPRectContainsPoint(rect,point))
|
||||
{
|
||||
return [[self searchButton] trackMouse:event];
|
||||
}
|
||||
{
|
||||
return [[self searchButton] trackMouse:event];
|
||||
}
|
||||
|
||||
rect = [self cancelButtonRectForBounds:[self frame]];
|
||||
if (CPRectContainsPoint(rect,point))
|
||||
{
|
||||
return [[self cancelButton] trackMouse:event];
|
||||
}
|
||||
{
|
||||
return [[self cancelButton] trackMouse:event];
|
||||
}
|
||||
|
||||
return [super trackMouse:event];
|
||||
}
|
||||
|
||||
- (CPMenu)_searchMenuTemplate
|
||||
- (CPMenu)_defaultSearchMenuTemplate
|
||||
{
|
||||
var template, item;
|
||||
|
||||
template = [[CPMenu alloc] init];
|
||||
|
||||
item = [[CPMenuItem alloc] initWithTitle:@"Recent searches" action:NULL keyEquivalent:@""];
|
||||
item = [[CPMenuItem alloc] initWithTitle:@"Recent searches"
|
||||
action:NULL
|
||||
keyEquivalent:@""];
|
||||
[item setTag:CPSearchFieldRecentsTitleMenuItemTag];
|
||||
[item setEnabled:NO];
|
||||
[template addItem:item];
|
||||
|
||||
item = [[CPMenuItem alloc] initWithTitle:@"Recent search item" action:@selector(_searchFieldSearch:) keyEquivalent:@""];
|
||||
item = [[CPMenuItem alloc] initWithTitle:@"Recent search item"
|
||||
action:@selector(_searchFieldSearch:)
|
||||
keyEquivalent:@""];
|
||||
[item setTag:CPSearchFieldRecentsMenuItemTag];
|
||||
[item setTarget:self];
|
||||
[template addItem:item];
|
||||
|
||||
item = [[CPMenuItem alloc] initWithTitle:@"Clear recent searches" action:@selector(_searchFieldClearRecents:) keyEquivalent:@""];
|
||||
item = [[CPMenuItem alloc] initWithTitle:@"Clear recent searches"
|
||||
action:@selector(_searchFieldClearRecents:)
|
||||
keyEquivalent:@""];
|
||||
[item setTag:CPSearchFieldClearRecentsMenuItemTag];
|
||||
[item setTarget:self];
|
||||
[template addItem:item];
|
||||
|
||||
item = [[CPMenuItem alloc] initWithTitle:@"No recent searches" action:NULL keyEquivalent:@""];
|
||||
item = [[CPMenuItem alloc] initWithTitle:@"No recent searches"
|
||||
action:NULL
|
||||
keyEquivalent:@""];
|
||||
[item setTag:CPSearchFieldNoRecentsMenuItemTag];
|
||||
[item setEnabled:NO];
|
||||
[template addItem:item];
|
||||
@@ -523,41 +540,56 @@ var CPSearchFieldSearchImage = nil,
|
||||
|
||||
- (void)_updateSearchMenu
|
||||
{
|
||||
if(_searchMenuTemplate == nil)
|
||||
if (_searchMenuTemplate === nil)
|
||||
return;
|
||||
|
||||
var i, menu = [[CPMenu alloc] init];
|
||||
var countOfRecents = [_recentSearches count];
|
||||
var i, menu = [[CPMenu alloc] init],
|
||||
countOfRecents = [_recentSearches count],
|
||||
numberOfItems = [_searchMenuTemplate numberOfItems];
|
||||
|
||||
for (i = 0; i < [_searchMenuTemplate numberOfItems]; i++)
|
||||
for (i = 0; i < numberOfItems; i++)
|
||||
{
|
||||
var item = [_searchMenuTemplate itemAtIndex:i];
|
||||
var tag = [item tag];
|
||||
var item = [_searchMenuTemplate itemAtIndex:i],
|
||||
tag = [item tag];
|
||||
|
||||
if(tag == CPSearchFieldClearRecentsMenuItemTag && countOfRecents != 0)
|
||||
if (!(tag === CPSearchFieldRecentsTitleMenuItemTag && countOfRecents === 0) &&
|
||||
!(tag === CPSearchFieldClearRecentsMenuItemTag && countOfRecents === 0) &&
|
||||
!(tag === CPSearchFieldNoRecentsMenuItemTag && countOfRecents != 0) &&
|
||||
!(tag === CPSearchFieldRecentsMenuItemTag))
|
||||
{
|
||||
var separator = [CPMenuItem separatorItem];
|
||||
[menu addItem:separator];
|
||||
}
|
||||
var itemAction, itemTarget;
|
||||
switch (tag)
|
||||
{
|
||||
case CPSearchFieldRecentsTitleMenuItemTag : itemAction = NULL; itemTarget = NULL; break;
|
||||
case CPSearchFieldClearRecentsMenuItemTag : itemAction = @selector(_searchFieldClearRecents:); itemTarget = self; break;
|
||||
case CPSearchFieldNoRecentsMenuItemTag : itemAction = NULL; itemTarget = NULL; break;
|
||||
default: itemAction = [item action]; itemTarget = [item target]; break;
|
||||
}
|
||||
|
||||
if (tag === CPSearchFieldClearRecentsMenuItemTag || tag === CPSearchFieldRecentsTitleMenuItemTag)
|
||||
{
|
||||
var separator = [CPMenuItem separatorItem];
|
||||
[separator setEnabled:NO];
|
||||
[menu addItem:separator];
|
||||
}
|
||||
|
||||
if (!(tag == CPSearchFieldRecentsTitleMenuItemTag && countOfRecents == 0) &&
|
||||
!(tag == CPSearchFieldClearRecentsMenuItemTag && countOfRecents == 0) &&
|
||||
!(tag == CPSearchFieldNoRecentsMenuItemTag && countOfRecents != 0) &&
|
||||
!(tag == CPSearchFieldRecentsMenuItemTag))
|
||||
{
|
||||
var templateItem = [[CPMenuItem alloc] initWithTitle:[item title] action:[item action] keyEquivalent:[item keyEquivalent]];
|
||||
[templateItem setTarget:[item target]];
|
||||
[templateItem setEnabled:[item isEnabled]];
|
||||
[templateItem setTag:[item tag]];
|
||||
var templateItem = [[CPMenuItem alloc] initWithTitle:[item title]
|
||||
action:itemAction
|
||||
keyEquivalent:[item keyEquivalent]];
|
||||
[templateItem setTarget:itemTarget];
|
||||
[templateItem setEnabled:([item isEnabled] && itemAction != NULL)];
|
||||
[templateItem setTag:tag];
|
||||
[menu addItem:templateItem];
|
||||
}
|
||||
else if (tag == CPSearchFieldRecentsMenuItemTag)
|
||||
else if (tag === CPSearchFieldRecentsMenuItemTag)
|
||||
{
|
||||
var j;
|
||||
for (j = 0; j < countOfRecents; j++)
|
||||
{
|
||||
var rencentItem = [[CPMenuItem alloc] initWithTitle:[_recentSearches objectAtIndex:j] action:[item action] keyEquivalent:[item keyEquivalent]];
|
||||
[rencentItem setTarget:[item target]];
|
||||
var rencentItem = [[CPMenuItem alloc] initWithTitle:[_recentSearches objectAtIndex:j]
|
||||
action:@selector(_searchFieldSearch:)
|
||||
keyEquivalent:[item keyEquivalent]];
|
||||
[rencentItem setTarget:self];
|
||||
[menu addItem:rencentItem];
|
||||
}
|
||||
}
|
||||
@@ -565,16 +597,15 @@ var CPSearchFieldSearchImage = nil,
|
||||
_searchMenu = menu;
|
||||
}
|
||||
|
||||
|
||||
- (void)_showMenu:(id)sender
|
||||
{
|
||||
if(_searchMenu == nil || ![self isEnabled])
|
||||
if (_searchMenu === nil || [_searchMenu numberOfItems] === 0 || ![self isEnabled])
|
||||
return;
|
||||
|
||||
[super selectText:nil];
|
||||
var aFrame = [[self superview] convertRect:[self frame] toView:nil],
|
||||
location = CPMakePoint(aFrame.origin.x + 10, aFrame.origin.y + aFrame.size.height - 4);
|
||||
|
||||
var origin = CPMakePoint([self frame].origin.x, [self frame].origin.y + [self frame].size.height);
|
||||
var anEvent = [CPEvent keyEventWithType:CPRightMouseDown location:origin modifierFlags:0 timestamp:[CPDate date] windowNumber:1 context:[[CPGraphicsContext currentContext] graphicsPort] characters:"" charactersIgnoringModifiers:"" isARepeat:NO keyCode:0];
|
||||
var anEvent = [CPEvent mouseEventWithType:CPRightMouseDown location:location modifierFlags:0 timestamp:[[CPApp currentEvent] timestamp] windowNumber:[[self window] windowNumber] context:nil eventNumber:1 clickCount:1 pressure:0];
|
||||
|
||||
[CPMenu popUpContextMenu:_searchMenu withEvent:anEvent forView:sender];
|
||||
}
|
||||
@@ -586,16 +617,21 @@ var CPSearchFieldSearchImage = nil,
|
||||
|
||||
- (void)_searchFieldCancel:(id)sender
|
||||
{
|
||||
[self setObjectValue:nil];
|
||||
[self setObjectValue:@""];
|
||||
[self _sendPartialString];
|
||||
[self _updateCancelButtonVisibility];
|
||||
[sender setHidden:YES];
|
||||
}
|
||||
|
||||
- (void)_searchFieldSearch:(id)sender
|
||||
{
|
||||
[self setObjectValue:[sender title]];
|
||||
var searchString = [sender title];
|
||||
|
||||
if ([sender tag] != CPSearchFieldRecentsMenuItemTag)
|
||||
[self _addStringToRecentSearches:searchString];
|
||||
|
||||
[self setObjectValue:searchString];
|
||||
[self _sendPartialString];
|
||||
|
||||
[self _updateCancelButtonVisibility];
|
||||
}
|
||||
|
||||
@@ -615,52 +651,48 @@ var CPSearchFieldSearchImage = nil,
|
||||
[[CPNotificationCenter defaultCenter] removeObserver:self name:@"CPAutosavedRecentsChangedNotification" object:nil];
|
||||
}
|
||||
|
||||
- (void)_updateAutosavedRecents:(id)notification
|
||||
{
|
||||
var name = [notification object];
|
||||
var list = [self recentSearches];
|
||||
|
||||
[[CPUserDefaults standardUserDefaults] setObject:list forKey:name];
|
||||
|
||||
}
|
||||
|
||||
- (void)_autosaveRecentSearchList
|
||||
{
|
||||
if(_recentsAutosaveName != nil)
|
||||
if (_recentsAutosaveName != nil)
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:@"CPAutosavedRecentsChangedNotification" object:_recentsAutosaveName];
|
||||
}
|
||||
|
||||
- (void)_updateAutosavedRecents:(id)notification
|
||||
{
|
||||
var list = [self recentSearches],
|
||||
name = [notification object],
|
||||
bundle_name = [[[CPBundle mainBundle] infoDictionary] objectForKey:"CPBundleName"],
|
||||
cookie_name = [bundle_name lowercaseString] + "." + [notification object],
|
||||
|
||||
cookie = [[CPCookie alloc] initWithName:cookie_name],
|
||||
cookie_value = [list componentsJoinedByString:@","];
|
||||
|
||||
[cookie setValue:cookie_value
|
||||
expires:[[CPDate alloc] initWithTimeIntervalSinceNow:3600*24*365]
|
||||
domain:(window.location.href.hostname)];
|
||||
}
|
||||
|
||||
- (void)_loadRecentSearchList
|
||||
{
|
||||
var list,
|
||||
name = [self recentsAutosaveName];
|
||||
|
||||
list = [[CPUserDefaults standardUserDefaults] objectForKey:name];
|
||||
_recentSearches = list;
|
||||
}
|
||||
if (name === nil)
|
||||
return;
|
||||
|
||||
/*
|
||||
- (BOOL)trackMouse:(CPEvent)theEvent inRect:(CPRect)cellFrame ofView:(CPView)aTextView untilMouseUp:(BOOL)flag
|
||||
{
|
||||
}
|
||||
var bundle_name = [[[CPBundle mainBundle] infoDictionary] objectForKey:"CPBundleName"],
|
||||
cookie_name = [bundle_name lowercaseString] + "." + name,
|
||||
|
||||
- (BOOL)_trimRecentSearchList
|
||||
{
|
||||
}
|
||||
cookie = [[CPCookie alloc] initWithName:cookie_name];
|
||||
|
||||
- (void)_trackButton:(CPButton)button forEvent:(CPEvent)event inRect:(CPRect)rect ofView:(id)view
|
||||
{
|
||||
if (cookie != nil)
|
||||
{
|
||||
var cookie_value = [cookie value];
|
||||
list = (cookie_value != @"") ? [cookie_value componentsSeparatedByString:@","] : [CPArray array];
|
||||
_recentSearches = list;
|
||||
}
|
||||
}
|
||||
|
||||
- (id)_selectOrEdit:(CPRect)rect inView:(id)view target:(id)target editor:(id)editor event:(id)event start:(int)start end:(int)end
|
||||
{
|
||||
}
|
||||
|
||||
- (void)resetCursorRect:(CPRect)rect inView:(id)view
|
||||
{
|
||||
}
|
||||
*/
|
||||
|
||||
@end
|
||||
|
||||
var CPSearchButtonKey = @"CPSearchButtonKey",
|
||||
@@ -679,11 +711,13 @@ var CPSearchButtonKey = @"CPSearchButtonKey",
|
||||
|
||||
[coder encodeObject:_searchButton forKey:CPSearchButtonKey];
|
||||
[coder encodeObject:_cancelButton forKey:CPCancelButtonKey];
|
||||
[coder encodeObject:_recentsAutosaveName forKey:CPRecentsAutosaveNameKey];
|
||||
[coder encodeBool:_sendsWholeSearchString forKey:CPSendsWholeSearchStringKey];
|
||||
[coder encodeBool:_sendsSearchStringImmediately forKey:CPSendsSearchStringImmediatelyKey];
|
||||
[coder encodeInt:_maximumRecents forKey:CPMaximumRecentsKey];
|
||||
[coder encodeObject:_searchMenuTemplate forKey:CPSearchMenuTemplateKey];
|
||||
if (_recentsAutosaveName)
|
||||
[coder encodeObject:_recentsAutosaveName forKey:CPRecentsAutosaveNameKey];
|
||||
if (_searchMenuTemplate)
|
||||
[coder encodeObject:_searchMenuTemplate forKey:CPSearchMenuTemplateKey];
|
||||
}
|
||||
|
||||
- (id)initWithCoder:(CPCoder)coder
|
||||
@@ -696,8 +730,10 @@ var CPSearchButtonKey = @"CPSearchButtonKey",
|
||||
_sendsWholeSearchString = [coder decodeBoolForKey:CPSendsWholeSearchStringKey];
|
||||
_sendsSearchStringImmediately = [coder decodeBoolForKey:CPSendsSearchStringImmediatelyKey];
|
||||
_maximumRecents = [coder decodeIntForKey:CPMaximumRecentsKey];
|
||||
[self setSearchMenuTemplate:[coder decodeObjectForKey:CPSearchMenuTemplateKey]];
|
||||
[self resetCancelButton];
|
||||
var template = [coder decodeObjectForKey:CPSearchMenuTemplateKey];
|
||||
if (template)
|
||||
[self setSearchMenuTemplate:template];
|
||||
|
||||
[self setDelegate:self];
|
||||
|
||||
return self;
|
||||
|
||||
@@ -151,6 +151,9 @@ var CPSplitViewHorizontalImage = nil,
|
||||
_DOMDividerElements = [];
|
||||
#endif
|
||||
|
||||
// The divider changes size when pane splitter mode is toggled, so the
|
||||
// subviews need to change size too.
|
||||
_needsResizeSubviews = YES;
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
|
||||
@@ -247,6 +247,8 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
|
||||
[_labelsView tabView:self didAddTabViewItem:aTabViewItem];
|
||||
|
||||
[aTabViewItem _setTabView:self];
|
||||
|
||||
if ([_tabViewItems count] == 1)
|
||||
[self selectFirstTabViewItem:self];
|
||||
|
||||
@@ -263,9 +265,11 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
var index = [self indexOfTabViewItem:aTabViewItem];
|
||||
|
||||
[_tabViewItems removeObjectIdenticalTo:aTabViewItem];
|
||||
|
||||
|
||||
[_labelsView tabView:self didRemoveTabViewItemAtIndex:index];
|
||||
|
||||
[aTabViewItem _setTabView:nil];
|
||||
|
||||
if (_delegateSelectors & CPTabViewDidChangeNumberOfTabViewItemsSelector)
|
||||
[_delegate tabViewDidChangeNumberOfTabViewItems:self];
|
||||
}
|
||||
|
||||
@@ -58,6 +58,8 @@ CPPressedTab = 2;
|
||||
|
||||
CPView _view;
|
||||
CPView _auxiliaryView;
|
||||
|
||||
CPTabView _tabView;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
@@ -168,6 +170,14 @@ CPPressedTab = 2;
|
||||
return _tabView;
|
||||
}
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
*/
|
||||
- (void)_setTabView:(CPTabView)aView
|
||||
{
|
||||
_tabView = aView;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPTabViewItemIdentifierKey = "CPTabViewItemIdentifierKey",
|
||||
|
||||
@@ -236,7 +236,7 @@ CPTableViewSolidHorizontalGridLineMask = 1 << 1;
|
||||
if ([_dataSource respondsToSelector:@selector(tableView:setObjectValue:forTableColumn:row:)])
|
||||
_implementedDataSourceMethods |= CPTableViewDataSource_tableView_setObjectValue_forTableColumn_row_;
|
||||
|
||||
if ([_dataSource respondsToSelector:@selector(tableView:setObjectValue:forTableColumn:row:)])
|
||||
if ([_dataSource respondsToSelector:@selector(tableView:acceptDrop:row:dropOperation:)])
|
||||
_implementedDataSourceMethods |= CPTableViewDataSource_tableView_acceptDrop_row_dropOperation_;
|
||||
|
||||
if ([_dataSource respondsToSelector:@selector(tableView:namesOfPromisedFilesDroppedAtDestination:forDraggedRowsWithIndexes:)])
|
||||
@@ -577,7 +577,9 @@ CPTableViewSolidHorizontalGridLineMask = 1 << 1;
|
||||
[_selectedRowIndexes addIndexes:rows];
|
||||
else
|
||||
_selectedRowIndexes = [rows copy];
|
||||
|
||||
|
||||
[self _noteSelectionDidChange];
|
||||
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
@@ -586,7 +588,7 @@ CPTableViewSolidHorizontalGridLineMask = 1 << 1;
|
||||
return _selectedColumnIndexes;
|
||||
}
|
||||
|
||||
- (void)selectedRowIndexes
|
||||
- (CPIndexSet)selectedRowIndexes
|
||||
{
|
||||
return _selectedRowIndexes;
|
||||
}
|
||||
@@ -1637,8 +1639,7 @@ CPTableViewSolidHorizontalGridLineMask = 1 << 1;
|
||||
|
||||
- (void)stopTracking:(CGPoint)lastPoint at:(CGPoint)aPoint mouseIsUp:(BOOL)mouseIsUp
|
||||
{
|
||||
if (![_previouslySelectedRowIndexes isEqualToIndexSet:_selectedRowIndexes])
|
||||
[self _noteSelectionDidChange];
|
||||
|
||||
}
|
||||
|
||||
- (void)_updateSelectionWithMouseAtRow:(CPInteger)aRow
|
||||
@@ -1709,10 +1710,10 @@ CPTableViewSolidHorizontalGridLineMask = 1 << 1;
|
||||
|
||||
if (!_previouslySelectedRowIndexes)
|
||||
_previouslySelectedRowIndexes = [_selectedRowIndexes copy];
|
||||
|
||||
[self selectRowIndexes:newSelection byExtendingSelection:NO];
|
||||
|
||||
|
||||
[self _noteSelectionIsChanging];
|
||||
|
||||
[self selectRowIndexes:newSelection byExtendingSelection:NO];
|
||||
}
|
||||
|
||||
- (void)_noteSelectionIsChanging
|
||||
|
||||
@@ -55,11 +55,7 @@ var CPTextFieldDOMInputElement = nil,
|
||||
CPTextFieldInputIsActive = NO,
|
||||
CPTextFieldCachedSelectStartFunction = nil,
|
||||
CPTextFieldCachedDragFunction = nil,
|
||||
|
||||
CPTextFieldBlurFunction = nil,
|
||||
CPTextFieldKeyUpFunction = nil,
|
||||
CPTextFieldKeyPressFunction = nil,
|
||||
CPTextFieldKeyDownFunction = nil;
|
||||
CPTextFieldBlurFunction = nil;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -246,10 +242,6 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
CPTextFieldDOMPasswordInputElement.style.outline = "none";
|
||||
CPTextFieldDOMPasswordInputElement.type = "password";
|
||||
|
||||
CPTextFieldDOMPasswordInputElement.attachEvent("on" + CPDOMEventKeyUp, CPTextFieldKeyUpFunction);
|
||||
CPTextFieldDOMPasswordInputElement.attachEvent("on" + CPDOMEventKeyDown, CPTextFieldKeyDownFunction);
|
||||
CPTextFieldDOMPasswordInputElement.attachEvent("on" + CPDOMEventKeyPress, CPTextFieldKeyPressFunction);
|
||||
|
||||
CPTextFieldDOMPasswordInputElement.onblur = CPTextFieldBlurFunction;
|
||||
}
|
||||
|
||||
@@ -639,6 +631,9 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
else
|
||||
[[self window] selectNextKeyView:self];
|
||||
|
||||
if ([[[self window] firstResponder] respondsToSelector:@selector(selectText:)])
|
||||
[[[self window] firstResponder] selectText:self];
|
||||
|
||||
[[[self window] platformWindow] _propagateCurrentDOMEvent:NO];
|
||||
}
|
||||
else
|
||||
@@ -781,11 +776,142 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
#if PLATFORM(DOM)
|
||||
var element = [self _inputElement];
|
||||
|
||||
if (element.parentNode === _DOMElement && ([self isEditable] || [self isSelectable]))
|
||||
window.setTimeout(function() { element.select(); }, 0);
|
||||
if (([self isEditable] || [self isSelectable]))
|
||||
{
|
||||
if ([[self window] firstResponder] === self)
|
||||
window.setTimeout(function() { element.select(); }, 0);
|
||||
else
|
||||
{
|
||||
[[self window] makeFirstResponder:self];
|
||||
window.setTimeout(function() {[self selectText:sender];}, 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)copy:(id)sender
|
||||
{
|
||||
var selectedRange = [self selectedRange];
|
||||
|
||||
if (selectedRange.length < 1)
|
||||
return;
|
||||
|
||||
var pasteboard = [CPPasteboard generalPasteboard],
|
||||
stringValue = [self stringValue],
|
||||
stringForPasting = [stringValue substringWithRange:selectedRange];
|
||||
|
||||
[pasteboard declareTypes:[CPStringPboardType] owner:nil];
|
||||
[pasteboard setString:stringForPasting forType:CPStringPboardType];
|
||||
}
|
||||
|
||||
- (void)cut:(id)sender
|
||||
{
|
||||
[self copy:sender];
|
||||
[self deleteBackwards:sender];
|
||||
}
|
||||
|
||||
- (void)paste:(id)sender
|
||||
{
|
||||
var pasteboard = [CPPasteboard generalPasteboard];
|
||||
|
||||
if (![[pasteboard types] containsObject:CPStringPboardType])
|
||||
return;
|
||||
|
||||
[self deleteBackwards:sender];
|
||||
|
||||
var selectedRange = [self selectedRange],
|
||||
stringValue = [self stringValue],
|
||||
pasteString = [pasteboard stringForType:CPStringPboardType],
|
||||
newValue = [stringValue stringByReplacingCharactersInRange:selectedRange withString:pasteString];
|
||||
|
||||
[self setStringValue:newValue];
|
||||
[self setSelectedRange:CPMakeRange(selectedRange.location+pasteString.length, 0)];
|
||||
}
|
||||
|
||||
- (CPRange)selectedRange
|
||||
{
|
||||
if ([[self window] firstResponder] !== self)
|
||||
return CPMakeRange(0, 0);
|
||||
|
||||
// we wrap this in try catch because firefox will throw an exception in certain instances
|
||||
try
|
||||
{
|
||||
var inputElement = [self _inputElement],
|
||||
selectionStart = inputElement.selectionStart,
|
||||
selectionEnd = inputElement.selectionEnd;
|
||||
|
||||
if ([selectionStart isKindOfClass:CPNumber])
|
||||
return CPMakeRange(selectionStart, selectionEnd - selectionStart);
|
||||
|
||||
// browsers which don't support selectionStart/selectionEnd (aka IE).
|
||||
var theDocument = inputElement.ownerDocument || inputElement.document,
|
||||
selectionRange = theDocument.selection.createRange(),
|
||||
range = inputElement.createTextRange();
|
||||
|
||||
if (range.inRange(selectionRange))
|
||||
{
|
||||
range.setEndPoint('EndToStart', selectionRange);
|
||||
return CPMakeRange(range.text.length, selectionRange.text.length);
|
||||
}
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
// fall through to the return
|
||||
}
|
||||
|
||||
return CGMakeRange(0, 0);
|
||||
}
|
||||
|
||||
- (void)setSelectedRange:(CPRange)aRange
|
||||
{
|
||||
if (![[self window] firstResponder] === self)
|
||||
return;
|
||||
|
||||
var inputElement = [self _inputElement];
|
||||
|
||||
try
|
||||
{
|
||||
if ([inputElement.selectionStart isKindOfClass:CPNumber])
|
||||
{
|
||||
inputElement.selectionStart = aRange.location;
|
||||
inputElement.selectionEnd = CPMaxRange(aRange);
|
||||
}
|
||||
else
|
||||
{
|
||||
// browsers which don't support selectionStart/selectionEnd (aka IE).
|
||||
var theDocument = inputElement.ownerDocument || inputElement.document,
|
||||
existingRange = theDocument.selection.createRange(),
|
||||
range = inputElement.createTextRange();
|
||||
|
||||
if (range.inRange(existingRange))
|
||||
{
|
||||
range.collapse(true);
|
||||
range.move('character', aRange.location);
|
||||
range.moveEnd('character', aRange.length);
|
||||
range.select();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
- (void)selectAll:(id)sender
|
||||
{
|
||||
[self selectText:sender];
|
||||
}
|
||||
|
||||
- (void)deleteBackwards:(id)sender
|
||||
{
|
||||
var selectedRange = [self selectedRange],
|
||||
stringValue = [self stringValue],
|
||||
newValue = [stringValue stringByReplacingCharactersInRange:selectedRange withString:""];
|
||||
|
||||
[self setStringValue:newValue];
|
||||
[self setSelectedRange:CPMakeRange(selectedRange.location, 0)];
|
||||
}
|
||||
|
||||
#pragma mark Setting the Delegate
|
||||
|
||||
- (void)setDelegate:(id)aDelegate
|
||||
|
||||
@@ -957,7 +957,14 @@ var TOP_MARGIN = 5.0,
|
||||
- (void)mouseDown:(CPEvent)anEvent
|
||||
{
|
||||
if ([_toolbarItem view])
|
||||
return;
|
||||
return [[self nextResponder] mouseDown:anEvent];
|
||||
|
||||
var identifier = [_toolbarItem itemIdentifier];
|
||||
|
||||
if (identifier === CPToolbarSpaceItemIdentifier ||
|
||||
identifier === CPToolbarFlexibleSpaceItemIdentifier ||
|
||||
identifier === CPToolbarSeparatorItemIdentifier)
|
||||
return [[self nextResponder] mouseDown:anEvent];
|
||||
|
||||
[super mouseDown:anEvent];
|
||||
}
|
||||
|
||||
@@ -495,6 +495,83 @@ CPToolbarItemVisibilityPriorityUser
|
||||
|
||||
@end
|
||||
|
||||
var CPToolbarItemIdentifierKey = @"CPToolbarItemIdentifierKey",
|
||||
CPToolbarItemLabelKey = @"CPToolbarItemLabelKey",
|
||||
CPToolbarItemPaletteLabelKey = @"CPToolbarItemPaletteLabelKey",
|
||||
CPToolbarItemToolTipKey = @"CPToolbarItemToolTipKey",
|
||||
CPToolbarItemTagKey = @"CPToolbarItemTagKey",
|
||||
CPToolbarItemTargetKey = @"CPToolbarItemTargetKey",
|
||||
CPToolbarItemActionKey = @"CPToolbarItemActionKey",
|
||||
CPToolbarItemEnabledKey = @"CPToolbarItemEnabledKey",
|
||||
CPToolbarItemImageKey = @"CPToolbarItemImageKey",
|
||||
CPToolbarItemAlternateImageKey = @"CPToolbarItemAlternateImageKey",
|
||||
CPToolbarItemViewKey = @"CPToolbarItemViewKey",
|
||||
CPToolbarItemMinSizeKey = @"CPToolbarItemMinSizeKey",
|
||||
CPToolbarItemMaxSizeKey = @"CPToolbarItemMaxSizeKey",
|
||||
CPToolbarItemVisibilityPriorityKey = @"CPToolbarItemVisibilityPriorityKey";
|
||||
|
||||
@implementation CPToolbarItem (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_itemIdentifier = [aCoder decodeObjectForKey:CPToolbarItemIdentifierKey];
|
||||
|
||||
[self setLabel:[aCoder decodeObjectForKey:CPToolbarItemLabelKey]];
|
||||
[self setPaletteLabel:[aCoder decodeObjectForKey:CPToolbarItemPaletteLabelKey]];
|
||||
[self setToolTip:[aCoder decodeObjectForKey:CPToolbarItemToolTipKey]];
|
||||
|
||||
[self setTag:[aCoder decodeObjectForKey:CPToolbarItemTagKey]];
|
||||
[self setTarget:[aCoder decodeObjectForKey:CPToolbarItemTargetKey]];
|
||||
[self setAction:[aCoder decodeObjectForKey:CPToolbarItemActionKey]];
|
||||
|
||||
[self setEnabled:[aCoder decodeBoolForKey:CPToolbarItemEnabledKey]];
|
||||
|
||||
[self setImage:[aCoder decodeBoolForKey:CPToolbarItemImageKey]];
|
||||
[self setAlternateImage:[aCoder decodeBoolForKey:CPToolbarItemAlternateImageKey]];
|
||||
|
||||
[self setView:[aCoder decodeObjectForKey:CPToolbarItemViewKey]];
|
||||
|
||||
[self setMinSize:[aCoder decodeSizeForKey:CPToolbarItemMinSizeKey]];
|
||||
[self setMaxSize:[aCoder decodeSizeForKey:CPToolbarItemMaxSizeKey]];
|
||||
|
||||
[self setVisibilityPriority:[aCoder decodeIntForKey:CPToolbarItemVisibilityPriorityKey]];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:_itemIdentifier forKey:CPToolbarItemItemIdentifierKey];
|
||||
|
||||
[aCoder encodeObject:[self label] forKey:CPToolbarItemLabelKey];
|
||||
[aCoder encodeObject:[self paletteLabel] forKey:CPToolbarItemPaletteLabelKey];
|
||||
|
||||
[aCoder encodeObject:[self toolTip] forKey:CPToolbarItemToolTipKey];
|
||||
|
||||
[aCoder encodeObject:[self tag] forKey:CPToolbarItemTagKey];
|
||||
[aCoder encodeObject:[self target] forKey:CPToolbarItemTargetKey];
|
||||
[aCoder encodeObject:[self action] forKey:CPToolbarItemActionKey];
|
||||
|
||||
[aCoder encodeObject:[self isEnabled] forKey:CPToolbarItemEnabledKey];
|
||||
|
||||
[aCoder encodeObject:[self image] forKey:CPToolbarItemImageKey];
|
||||
[aCoder encodeObject:[self alternateImage] forKey:CPToolbarItemAlternateImageKey];
|
||||
|
||||
[aCoder encodeObject:[self view] forKey:CPToolbarItemViewKey];
|
||||
|
||||
[aCoder encodeSize:[self minSize] forKey:CPToolbarItemMinSizeKey];
|
||||
[aCoder encodeSize:[self maxSize] forKey:CPToolbarItemMaxSizeKey];
|
||||
|
||||
[aCoder encodeObject:[self visibilityPriority] forKey:CPToolbarItemVisibilityPriorityKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPToolbarItem (CPCopying)
|
||||
|
||||
- (id)copy
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
id _representedObject @accessors(readonly, property=representedObject);
|
||||
|
||||
CPTreeNode _parentNode @accessors(readonly, property=parentNode);
|
||||
CPMutableArray _childNodes @accessors(readonly, property=childNodes);
|
||||
CPMutableArray _childNodes;
|
||||
}
|
||||
|
||||
+ (id)treeNodeWithRepresentedObject:(id)anObject
|
||||
@@ -33,16 +33,23 @@
|
||||
return [_childNodes count] <= 0;
|
||||
}
|
||||
|
||||
- (CPArray)childNodes
|
||||
{
|
||||
return [_childNodes copy];
|
||||
}
|
||||
|
||||
- (CPMutableArray)mutableChildNodes
|
||||
{
|
||||
return [self mutableArrayValueForKey:@"childNodes"];
|
||||
}
|
||||
|
||||
- (void)insertObject:(id)anObject inChildNodesAtIndex:(CPInteger)anIndex
|
||||
- (void)insertObject:(id)aTreeNode inChildNodesAtIndex:(CPInteger)anIndex
|
||||
{
|
||||
anObject._parentNode = self;
|
||||
[[aTreeNode._parentNode mutableChildNodes] removeObjectIdenticalTo:aTreeNode];
|
||||
|
||||
[_childNodes addObject:anObject];
|
||||
aTreeNode._parentNode = self;
|
||||
|
||||
[_childNodes insertObject:aTreeNode atIndex:anIndex];
|
||||
}
|
||||
|
||||
- (void)removeObjectFromChildNodesAtIndex:(CPInteger)anIndex
|
||||
@@ -52,13 +59,14 @@
|
||||
[_childNodes removeObjectAtIndex:anIndex];
|
||||
}
|
||||
|
||||
- (void)replaceObjectFromChildNodesAtIndex:(CPInteger)anIndex withObject:(id)anObject
|
||||
- (void)replaceObjectFromChildNodesAtIndex:(CPInteger)anIndex withObject:(id)aTreeNode
|
||||
{
|
||||
var oldObject = [_childNodes objectAtIndex:anIndex];
|
||||
var oldTreeNode = [_childNodes objectAtIndex:anIndex];
|
||||
|
||||
oldObject._parentNode = nil;
|
||||
oldTreeNode._parentNode = nil;
|
||||
aTreeNode._parentNode = self;
|
||||
|
||||
[_childNodes replaceObjectAtIndex:anIndex withObject:anObject];
|
||||
[_childNodes replaceObjectAtIndex:anIndex withObject:aTreeNode];
|
||||
}
|
||||
|
||||
- (id)objectInChildNodesAtIndex:(CPInteger)anIndex
|
||||
@@ -80,3 +88,32 @@
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPTreeNodeRepresentedObjectKey = @"CPTreeNodeRepresentedObjectKey",
|
||||
CPTreeNodeParentNodeKey = @"CPTreeNodeParentNodeKey",
|
||||
CPTreeNodeChildNodesKey = @"CPTreeNodeChildNodesKey";
|
||||
|
||||
@implementation CPTreeNode (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_representedObject = [aCoder decodeObjectForKey:CPTreeNodeRepresentedObjectKey];
|
||||
_parentNode = [aCoder decodeObjectForKey:CPTreeNodeParentNodeKey];
|
||||
_childNodes = [aCoder decodeObjectForKey:CPTreeNodeChildNodesKey];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:_representedObject forKey:CPTreeNodeRepresentedObjectKey];
|
||||
[aCoder encodeConditionalObject:_parentNode forKey:CPTreeNodeParentNodeKey];
|
||||
[aCoder encodeObject:_childNodes forKey:CPTreeNodeChildNodesKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -238,6 +238,11 @@ var CPViewFlags = { },
|
||||
return [CPSet setWithObjects:@"frameOrigin", @"frameSize"];
|
||||
}
|
||||
|
||||
+ (CPSet)keyPathsForValuesAffectingBounds
|
||||
{
|
||||
return [CPSet setWithObjects:@"boundsOrigin", @"boundsSize"];
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
return [self initWithFrame:CGRectMakeZero()];
|
||||
@@ -308,7 +313,7 @@ var CPViewFlags = { },
|
||||
*/
|
||||
- (CPArray)subviews
|
||||
{
|
||||
return _subviews;
|
||||
return [_subviews copy];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -441,7 +446,7 @@ var CPViewFlags = { },
|
||||
|
||||
[_superview willRemoveSubview:self];
|
||||
|
||||
[[_superview subviews] removeObject:self];
|
||||
[_superview._subviews removeObject:self];
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
CPDOMDisplayServerRemoveChild(_superview._DOMElement, _DOMElement);
|
||||
@@ -468,6 +473,73 @@ var CPViewFlags = { },
|
||||
[self _insertSubview:aView atIndex:index];
|
||||
}
|
||||
|
||||
- (void)setSubviews:(CPArray)newSubviews
|
||||
{
|
||||
if (!newSubviews)
|
||||
[CPException raise:CPInvalidArgumentException reason:"newSubviews cannot be nil in -[CPView setSubviews:]"];
|
||||
|
||||
// Trivial Case 0: Same array somehow
|
||||
if ([_subviews isEqual:newSubviews])
|
||||
return;
|
||||
|
||||
// Trivial Case 1: No current subviews, simply add all new subviews.
|
||||
if ([_subviews count] === 0)
|
||||
{
|
||||
var index = 0,
|
||||
count = [newSubviews count];
|
||||
|
||||
for (; index < count; ++index)
|
||||
[self addSubview:newSubviews[index]];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Trivial Case 2: No new subviews, simply remove all current subviews.
|
||||
if ([newSubviews count] === 0)
|
||||
{
|
||||
var count = [_subviews count];
|
||||
|
||||
while (count--)
|
||||
[_subviews[count] removeFromSuperview];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Find out the views that were removed.
|
||||
var removedSubviews = [CPMutableSet setWithArray:_subviews];
|
||||
|
||||
[removedSubviews removeObjectsInArray:newSubviews];
|
||||
[removedSubviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
|
||||
|
||||
// Find out which views need to be added.
|
||||
var addedSubviews = [CPMutableSet setWithArray:newSubviews];
|
||||
|
||||
[addedSubviews removeObjectsInArray:_subviews];
|
||||
|
||||
var addedSubview = nil,
|
||||
addedSubviewEnumerator = [addedSubviews objectEnumerator];
|
||||
|
||||
while (addedSubview = [addedSubviewEnumerator nextObject])
|
||||
[self addSubview:addedSubview];
|
||||
|
||||
// If the order is fine, no need to reorder.
|
||||
if ([_subviews isEqual:newSubviews])
|
||||
return;
|
||||
|
||||
_subviews = [newSubviews copy];
|
||||
|
||||
var index = 0,
|
||||
count = [_subviews count];
|
||||
|
||||
for (; index < count; ++index)
|
||||
{
|
||||
var subview = _subviews[index];
|
||||
|
||||
CPDOMDisplayServerRemoveChild(_DOMElement, subview._DOMElement);
|
||||
CPDOMDisplayServerAppendChild(_DOMElement, subview._DOMElement);
|
||||
}
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (void)_setWindow:(CPWindow)aWindow
|
||||
{
|
||||
@@ -596,7 +668,7 @@ var CPViewFlags = { },
|
||||
|
||||
- (void)viewWithTag:(CPInteger)aTag
|
||||
{
|
||||
if ([self tag] === aTag)
|
||||
if ([self tag] == aTag)
|
||||
return self;
|
||||
|
||||
var index = 0,
|
||||
@@ -816,6 +888,16 @@ var CPViewFlags = { },
|
||||
return _CGRectMakeCopy(_bounds);
|
||||
}
|
||||
|
||||
- (CGPoint)boundsOrigin
|
||||
{
|
||||
return _CGPointMakeCopy(_bounds.origin);
|
||||
}
|
||||
|
||||
- (CGSize)boundsSize
|
||||
{
|
||||
return _CGSizeMakeCopy(_bounds.size);
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the location of the receiver inside its frame. The method
|
||||
posts a CPViewBoundsDidChangeNotification to the
|
||||
@@ -1863,12 +1945,11 @@ setBoundsOrigin:
|
||||
|
||||
- (BOOL)performKeyEquivalent:(CPEvent)anEvent
|
||||
{
|
||||
var subviews = [self subviews],
|
||||
count = [subviews count];
|
||||
var count = [_subviews count];
|
||||
|
||||
// Is reverse iteration correct here? It matches the other (correct) code like hit testing.
|
||||
while (count--)
|
||||
if ([subviews[count] performKeyEquivalent:anEvent])
|
||||
if ([_subviews[count] performKeyEquivalent:anEvent])
|
||||
return YES;
|
||||
|
||||
return NO;
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
@import <AppKit/CPResponder.j>
|
||||
|
||||
var CPViewControllerCachedCibs;
|
||||
|
||||
/*! @class CPViewController
|
||||
The CPViewController class provides the fundamental view-management controller for Cappuccino applications.
|
||||
@@ -56,6 +57,12 @@
|
||||
CPDictionary _cibExternalNameTable @accessors(property=cibExternalNameTable, readonly);
|
||||
}
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
if (self === CPViewController)
|
||||
CPViewControllerCachedCibs = [CPDictionary dictionary];
|
||||
}
|
||||
|
||||
/*!
|
||||
Convenience initializer calls -initWithCibName:bundle: with nil for both parameters.
|
||||
*/
|
||||
@@ -113,11 +120,16 @@
|
||||
{
|
||||
if (_view)
|
||||
return;
|
||||
|
||||
// check if a cib is already cached for the current _cibName
|
||||
var cib = [CPViewControllerCachedCibs objectForKey:_cibName];
|
||||
|
||||
// if (_cibName)
|
||||
// [CPException raise: reason:];
|
||||
|
||||
var cib = [[CPCib alloc] initWithContentsOfURL:[_cibBundle pathForResource:_cibName + @".cib"]];
|
||||
if (!cib)
|
||||
{
|
||||
// if the cib isn't cached yet : fetch it and cache it
|
||||
cib = [[CPCib alloc] initWithContentsOfURL:[_cibBundle pathForResource:_cibName + @".cib"]];
|
||||
[CPViewControllerCachedCibs setObject:cib forKey:_cibName];
|
||||
}
|
||||
|
||||
[cib instantiateCibWithExternalNameTable:_cibExternalNameTable];
|
||||
}
|
||||
|
||||
@@ -150,8 +150,10 @@ CPWebViewScrollNative = 2;
|
||||
|
||||
|
||||
_frameView = [[CPView alloc] initWithFrame:[self bounds]];
|
||||
[_frameView setAutoresizingMask:CPViewWidthSizable|CPViewHeightSizable];
|
||||
|
||||
_scrollView = [[CPScrollView alloc] initWithFrame:[self bounds]];
|
||||
[_scrollView setAutohidesScrollers:YES];
|
||||
[_scrollView setAutoresizingMask:CPViewWidthSizable|CPViewHeightSizable];
|
||||
[_scrollView setDocumentView:_frameView];
|
||||
|
||||
@@ -166,7 +168,6 @@ CPWebViewScrollNative = 2;
|
||||
- (void)setFrameSize:(CPSize)aSize
|
||||
{
|
||||
[super setFrameSize:aSize];
|
||||
|
||||
[self _resizeWebFrame];
|
||||
}
|
||||
|
||||
@@ -180,7 +181,7 @@ CPWebViewScrollNative = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
[_frameView setFrameSize:[_scrollView bounds].size];
|
||||
[_frameView setFrameSize:[_scrollView contentSize]];
|
||||
|
||||
// try to get the document size so we can correctly set the frame
|
||||
var win = null;
|
||||
|
||||
@@ -175,6 +175,7 @@ CPWindowBelow = 2;
|
||||
CPWindowWillCloseNotification = @"CPWindowWillCloseNotification";
|
||||
CPWindowDidBecomeMainNotification = @"CPWindowDidBecomeMainNotification";
|
||||
CPWindowDidResignMainNotification = @"CPWindowDidResignMainNotification";
|
||||
CPWindowDidResizeNotification = @"CPWindowDidResizeNotification";
|
||||
CPWindowDidMoveNotification = @"CPWindowDidMoveNotification";
|
||||
CPWindowWillBeginSheetNotification = @"CPWindowWillBeginSheetNotification";
|
||||
CPWindowDidEndSheetNotification = @"CPWindowDidEndSheetNotification";
|
||||
@@ -333,6 +334,11 @@ var CPWindowSaveImage = nil,
|
||||
CPWindowSavingImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPProgressIndicator/CPProgressIndicatorSpinningStyleRegular.gif"] size:CGSizeMake(16.0, 16.0)]
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
return [self initWithContentRect:_CGRectMakeZero() styleMask:CPTitledWindowMask];
|
||||
}
|
||||
|
||||
/*!
|
||||
Initializes the window. The method also takes a style bit mask made up
|
||||
of any of the following values:
|
||||
@@ -610,6 +616,8 @@ CPTexturedBackgroundWindowMask
|
||||
*/
|
||||
- (void)setFrame:(CGRect)aFrame display:(BOOL)shouldDisplay animate:(BOOL)shouldAnimate
|
||||
{
|
||||
aFrame = _CGRectMakeCopy(aFrame);
|
||||
|
||||
var value = aFrame.origin.x,
|
||||
delta = value - FLOOR(value);
|
||||
|
||||
@@ -673,8 +681,8 @@ CPTexturedBackgroundWindowMask
|
||||
if (_hasShadow)
|
||||
[_shadowView setFrameSize:_CGSizeMake(SHADOW_MARGIN_LEFT + size.width + SHADOW_MARGIN_RIGHT, SHADOW_MARGIN_BOTTOM + size.height + SHADOW_MARGIN_TOP + SHADOW_DISTANCE)];
|
||||
|
||||
if (!_isAnimating && [_delegate respondsToSelector:@selector(windowDidResize:)])
|
||||
[_delegate windowDidResize:self];
|
||||
if (!_isAnimating)
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPWindowDidResizeNotification object:self];
|
||||
}
|
||||
|
||||
if ([self _sharesChromeWithPlatformWindow])
|
||||
@@ -1079,6 +1087,13 @@ CPTexturedBackgroundWindowMask
|
||||
selector:@selector(windowDidMove:)
|
||||
name:CPWindowDidMoveNotification
|
||||
object:self];
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(windowDidResize:)])
|
||||
[defaultCenter
|
||||
addObserver:_delegate
|
||||
selector:@selector(windowDidResize:)
|
||||
name:CPWindowDidResizeNotification
|
||||
object:self];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -1678,6 +1693,17 @@ CPTexturedBackgroundWindowMask
|
||||
if (!(_styleMask & CPClosableWindowMask))
|
||||
return;
|
||||
|
||||
if ([self isFullBridge])
|
||||
{
|
||||
var event = [CPApp currentEvent];
|
||||
|
||||
if ([event type] === CPKeyDown && [event characters] === "w" && ([event modifierFlags] & CPPlatformActionKeyMask))
|
||||
{
|
||||
[[self platformWindow] _propagateCurrentDOMEvent:YES];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Only send ONE windowShouldClose: message.
|
||||
if ([_delegate respondsToSelector:@selector(windowShouldClose:)])
|
||||
{
|
||||
@@ -1981,7 +2007,8 @@ CPTexturedBackgroundWindowMask
|
||||
|
||||
- (void)_setUpMasksForView:(CPView)aView
|
||||
{
|
||||
var views = [CPArray arrayWithArray:[aView subviews]];
|
||||
var views = [aView subviews];
|
||||
|
||||
[views addObject:aView];
|
||||
|
||||
for (var i = 0, count = [views count]; i < count; i++)
|
||||
@@ -1996,7 +2023,8 @@ CPTexturedBackgroundWindowMask
|
||||
|
||||
- (void)_restoreMasksForView:(CPView)aView
|
||||
{
|
||||
var views = [CPArray arrayWithArray:[aView subviews]];
|
||||
var views = [aView subviews];
|
||||
|
||||
[views addObject:aView];
|
||||
|
||||
for (var i = 0, count = [views count]; i < count; i++)
|
||||
@@ -2351,9 +2379,21 @@ var keyViewComparator = function(a, b, context)
|
||||
*/
|
||||
- (CPUndoManager)undoManager
|
||||
{
|
||||
// If we've ever created an undo manager, return it.
|
||||
if (_undoManager)
|
||||
return _undoManager;
|
||||
|
||||
// If not, check to see if the document has one.
|
||||
var documentUndoManager = [[_windowController document] undoManager];
|
||||
|
||||
if (documentUndoManager)
|
||||
return documentUndoManager;
|
||||
|
||||
// If not, check to see if the delegate has one.
|
||||
if (_delegateRespondsToWindowWillReturnUndoManagerSelector)
|
||||
return [_delegate windowWillReturnUndoManager:self];
|
||||
|
||||
|
||||
// If not, create one.
|
||||
if (!_undoManager)
|
||||
_undoManager = [[CPUndoManager alloc] init];
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ var HUD_TITLEBAR_HEIGHT = 26.0;
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground2.png"] size:CPSizeMake(6.0, 78.0)],
|
||||
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground3.png"] size:CPSizeMake(6.0, 1.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground4.png"] size:CPSizeMake(1.0, 1.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground4.png"] size:CPSizeMake(5.0, 5.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground5.png"] size:CPSizeMake(6.0, 1.0)],
|
||||
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground6.png"] size:CPSizeMake(6.0, 6.0)],
|
||||
|
||||
@@ -37,6 +37,7 @@ var _CPWindowViewResizeIndicatorImage = nil;
|
||||
|
||||
CPView _toolbarView;
|
||||
// BOOL _isAnimatingToolbar;
|
||||
|
||||
|
||||
CGRect _resizeFrame;
|
||||
CGPoint _mouseDraggedPoint;
|
||||
@@ -160,10 +161,15 @@ var _CPWindowViewResizeIndicatorImage = nil;
|
||||
if (!visibleFrame)
|
||||
visibleFrame = [[CPPlatformWindow primaryPlatformWindow] visibleFrame];
|
||||
|
||||
var minPointY = 0;
|
||||
|
||||
if([CPMenu menuBarVisible])
|
||||
minPointY = [[CPApp mainMenu] menuBarHeight];
|
||||
|
||||
var restrictedPoint = CGPointMake(0, 0);
|
||||
|
||||
restrictedPoint.x = MIN(MAX(aPoint.x, -_frame.size.width + 4.0), CGRectGetMaxX(visibleFrame) - 4.0);
|
||||
restrictedPoint.y = MIN(MAX(aPoint.y, 0.0), CGRectGetMaxY(visibleFrame) - 8.0);
|
||||
restrictedPoint.y = MIN(MAX(aPoint.y, minPointY), CGRectGetMaxY(visibleFrame) - 8.0);
|
||||
|
||||
return restrictedPoint;
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@
|
||||
{
|
||||
var theWindow = [self window];
|
||||
|
||||
if ([theWindow respondsToSelector:@selector(becomesKeyOnlyIfNeeded)] && [theWindow becomesKeyOnlyIfNeeded])
|
||||
if ([theWindow respondsToSelector:@selector(becomesKeyOnlyIfNeeded)] && [theWindow becomesKeyOnlyIfNeeded])
|
||||
[theWindow orderFront:aSender];
|
||||
else
|
||||
[theWindow makeKeyAndOrderFront:aSender];
|
||||
@@ -173,6 +173,14 @@
|
||||
|
||||
if (_window === nil && [_cibOwner isKindOfClass:[CPDocument class]])
|
||||
[self setWindow:[_cibOwner valueForKey:@"window"]];
|
||||
|
||||
if (!_window)
|
||||
{
|
||||
var reason = [CPString stringWithFormat:@"Window for %@ could not be loaded from Cib or no window specified. \
|
||||
Override loadWindow to load the window manually.", self];
|
||||
|
||||
[CPException raise:CPInternalInconsistencyException reason:reason];
|
||||
}
|
||||
|
||||
[self windowDidLoad];
|
||||
[_document windowControllerDidLoadCib:self];
|
||||
|
||||
@@ -37,7 +37,13 @@
|
||||
|
||||
- (void)setCustomClassName:(CPString)aClassName
|
||||
{
|
||||
if (_className === aClassName)
|
||||
return;
|
||||
|
||||
_className = aClassName;
|
||||
|
||||
[self setNeedsDisplay:YES];
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -97,9 +103,7 @@ var _CPCibCustomViewClassNameKey = @"_CPCibCustomViewClassNameKey";
|
||||
[view setBounds:[self bounds]];
|
||||
|
||||
// Since the object replacement logic hasn't had a chance to kick in yet, we need to do it manually:
|
||||
|
||||
// we need to copy subviews since each time we add a subview to a different view its removed from the original subviews array
|
||||
var subviews = [[self subviews] copy],
|
||||
var subviews = [self subviews],
|
||||
index = 0,
|
||||
count = subviews.length;
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ var _CPCibWindowTemplateMinSizeKey = @"_CPCibWindowTemplateMinS
|
||||
_CPCibWindowTemplateWindowViewKey = @"_CPCibWindowTemplateWindowViewKey",
|
||||
|
||||
_CPCibWindowTemplateWindowAutorecalculatesKeyViewLoop = @"_CPCibWindowTemplateWindowAutorecalculatesKeyViewLoop";
|
||||
_CPCibWindowTemplateWindowIsFullPlatformWindowKey = @"_CPCibWindowTemplateWindowIsFullPlatformWindowKey";
|
||||
_CPCibWindowTemplateWindowIsFullPlatformWindowKey = @"_CPCibWindowTemplateWindowIsFullPlatformWindowKey";
|
||||
|
||||
@implementation _CPCibWindowTemplate : CPObject
|
||||
{
|
||||
@@ -134,9 +134,8 @@ var _CPCibWindowTemplateMinSizeKey = @"_CPCibWindowTemplateMinS
|
||||
[theWindow setMinSize:_minSize];
|
||||
if (_maxSize)
|
||||
[theWindow setMaxSize:_maxSize];
|
||||
[theWindow setLevel:CPFloatingWindowLevel];
|
||||
|
||||
//[result setHidesOnDeactivate:(_wtFlags&0x80000000)?YES:NO];
|
||||
//[result setHidesOnDeactivate:(_wtFlags&0x80000000)?YES:NO];
|
||||
[theWindow setTitle:_windowTitle];
|
||||
|
||||
// FIXME: we can't autoresize yet...
|
||||
|
||||
@@ -53,4 +53,20 @@
|
||||
#endif
|
||||
}
|
||||
|
||||
+ (void)hideOtherApplications:(id)aSender
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
if (typeof window["cpHideOtherApplications"] === "function")
|
||||
window.cpHideOtherApplications();
|
||||
#endif
|
||||
}
|
||||
|
||||
+ (void)hide:(id)aSender
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
if (typeof window["cpHide"] === "function")
|
||||
window.cpHide();
|
||||
#endif
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -30,6 +30,10 @@ var PrimaryPlatformWindow = NULL;
|
||||
|
||||
Object _charCodes;
|
||||
unsigned _keyCode;
|
||||
unsigned _lastKey;
|
||||
BOOL _capsLockActive;
|
||||
BOOL _ignoreNativeCopyOrCutEvent;
|
||||
BOOL _ignoreNativePastePreparation;
|
||||
|
||||
BOOL _DOMEventMode;
|
||||
|
||||
@@ -41,6 +45,11 @@ var PrimaryPlatformWindow = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
+ (CPSet)visiblePlatformWindows
|
||||
{
|
||||
return [CPSet set];
|
||||
}
|
||||
|
||||
+ (CPPlatformWindow)primaryPlatformWindow
|
||||
{
|
||||
return PrimaryPlatformWindow;
|
||||
@@ -95,7 +104,7 @@ var PrimaryPlatformWindow = NULL;
|
||||
|
||||
frame.origin = CGPointMakeZero();
|
||||
|
||||
if ([CPMenu menuBarVisible])
|
||||
if ([CPMenu menuBarVisible] && [CPPlatformWindow primaryPlatformWindow] === self)
|
||||
{
|
||||
var menuBarHeight = [[CPApp mainMenu] menuBarHeight];
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
* until the first key is released. This can cause a key event to be fired with
|
||||
* a keyCode for the first key and a charCode for the second key.
|
||||
*
|
||||
* Safari in keypress
|
||||
* Safari 2 in keypress (not supported)
|
||||
*
|
||||
* charCode keyCode which
|
||||
* ENTER: 13 13 13
|
||||
@@ -117,26 +117,12 @@
|
||||
|
||||
@import "CPPlatform.j"
|
||||
@import "CPPlatformWindow.j"
|
||||
@import "CPPlatformWindow+DOMKeys.j"
|
||||
|
||||
#import "../../CoreGraphics/CGGeometry.h"
|
||||
|
||||
|
||||
var DoubleClick = "dblclick",
|
||||
MouseDown = "mousedown",
|
||||
MouseUp = "mouseup",
|
||||
MouseMove = "mousemove",
|
||||
MouseDrag = "mousedrag",
|
||||
KeyUp = "keyup",
|
||||
KeyDown = "keydown",
|
||||
KeyPress = "keypress",
|
||||
Copy = "copy",
|
||||
Paste = "paste",
|
||||
Resize = "resize",
|
||||
ScrollWheel = "mousewheel",
|
||||
TouchStart = "touchstart",
|
||||
TouchMove = "touchmove",
|
||||
TouchEnd = "touchend",
|
||||
TouchCancel = "touchcancel";
|
||||
// List of all open native windows
|
||||
var PlatformWindows = [CPSet set];
|
||||
|
||||
// Define up here so compressor knows about em.
|
||||
var CPDOMEventGetClickCount,
|
||||
@@ -148,9 +134,12 @@ var CPDOMEventGetClickCount,
|
||||
//might be mac only, we should investigate futher later.
|
||||
var KeyCodesToPrevent = {},
|
||||
CharacterKeysToPrevent = {},
|
||||
KeyCodesWithoutKeyPressEvents = { '8':1, '9':1, '16':1, '33':1, '34':1, '35':1, '36':1, '37':1, '38':1, '39':1, '40':1, '46':1, '33':1, '34':1 };
|
||||
MozKeyCodeToKeyCodeMap = {
|
||||
61: 187, // =, equals
|
||||
59: 186 // ;, semicolon
|
||||
};
|
||||
|
||||
var CTRL_KEY_CODE = 17;
|
||||
KeyCodesToPrevent[CPKeyCodes.A] = YES;
|
||||
|
||||
var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
|
||||
|
||||
@@ -251,6 +240,7 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
|
||||
|
||||
_DOMBodyElement.webkitTouchCallout = "none";
|
||||
|
||||
// This guy fixes an issue in Firefox where if you focus the URL field, we stop getting key events
|
||||
_DOMFocusElement = theDocument.createElement("input");
|
||||
|
||||
_DOMFocusElement.style.position = "absolute";
|
||||
@@ -261,11 +251,11 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
|
||||
_DOMBodyElement.appendChild(_DOMFocusElement);
|
||||
|
||||
// Create Native Pasteboard handler.
|
||||
_DOMPasteboardElement = theDocument.createElement("input");
|
||||
_DOMPasteboardElement = theDocument.createElement("textarea");
|
||||
|
||||
_DOMPasteboardElement.style.position = "absolute";
|
||||
_DOMPasteboardElement.style.top = "-10000px";
|
||||
_DOMPasteboardElement.style.zIndex = "99";
|
||||
_DOMPasteboardElement.style.zIndex = "999";
|
||||
|
||||
_DOMBodyElement.appendChild(_DOMPasteboardElement);
|
||||
|
||||
@@ -283,6 +273,14 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
|
||||
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); },
|
||||
|
||||
pasteEventSelector = @selector(pasteEvent:),
|
||||
pasteEventImplementation = class_getMethodImplementation(theClass, pasteEventSelector),
|
||||
pasteEventCallback = function (anEvent) {pasteEventImplementation(self, nil, anEvent); },
|
||||
|
||||
keyEventSelector = @selector(keyEvent:),
|
||||
keyEventImplementation = class_getMethodImplementation(theClass, keyEventSelector),
|
||||
keyEventCallback = function (anEvent) { keyEventImplementation(self, nil, anEvent); },
|
||||
@@ -299,6 +297,7 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
|
||||
touchEventImplementation = class_getMethodImplementation(theClass, touchEventSelector),
|
||||
touchEventCallback = function (anEvent) { touchEventImplementation(self, nil, anEvent); };
|
||||
|
||||
|
||||
if (theDocument.addEventListener)
|
||||
{
|
||||
if ([CPPlatform supportsDragAndDrop])
|
||||
@@ -315,6 +314,10 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
|
||||
theDocument.addEventListener("mousedown", mouseEventCallback, NO);
|
||||
theDocument.addEventListener("mousemove", mouseEventCallback, NO);
|
||||
|
||||
theDocument.addEventListener("beforecopy", copyEventCallback, NO);
|
||||
theDocument.addEventListener("beforecut", copyEventCallback, NO);
|
||||
theDocument.addEventListener("beforepaste", pasteEventCallback, NO);
|
||||
|
||||
theDocument.addEventListener("keyup", keyEventCallback, NO);
|
||||
theDocument.addEventListener("keydown", keyEventCallback, NO);
|
||||
theDocument.addEventListener("keypress", keyEventCallback, NO);
|
||||
@@ -342,6 +345,10 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
|
||||
theDocument.removeEventListener("keydown", keyEventCallback, NO);
|
||||
theDocument.removeEventListener("keypress", keyEventCallback, NO);
|
||||
|
||||
theDocument.removeEventListener("beforecopy", copyEventCallback, NO);
|
||||
theDocument.removeEventListener("beforecut", copyEventCallback, NO);
|
||||
theDocument.removeEventListener("beforepaste", pasteEventCallback, NO);
|
||||
|
||||
theDocument.removeEventListener("touchstart", touchEventCallback, NO);
|
||||
theDocument.removeEventListener("touchend", touchEventCallback, NO);
|
||||
theDocument.removeEventListener("touchmove", touchEventCallback, NO);
|
||||
@@ -354,6 +361,8 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
|
||||
|
||||
//_DOMWindow.removeEventListener("beforeunload", this, NO);
|
||||
|
||||
[PlatformWindows removeObject:self];
|
||||
|
||||
self._DOMWindow = nil;
|
||||
}, NO);
|
||||
}
|
||||
@@ -400,11 +409,18 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
|
||||
|
||||
//_DOMWindow.removeEvent("beforeunload", this);
|
||||
|
||||
[PlatformWindows removeObject:self];
|
||||
|
||||
self._DOMWindow = nil;
|
||||
}, NO);
|
||||
}
|
||||
}
|
||||
|
||||
+ (CPSet)visiblePlatformWindows
|
||||
{
|
||||
return PlatformWindows;
|
||||
}
|
||||
|
||||
- (void)orderFront:(id)aSender
|
||||
{
|
||||
if (_DOMWindow)
|
||||
@@ -412,6 +428,8 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
|
||||
|
||||
_DOMWindow = window.open("", "_blank", "menubar=no,location=no,resizable=yes,scrollbars=no,status=no,left=" + _CGRectGetMinX(_contentRect) + ",top=" + _CGRectGetMinY(_contentRect) + ",width=" + _CGRectGetWidth(_contentRect) + ",height=" + _CGRectGetHeight(_contentRect));
|
||||
|
||||
[PlatformWindows addObject:self];
|
||||
|
||||
// FIXME: cpSetFrame?
|
||||
_DOMWindow.document.write("<html><head></head><body style = 'background-color:transparent;'></body></html>");
|
||||
_DOMWindow.document.close();
|
||||
@@ -425,6 +443,8 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
|
||||
_DOMWindow.cpSetShadowStyle(_shadowStyle);
|
||||
}
|
||||
|
||||
_DOMWindow.document.body.style.cursor = [[CPCursor currentCursor] _cssString];
|
||||
|
||||
[self registerDOMWindow];
|
||||
}
|
||||
|
||||
@@ -538,12 +558,11 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
|
||||
(aDOMEvent.ctrlKey ? CPControlKeyMask : 0) |
|
||||
(aDOMEvent.altKey ? CPAlternateKeyMask : 0) |
|
||||
(aDOMEvent.metaKey ? CPCommandKeyMask : 0);
|
||||
|
||||
|
||||
//We want to stop propagation if this is a command key AND this character or keycode has been added to our blacklist
|
||||
StopDOMEventPropagation = !(modifierFlags & (CPControlKeyMask | CPCommandKeyMask)) ||
|
||||
|
||||
//We want to stop propagation if this is a command key AND this character or keycode has been added to our blacklist
|
||||
StopDOMEventPropagation = !!(!(modifierFlags & (CPControlKeyMask | CPCommandKeyMask)) ||
|
||||
CharacterKeysToPrevent[String.fromCharCode(aDOMEvent.keyCode || aDOMEvent.charCode).toLowerCase()] ||
|
||||
KeyCodesToPrevent[aDOMEvent.keyCode];
|
||||
KeyCodesToPrevent[aDOMEvent.keyCode]);
|
||||
|
||||
var isNativePasteEvent = NO,
|
||||
isNativeCopyOrCutEvent = NO,
|
||||
@@ -552,56 +571,80 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
|
||||
switch (aDOMEvent.type)
|
||||
{
|
||||
case "keydown": // Grab and store the keycode now since it is correct and consistent at this point.
|
||||
_keyCode = aDOMEvent.keyCode;
|
||||
if (aDOMEvent.keyCode.keyCode in MozKeyCodeToKeyCodeMap)
|
||||
_keyCode = MozKeyCodeToKeyCodeMap[aDOMEvent.keyCode];
|
||||
else
|
||||
_keyCode = aDOMEvent.keyCode;
|
||||
|
||||
var characters = String.fromCharCode(_keyCode).toLowerCase();
|
||||
overrideCharacters = modifierFlags & CPShiftKeyMask ? characters.toUpperCase() : characters;
|
||||
|
||||
// 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" && (modifierFlags & CPPlatformActionKeyMask))
|
||||
overrideCharacters = (modifierFlags & CPShiftKeyMask || _capsLockActive) ? characters.toUpperCase() : characters;
|
||||
|
||||
// check for caps lock state
|
||||
if (_keyCode === CPKeyCodes.CAPS_LOCK)
|
||||
_capsLockActive = YES;
|
||||
|
||||
if (modifierFlags & (CPControlKeyMask | CPCommandKeyMask))
|
||||
{
|
||||
_DOMPasteboardElement.select();
|
||||
_DOMPasteboardElement.value = "";
|
||||
|
||||
isNativePasteEvent = YES;
|
||||
//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 eligibleForCopyPaste = [self _validateCopyCutOrPasteEvent: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" && eligibleForCopyPaste)
|
||||
{
|
||||
if (!_ignoreNativePastePreparation)
|
||||
{
|
||||
_DOMPasteboardElement.select();
|
||||
_DOMPasteboardElement.value = "";
|
||||
}
|
||||
|
||||
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") && eligibleForCopyPaste)
|
||||
{
|
||||
isNativeCopyOrCutEvent = YES;
|
||||
|
||||
if (_ignoreNativeCopyOrCutEvent)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (CPKeyCodes.firesKeyPressEvent(_keyCode, _lastKey, aDOMEvent.shiftKey, aDOMEvent.ctrlKey, aDOMEvent.altKey))
|
||||
{
|
||||
// this branch is taken by events which fire keydown, keypress, and keyup.
|
||||
// this is the only time we'll ALLOW character keys to propagate (needed for text fields)
|
||||
StopDOMEventPropagation = NO;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
//this branch is taken by "remedial" key events
|
||||
// In this state we continue to keypress and send the CPEvent
|
||||
}
|
||||
|
||||
// Normally we return now because we let keypress send the actual CPEvent keyDown event, since we don't have
|
||||
// a complete set of information yet.
|
||||
|
||||
// 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") && (modifierFlags & CPPlatformActionKeyMask))
|
||||
isNativeCopyOrCutEvent = YES;
|
||||
case "keypress":
|
||||
// we unconditionally break on keypress events with modifiers,
|
||||
// because we forced the event to be sent on the keydown
|
||||
if (aDOMEvent.type === "keypress" && (modifierFlags & (CPControlKeyMask | CPCommandKeyMask)))
|
||||
break;
|
||||
|
||||
// Also, certain browsers (IE and Safari), have broken keyboard supportwhere they don't send keypresses for certain events.
|
||||
// So, allow the keypress event to handle the event if we are not a browser with broken (remedial) key support...
|
||||
else if (!CPFeatureIsCompatible(CPJavascriptRemedialKeySupport))
|
||||
return;
|
||||
|
||||
// Or, if this is not one of those special keycodes, and also not a ctrl+event
|
||||
else if (!KeyCodesWithoutKeyPressEvents[_keyCode] && (_keyCode == CTRL_KEY_CODE || !(modifierFlags & CPControlKeyMask)))
|
||||
return;
|
||||
|
||||
// If this is in fact our broke state, continue to keypress and send the keydown.
|
||||
case "keypress": // If the source of this event is our pasteboard element, then simply let it continue
|
||||
// as normal, so that the paste event can successfully complete.
|
||||
|
||||
if ((aDOMEvent.target || aDOMEvent.srcElement) == _DOMPasteboardElement)
|
||||
return;
|
||||
|
||||
var keyCode = _keyCode,
|
||||
charCode = aDOMEvent.keyCode || aDOMEvent.charCode,
|
||||
isARepeat = (_charCodes[keyCode] != nil);
|
||||
|
||||
_lastKey = keyCode;
|
||||
_charCodes[keyCode] = charCode;
|
||||
|
||||
var characters = overrideCharacters || String.fromCharCode(charCode),
|
||||
charactersIgnoringModifiers = characters.toLowerCase();
|
||||
|
||||
// Safari won't send proper capitalization during cmd-key events
|
||||
if (!overrideCharacters && (modifierFlags & CPCommandKeyMask) && (modifierFlags & CPShiftKeyMask))
|
||||
if (!overrideCharacters && (modifierFlags & CPCommandKeyMask) && ((modifierFlags & CPShiftKeyMask) || _capsLockActive))
|
||||
characters = characters.toUpperCase();
|
||||
|
||||
event = [CPEvent keyEventWithType:CPKeyDown location:location modifierFlags:modifierFlags
|
||||
@@ -612,7 +655,6 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
|
||||
{
|
||||
_pasteboardKeyDownEvent = event;
|
||||
window.setNativeTimeout(function () { [self _checkPasteboardElement] }, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -620,12 +662,20 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
|
||||
case "keyup": var keyCode = aDOMEvent.keyCode,
|
||||
charCode = _charCodes[keyCode];
|
||||
|
||||
_keyCode = -1;
|
||||
_lastKey = -1;
|
||||
_charCodes[keyCode] = nil;
|
||||
|
||||
_ignoreNativeCopyOrCutEvent = NO;
|
||||
_ignoreNativePastePreparation = NO;
|
||||
|
||||
// check for caps lock state
|
||||
if (keyCode === CPKeyCodes.CAPS_LOCK)
|
||||
_capsLockActive = NO;
|
||||
|
||||
var characters = String.fromCharCode(charCode),
|
||||
charactersIgnoringModifiers = characters.toLowerCase();
|
||||
|
||||
if (!(modifierFlags & CPShiftKeyMask))
|
||||
if (!(modifierFlags & CPShiftKeyMask) && (modifierFlags & CPCommandKeyMask) && !_capsLockActive)
|
||||
characters = charactersIgnoringModifiers;
|
||||
|
||||
event = [CPEvent keyEventWithType:CPKeyUp location:location modifierFlags:modifierFlags
|
||||
@@ -633,40 +683,126 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
|
||||
characters:characters charactersIgnoringModifiers:charactersIgnoringModifiers isARepeat:NO keyCode:keyCode];
|
||||
break;
|
||||
}
|
||||
|
||||
if (event)
|
||||
|
||||
if (event && !isNativePasteEvent)
|
||||
{
|
||||
event._DOMEvent = aDOMEvent;
|
||||
|
||||
|
||||
[CPApp sendEvent:event];
|
||||
|
||||
if (isNativeCopyOrCutEvent)
|
||||
{
|
||||
var pasteboard = [CPPasteboard generalPasteboard],
|
||||
types = [pasteboard types];
|
||||
|
||||
// If this is a native copy event, then check if the pasteboard has anything in it.
|
||||
if (types.length)
|
||||
{
|
||||
if ([types indexOfObjectIdenticalTo:CPStringPboardType] != CPNotFound)
|
||||
_DOMPasteboardElement.value = [pasteboard stringForType:CPStringPboardType];
|
||||
else
|
||||
_DOMPasteboardElement.value = [pasteboard _generateStateUID];
|
||||
|
||||
_DOMPasteboardElement.select();
|
||||
|
||||
window.setNativeTimeout(function() { [self _clearPasteboardElement]; }, 0);
|
||||
}
|
||||
|
||||
return;
|
||||
[self _primePasteboardElement];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (StopDOMEventPropagation)
|
||||
CPDOMEventStop(aDOMEvent, self);
|
||||
|
||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||
}
|
||||
|
||||
- (void)copyEvent:(DOMEvent)aDOMEvent
|
||||
{
|
||||
if ([self _validateCopyCutOrPasteEvent: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 cut = aDOMEvent.type === "beforecut",
|
||||
keyCode = cut ? CPKeyCodes.X : CPKeyCodes.C,
|
||||
characters = cut ? "x" : "c",
|
||||
timestamp = aDOMEvent.timeStamp ? aDOMEvent.timeStamp : new Date(),
|
||||
windowNumber = [[CPApp keyWindow] windowNumber],
|
||||
modifierFlags = CPPlatformActionKeyMask;
|
||||
|
||||
event = [CPEvent keyEventWithType:CPKeyDown location:location modifierFlags:modifierFlags
|
||||
timestamp:timestamp windowNumber:windowNumber context:nil
|
||||
characters:characters charactersIgnoringModifiers:characters isARepeat:NO keyCode:keyCode];
|
||||
|
||||
event._DOMEvent = aDOMEvent;
|
||||
[CPApp sendEvent:event];
|
||||
|
||||
[self _primePasteboardElement];
|
||||
|
||||
//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];
|
||||
}
|
||||
|
||||
- (void)pasteEvent:(DOMEvent)aDOMEvent
|
||||
{
|
||||
if ([self _validateCopyCutOrPasteEvent:aDOMEvent flags:CPPlatformActionKeyMask])
|
||||
{
|
||||
_DOMPasteboardElement.focus();
|
||||
_DOMPasteboardElement.select();
|
||||
_DOMPasteboardElement.value = "";
|
||||
_ignoreNativePastePreparation = YES;
|
||||
}
|
||||
|
||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||
}
|
||||
|
||||
- (void)_validateCopyCutOrPasteEvent:(DOMEvent)aDOMEvent flags:(unsigned)modifierFlags
|
||||
{
|
||||
return (
|
||||
((aDOMEvent.target || aDOMEvent.srcElement).nodeName.toUpperCase() !== "INPUT" &&
|
||||
(aDOMEvent.target || aDOMEvent.srcElement).nodeName.toUpperCase() !== "TEXTAREA"
|
||||
) || aDOMEvent.target === _DOMPasteboardElement
|
||||
) &&
|
||||
(modifierFlags & CPPlatformActionKeyMask);
|
||||
}
|
||||
|
||||
- (void)_primePasteboardElement
|
||||
{
|
||||
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
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
- (void)scrollEvent:(DOMEvent)aDOMEvent
|
||||
@@ -760,6 +896,9 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
|
||||
// We should pay attention to mouse down and mouse up in conjunction with this.
|
||||
//window.liveResize = YES;
|
||||
|
||||
if ([CPPlatform isBrowser])
|
||||
[CPApp._activeMenu cancelTracking];
|
||||
|
||||
var oldSize = [self contentRect].size;
|
||||
|
||||
[self updateFromNativeContentRect];
|
||||
@@ -1077,8 +1216,16 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
|
||||
StopDOMEventPropagation = !aFlag;
|
||||
}
|
||||
|
||||
- (BOOL)_willPropagateCurrentDOMEvent
|
||||
{
|
||||
return !StopDOMEventPropagation;
|
||||
}
|
||||
|
||||
- (CPWindow)hitTest:(CPPoint)location
|
||||
{if (self._only) return self._only;
|
||||
{
|
||||
if (self._only)
|
||||
return self._only;
|
||||
|
||||
var levels = _windowLevels,
|
||||
layers = _windowLayers,
|
||||
levelCount = levels.length,
|
||||
@@ -1101,37 +1248,6 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
|
||||
return theWindow;
|
||||
}
|
||||
|
||||
- (void)_checkPasteboardElement
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
/*!
|
||||
When using command (mac) or control (windows), keys are propagated to the browser by default.
|
||||
To prevent a character key from propagating (to prevent its default action, and instead use it
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
* CPPlatformWindow+DOMKeys.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Ross Boucher.
|
||||
* Copyright 2009, 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
|
||||
*/
|
||||
|
||||
// Keycodes taken and modified from Google Closure, available under Apache 2 License
|
||||
|
||||
CPKeyCodes = {
|
||||
BACKSPACE: 8,
|
||||
TAB: 9,
|
||||
NUM_CENTER: 12,
|
||||
ENTER: 13,
|
||||
SHIFT: 16,
|
||||
CTRL: 17,
|
||||
ALT: 18,
|
||||
PAUSE: 19,
|
||||
CAPS_LOCK: 20,
|
||||
ESC: 27,
|
||||
SPACE: 32,
|
||||
PAGE_UP: 33, // also NUM_NORTH_EAST
|
||||
PAGE_DOWN: 34, // also NUM_SOUTH_EAST
|
||||
END: 35, // also NUM_SOUTH_WEST
|
||||
HOME: 36, // also NUM_NORTH_WEST
|
||||
LEFT: 37, // also NUM_WEST
|
||||
UP: 38, // also NUM_NORTH
|
||||
RIGHT: 39, // also NUM_EAST
|
||||
DOWN: 40, // also NUM_SOUTH
|
||||
PRINT_SCREEN: 44,
|
||||
INSERT: 45, // also NUM_INSERT
|
||||
DELETE: 46, // also NUM_DELETE
|
||||
ZERO: 48,
|
||||
ONE: 49,
|
||||
TWO: 50,
|
||||
THREE: 51,
|
||||
FOUR: 52,
|
||||
FIVE: 53,
|
||||
SIX: 54,
|
||||
SEVEN: 55,
|
||||
EIGHT: 56,
|
||||
NINE: 57,
|
||||
QUESTION_MARK: 63, // needs localization
|
||||
A: 65,
|
||||
B: 66,
|
||||
C: 67,
|
||||
D: 68,
|
||||
E: 69,
|
||||
F: 70,
|
||||
G: 71,
|
||||
H: 72,
|
||||
I: 73,
|
||||
J: 74,
|
||||
K: 75,
|
||||
L: 76,
|
||||
M: 77,
|
||||
N: 78,
|
||||
O: 79,
|
||||
P: 80,
|
||||
Q: 81,
|
||||
R: 82,
|
||||
S: 83,
|
||||
T: 84,
|
||||
U: 85,
|
||||
V: 86,
|
||||
W: 87,
|
||||
X: 88,
|
||||
Y: 89,
|
||||
Z: 90,
|
||||
META: 91,
|
||||
CONTEXT_MENU: 93,
|
||||
NUM_ZERO: 96,
|
||||
NUM_ONE: 97,
|
||||
NUM_TWO: 98,
|
||||
NUM_THREE: 99,
|
||||
NUM_FOUR: 100,
|
||||
NUM_FIVE: 101,
|
||||
NUM_SIX: 102,
|
||||
NUM_SEVEN: 103,
|
||||
NUM_EIGHT: 104,
|
||||
NUM_NINE: 105,
|
||||
NUM_MULTIPLY: 106,
|
||||
NUM_PLUS: 107,
|
||||
NUM_MINUS: 109,
|
||||
NUM_PERIOD: 110,
|
||||
NUM_DIVISION: 111,
|
||||
F1: 112,
|
||||
F2: 113,
|
||||
F3: 114,
|
||||
F4: 115,
|
||||
F5: 116,
|
||||
F6: 117,
|
||||
F7: 118,
|
||||
F8: 119,
|
||||
F9: 120,
|
||||
F10: 121,
|
||||
F11: 122,
|
||||
F12: 123,
|
||||
NUMLOCK: 144,
|
||||
SEMICOLON: 186, // needs localization
|
||||
DASH: 189, // needs localization
|
||||
EQUALS: 187, // needs localization
|
||||
COMMA: 188, // needs localization
|
||||
PERIOD: 190, // needs localization
|
||||
SLASH: 191, // needs localization
|
||||
APOSTROPHE: 192, // needs localization
|
||||
SINGLE_QUOTE: 222, // needs localization
|
||||
OPEN_SQUARE_BRACKET: 219, // needs localization
|
||||
BACKSLASH: 220, // needs localization
|
||||
CLOSE_SQUARE_BRACKET: 221, // needs localization
|
||||
WIN_KEY: 224,
|
||||
MAC_FF_META: 224, // Firefox (Gecko) fires this for the meta key instead of 91
|
||||
WIN_IME: 229
|
||||
};
|
||||
|
||||
|
||||
/*!
|
||||
* Returns true if the key fires a keypress event in the current browser.
|
||||
*
|
||||
* Accoridng to MSDN [1] IE only fires keypress events for the following keys:
|
||||
* - Letters: A - Z (uppercase and lowercase)
|
||||
* - Numerals: 0 - 9
|
||||
* - Symbols: ! @ # $ % ^ & * ( ) _ - + = < [ ] { } , . / ? \ | ' ` " ~
|
||||
* - System: ESC, SPACEBAR, ENTER
|
||||
*
|
||||
* That's not entirely correct though, for instance there's no distinction
|
||||
* between upper and lower case letters.
|
||||
*
|
||||
* [1] http://msdn2.microsoft.com/en-us/library/ms536939(VS.85).aspx)
|
||||
*
|
||||
* Safari is similar to IE, but does not fire keypress for ESC.
|
||||
*
|
||||
* Additionally, IE6 does not fire keydown or keypress events for letters when
|
||||
* the control or alt keys are held down and the shift key is not. IE7 does
|
||||
* fire keydown in these cases, though, but not keypress.
|
||||
*
|
||||
* @param keyCode A key code.
|
||||
* @param opt_heldKeyCode Key code of a currently-held key.
|
||||
* @param opt_shiftKey Whether the shift key is held down.
|
||||
* @param opt_ctrlKey Whether the control key is held down.
|
||||
* @param opt_altKey Whether the alt key is held down.
|
||||
* @return Returns YES if it's a key that fires a keypress event.
|
||||
*/
|
||||
CPKeyCodes.firesKeyPressEvent = function(keyCode, opt_heldKeyCode, opt_shiftKey, opt_ctrlKey, opt_altKey)
|
||||
{
|
||||
if (!CPFeatureIsCompatible(CPJavascriptRemedialKeySupport))
|
||||
return true;
|
||||
|
||||
if (CPBrowserIsOperatingSystem(CPMacOperatingSystem) && opt_altKey)
|
||||
return CPKeyCodes.isCharacterKey(keyCode);
|
||||
|
||||
// Alt but not AltGr which is represented as Alt+Ctrl.
|
||||
if (opt_altKey && !opt_ctrlKey)
|
||||
return false;
|
||||
|
||||
// Saves Ctrl or Alt + key for IE7, which won't fire keypress.
|
||||
if (CPBrowserIsEngine(CPInternetExplorerBrowserEngine) && !opt_shiftKey && (opt_ctrlKey || opt_altKey))
|
||||
return false;
|
||||
|
||||
// When Ctrl+<somekey> is held in IE, it only fires a keypress once, but it
|
||||
// continues to fire keydown events as the event repeats.
|
||||
if (CPBrowserIsEngine(CPInternetExplorerBrowserEngine) && opt_ctrlKey && opt_heldKeyCode == keyCode)
|
||||
return false;
|
||||
|
||||
switch (keyCode)
|
||||
{
|
||||
case CPKeyCodes.ENTER: return true;
|
||||
case CPKeyCodes.ESC: return !CPBrowserIsEngine(CPWebKitBrowserEngine);
|
||||
}
|
||||
|
||||
return CPKeyCodes.isCharacterKey(keyCode);
|
||||
};
|
||||
|
||||
|
||||
/*!
|
||||
* Test for whether or not a given keyCode represents a character key.
|
||||
*
|
||||
* @param keyCode A key code.
|
||||
* @return Returns YES if the keyCode is a character key.
|
||||
*/
|
||||
CPKeyCodes.isCharacterKey = function(keyCode)
|
||||
{
|
||||
if (keyCode >= CPKeyCodes.ZERO && keyCode <= CPKeyCodes.NINE)
|
||||
return true;
|
||||
|
||||
if (keyCode >= CPKeyCodes.NUM_ZERO && keyCode <= CPKeyCodes.NUM_MULTIPLY)
|
||||
return true;
|
||||
|
||||
if (keyCode >= CPKeyCodes.A && keyCode <= CPKeyCodes.Z)
|
||||
return true;
|
||||
|
||||
switch (keyCode)
|
||||
{
|
||||
case CPKeyCodes.SPACE:
|
||||
case CPKeyCodes.QUESTION_MARK:
|
||||
case CPKeyCodes.NUM_PLUS:
|
||||
case CPKeyCodes.NUM_MINUS:
|
||||
case CPKeyCodes.NUM_PERIOD:
|
||||
case CPKeyCodes.NUM_DIVISION:
|
||||
case CPKeyCodes.SEMICOLON:
|
||||
case CPKeyCodes.DASH:
|
||||
case CPKeyCodes.EQUALS:
|
||||
case CPKeyCodes.COMMA:
|
||||
case CPKeyCodes.PERIOD:
|
||||
case CPKeyCodes.SLASH:
|
||||
case CPKeyCodes.APOSTROPHE:
|
||||
case CPKeyCodes.SINGLE_QUOTE:
|
||||
case CPKeyCodes.OPEN_SQUARE_BRACKET:
|
||||
case CPKeyCodes.BACKSLASH:
|
||||
case CPKeyCodes.CLOSE_SQUARE_BRACKET:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,18 @@
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<div>Test custom cursors with .cur images.</div>
|
||||
<div style="width: 20px; height: 20px; margin: 10px; background: #ddd; cursor: url(disappearingItemCursor.cur), default">disappearingItemCursor</div>
|
||||
<div style="width: 20px; height: 20px; margin: 10px; background: #ddd; cursor: url(closedHandCursor.cur), default">closedHandCursor</div>
|
||||
<div style="width: 20px; height: 20px; margin: 10px; background: #ddd; cursor: url(contextMenuCursor.cur), default">contextMenuCursor</div>
|
||||
<div style="width: 20px; height: 20px; margin: 10px; background: #ddd; cursor: url(dragCopyCursor.cur), default">dragCopyCursor</div>
|
||||
<div style="width: 20px; height: 20px; margin: 10px; background: #ddd; cursor: url(dragLinkCursor.cur), default">dragLinkCursor</div>
|
||||
<div style="width: 20px; height: 20px; margin: 10px; background: #ddd; cursor: url(openHandCursor.cur), default">openHandCursor</div>
|
||||
<div style="width: 20px; height: 20px; margin: 10px; background: #ddd; cursor: url(resizeDownCursor.cur), default">resizeDownCursor</div>
|
||||
<div style="width: 20px; height: 20px; margin: 10px; background: #ddd; cursor: url(resizeLeftCursor.cur), default">resizeLeftCursor</div>
|
||||
<div style="width: 20px; height: 20px; margin: 10px; background: #ddd; cursor: url(resizeRightCursor.cur), default">resizeRightCursor</div>
|
||||
<div style="width: 20px; height: 20px; margin: 10px; background: #ddd; cursor: url(resizeUpCursor.cur), default">resizeUpCursor</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
Before Width: | Height: | Size: 107 B After Width: | Height: | Size: 118 B |
|
Before Width: | Height: | Size: 137 B After Width: | Height: | Size: 147 B |
|
After Width: | Height: | Size: 151 B |
|
After Width: | Height: | Size: 13 KiB |
@@ -177,8 +177,8 @@ var BKLearnMoreToolbarItemIdentifier = @"BKLearnMoreToolbarItemId
|
||||
|
||||
var width = CGRectGetWidth([popUpButton frame]);
|
||||
|
||||
[toolbarItem setMinSize:CGSizeMake(width + 20.0, 32.0)];
|
||||
[toolbarItem setMaxSize:CGSizeMake(width + 20.0, 32.0)];
|
||||
[toolbarItem setMinSize:CGSizeMake(width + 20.0, 24.0)];
|
||||
[toolbarItem setMaxSize:CGSizeMake(width + 20.0, 24.0)];
|
||||
}
|
||||
|
||||
else if (anItemIdentifier === BKBackgroundColorToolbarItemIdentifier)
|
||||
@@ -206,8 +206,8 @@ var BKLearnMoreToolbarItemIdentifier = @"BKLearnMoreToolbarItemId
|
||||
|
||||
var width = CGRectGetWidth([popUpButton frame]);
|
||||
|
||||
[toolbarItem setMinSize:CGSizeMake(width, 32.0)];
|
||||
[toolbarItem setMaxSize:CGSizeMake(width, 32.0)];
|
||||
[toolbarItem setMinSize:CGSizeMake(width, 24.0)];
|
||||
[toolbarItem setMaxSize:CGSizeMake(width, 24.0)];
|
||||
}
|
||||
else if (anItemIdentifier === BKLearnMoreToolbarItemIdentifier)
|
||||
{
|
||||
@@ -227,8 +227,8 @@ var BKLearnMoreToolbarItemIdentifier = @"BKLearnMoreToolbarItemId
|
||||
|
||||
var width = CGRectGetWidth([button frame]);
|
||||
|
||||
[toolbarItem setMinSize:CGSizeMake(width, 32.0)];
|
||||
[toolbarItem setMaxSize:CGSizeMake(width, 32.0)];
|
||||
[toolbarItem setMinSize:CGSizeMake(width, 24.0)];
|
||||
[toolbarItem setMaxSize:CGSizeMake(width, 24.0)];
|
||||
}
|
||||
|
||||
return toolbarItem;
|
||||
|
||||
@@ -340,7 +340,7 @@ var HORIZONTAL_MARGIN = 3.0,
|
||||
|
||||
textStyle.position = "absolute";
|
||||
textStyle.whiteSpace = "pre";
|
||||
textStyle.cursor = "default";
|
||||
|
||||
textStyle.zIndex = 200;
|
||||
textStyle.overflow = "hidden";
|
||||
|
||||
@@ -376,7 +376,8 @@ var HORIZONTAL_MARGIN = 3.0,
|
||||
shadowStyle.font = [_font ? _font : [CPFont systemFontOfSize:12.0] cssString];
|
||||
shadowStyle.position = "absolute";
|
||||
shadowStyle.whiteSpace = textStyle.whiteSpace;
|
||||
shadowStyle.cursor = "default";
|
||||
shadowStyle.color = [_textShadowColor cssString];
|
||||
|
||||
shadowStyle.zIndex = 150;
|
||||
shadowStyle.textOverflow = textStyle.textOverflow;
|
||||
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
#!/usr/bin/env narwhal
|
||||
|
||||
var FILE = require("file"),
|
||||
ENV = require("system").env,
|
||||
OS = require("os"),
|
||||
Jake = require("jake");
|
||||
/*,
|
||||
Clean = require("jake/clean");
|
||||
*/
|
||||
|
||||
require(FILE.absolute("../common.jake"));
|
||||
|
||||
$ENVIRONMENT_NARWHAL_PRODUCT = $ENVIRONMENT_DIR
|
||||
$ENVIRONMENT_PACKAGES_PRODUCT = FILE.join($ENVIRONMENT_NARWHAL_PRODUCT, 'packages')
|
||||
|
||||
$EXTERNALS = ['browserjs', 'jack', 'narwhal', 'ojunit'];
|
||||
$PACKAGES = ['browserjs', 'jack'];
|
||||
|
||||
function git_export(source, destination)
|
||||
{
|
||||
destination = FILE.join(FILE.absolute(destination), '');
|
||||
OS.system("cd " + source + " && git checkout-index -a -f \"--prefix=" + destination + "\"");
|
||||
};
|
||||
|
||||
filedir ($ENVIRONMENT_DIR, function()
|
||||
{
|
||||
FILE.mkdirs($ENVIRONMENT_DIR);
|
||||
});
|
||||
|
||||
task ("update_submodules", function()
|
||||
{
|
||||
if (ENV['NOSUBUP'])
|
||||
return;
|
||||
|
||||
if (executableExists("git"))
|
||||
OS.system("cd .. && git submodule init && git submodule update");
|
||||
else
|
||||
throw "Git not installed";
|
||||
});
|
||||
|
||||
task ("build", ["update_submodules", $ENVIRONMENT_DIR], function()
|
||||
{
|
||||
rm_rf($ENVIRONMENT_NARWHAL_PRODUCT);
|
||||
|
||||
git_export('narwhal', $ENVIRONMENT_NARWHAL_PRODUCT);
|
||||
|
||||
$PACKAGES.forEach(function(aPackage)
|
||||
{
|
||||
git_export(aPackage, FILE.join($ENVIRONMENT_PACKAGES_PRODUCT, aPackage));
|
||||
});
|
||||
|
||||
symlink_executable(FILE.join($ENVIRONMENT_PACKAGES_PRODUCT, 'jack', 'bin', 'jackup'))
|
||||
});
|
||||
|
||||
//CLOBBER.include($ENVIRONMENT_NARWHAL_PRODUCT)
|
||||
|
||||
task ("pull", function()
|
||||
{
|
||||
$EXTERNALS.forEach(function(external)
|
||||
{
|
||||
OS.system("cd " + external + " && git pull origin master");
|
||||
});
|
||||
});
|
||||
@@ -1,54 +0,0 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require '../common'
|
||||
require 'objective-j'
|
||||
require 'rake'
|
||||
require 'rake/clean'
|
||||
|
||||
require 'fileutils'
|
||||
|
||||
$ENVIRONMENT_NARWHAL_PRODUCT = $ENVIRONMENT_DIR
|
||||
$ENVIRONMENT_PACKAGES_PRODUCT = File.join($ENVIRONMENT_NARWHAL_PRODUCT, 'packages')
|
||||
|
||||
$EXTERNALS = ['browserjs', 'jack', 'narwhal', 'ojunit']
|
||||
$PACKAGES = ['browserjs', 'jack']
|
||||
|
||||
def git_export(source, dest)
|
||||
dest = File.join(File.expand_path(dest), '');
|
||||
system %{cd "#{source}" && git checkout-index -a -f "--prefix=#{dest}"}
|
||||
end
|
||||
|
||||
file_d $ENVIRONMENT_DIR do
|
||||
mkdir_p $ENVIRONMENT_DIR
|
||||
end
|
||||
|
||||
task :update_submodules do
|
||||
if !ENV['NOSUBUP'] then
|
||||
if executable_exists? "git"
|
||||
system %{cd .. && git submodule init && git submodule update}
|
||||
else
|
||||
puts "Git not installed"
|
||||
rake abort
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
task :build => [:update_submodules, $ENVIRONMENT_DIR] do
|
||||
rm_rf($ENVIRONMENT_NARWHAL_PRODUCT)
|
||||
|
||||
git_export('narwhal', $ENVIRONMENT_NARWHAL_PRODUCT)
|
||||
|
||||
$PACKAGES.each do |package|
|
||||
git_export(package, File.join($ENVIRONMENT_PACKAGES_PRODUCT, package))
|
||||
end
|
||||
|
||||
symlink_executable(File.join($ENVIRONMENT_PACKAGES_PRODUCT, 'jack', 'bin', 'jackup'))
|
||||
end
|
||||
|
||||
CLOBBER.include($ENVIRONMENT_NARWHAL_PRODUCT)
|
||||
|
||||
task :pull do
|
||||
$EXTERNALS.each do |external|
|
||||
system "cd #{external} && git pull origin master"
|
||||
end
|
||||
end
|
||||
@@ -181,6 +181,45 @@
|
||||
return [[self _representedObject] count];
|
||||
}
|
||||
|
||||
- (int)indexOfObject:(CPObject)anObject inRange:(CPRange)aRange
|
||||
{
|
||||
var index = aRange.location,
|
||||
count = aRange.length,
|
||||
shouldIsEqual = !!anObject.isa;
|
||||
|
||||
for (; index < count; ++index)
|
||||
{
|
||||
var object = [self objectAtIndex:index];
|
||||
|
||||
if (anObject === object || shouldIsEqual && !!object.isa && [anObject isEqual:object])
|
||||
return index;
|
||||
}
|
||||
|
||||
return CPNotFound;
|
||||
}
|
||||
|
||||
- (int)indexOfObject:(CPObject)anObject
|
||||
{
|
||||
return [self indexOfObject:anObject range:CPMakeRange(0, [self count])];
|
||||
}
|
||||
|
||||
- (int)indexOfObjectIdenticalTo:(CPObject)anObject inRange:(CPRange)aRange
|
||||
{
|
||||
var index = aRange.location,
|
||||
count = aRange.length;
|
||||
|
||||
for (; index < count; ++index)
|
||||
if (anObject === [self objectAtIndex:index])
|
||||
return index;
|
||||
|
||||
return CPNotFound;
|
||||
}
|
||||
|
||||
- (int)indexOfObjectIdenticalTo:(CPObject)anObject
|
||||
{
|
||||
return [self indexOfObjectIdenticalTo:anObject inRange:CPMakeRange(0, [self count])];
|
||||
}
|
||||
|
||||
- (id)objectAtIndex:(unsigned)anIndex
|
||||
{
|
||||
if(_objectAtIndex)
|
||||
@@ -200,6 +239,15 @@
|
||||
[self _setRepresentedObject:target];
|
||||
}
|
||||
|
||||
- (void)addObjectsFromArray:(CPArray)anArray
|
||||
{
|
||||
var index = 0,
|
||||
count = [anArray count];
|
||||
|
||||
for (; index < count; ++index)
|
||||
[self addObject:[anArray objectAtIndex:index]];
|
||||
}
|
||||
|
||||
- (void)insertObject:(id)anObject atIndex:(unsigned)anIndex
|
||||
{
|
||||
if (_insert)
|
||||
|
||||
@@ -365,7 +365,7 @@
|
||||
count = length;
|
||||
|
||||
for(; index < count; ++index)
|
||||
if(self[index] == anObject)
|
||||
if(self[index] === anObject)
|
||||
return index;
|
||||
}
|
||||
|
||||
@@ -521,8 +521,8 @@
|
||||
*/
|
||||
- (id)objectAtIndex:(int)anIndex
|
||||
{
|
||||
if (anIndex >= length)
|
||||
[CPException raise:CPRangeException reason:@"index (" + anIndex + @") beyond bounds (" + length + @")"];
|
||||
if (anIndex >= length || anIndex < 0)
|
||||
[CPException raise:CPRangeException reason:@"index (" + anIndex + @") out of bounds (0 - " + length + @")"];
|
||||
|
||||
return self[anIndex];
|
||||
}
|
||||
@@ -534,14 +534,11 @@
|
||||
*/
|
||||
- (CPArray)objectsAtIndexes:(CPIndexSet)indexes
|
||||
{
|
||||
var index = [indexes firstIndex],
|
||||
var index = CPNotFound,
|
||||
objects = [];
|
||||
|
||||
while(index != CPNotFound)
|
||||
{
|
||||
[objects addObject:self[index]];
|
||||
index = [indexes indexGreaterThanIndex:index];
|
||||
}
|
||||
while((index = [indexes indexGreaterThanIndex:index]) !== CPNotFound)
|
||||
[objects addObject:[self objectAtIndex:index]];
|
||||
|
||||
return objects;
|
||||
}
|
||||
@@ -816,15 +813,18 @@
|
||||
|
||||
for(; index < count; ++index)
|
||||
{
|
||||
var object = self[index];
|
||||
if (index === 0)
|
||||
description += '\n';
|
||||
|
||||
if (object && object.isa)
|
||||
description += [object description];
|
||||
else
|
||||
description += object;
|
||||
var object = self[index],
|
||||
objectDescription = object && object.isa ? [object description] : object + "";
|
||||
|
||||
description += "\t" + objectDescription.split('\n').join("\n\t");
|
||||
|
||||
if (index !== count - 1)
|
||||
description += ", ";
|
||||
|
||||
description += '\n';
|
||||
}
|
||||
|
||||
return description + ')';
|
||||
@@ -1112,7 +1112,7 @@
|
||||
*/
|
||||
- (void)removeObjectIdenticalTo:(id)anObject
|
||||
{
|
||||
[self removeObjectIdenticalTo:anObject inRange:CPMakeRange(0, length)];
|
||||
[self removeObjectIdenticalTo:anObject inRange:CPMakeRange(0, [self count])];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -1124,12 +1124,13 @@
|
||||
*/
|
||||
- (void)removeObjectIdenticalTo:(id)anObject inRange:(CPRange)aRange
|
||||
{
|
||||
var index;
|
||||
var index,
|
||||
count = [self count];
|
||||
|
||||
while ((index = [self indexOfObjectIdenticalTo:anObject inRange:aRange]) != CPNotFound)
|
||||
while ((index = [self indexOfObjectIdenticalTo:anObject inRange:aRange]) !== CPNotFound)
|
||||
{
|
||||
[self removeObjectAtIndex:index];
|
||||
aRange = CPIntersectionRange(CPMakeRange(index, length - index), aRange);
|
||||
aRange = CPIntersectionRange(CPMakeRange(index, (--count) - index), aRange);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,10 @@
|
||||
|
||||
@implementation CPBundle : CPObject
|
||||
{
|
||||
//properties set up in file.js
|
||||
//this.path = NULL;
|
||||
//this.info = NULL;
|
||||
//this._URIMap = { };
|
||||
}
|
||||
|
||||
+ (id)alloc
|
||||
@@ -185,13 +189,25 @@
|
||||
|
||||
- (void)connection:(CPURLConnection)aConnection didFailWithError:(CPError)anError
|
||||
{
|
||||
alert("Couldnot find bundle:" + anError)
|
||||
if ([_delegate respondsToSelector:@selector(bundle:didFailWithError:)])
|
||||
[_delegate bundle:self didFailWithError:anError];
|
||||
|
||||
CPLog.error("Could not find bundle: " + self);
|
||||
}
|
||||
|
||||
- (void)connectionDidFinishLoading:(CPURLConnection)aConnection
|
||||
{
|
||||
}
|
||||
|
||||
- (CPString)description
|
||||
{
|
||||
return [super description] + "(" + path + ")";
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
objj_bundle.prototype.isa = CPBundle;
|
||||
objj_bundle.prototype.toString = function()
|
||||
{
|
||||
return [this description];
|
||||
}
|
||||
|
||||
@@ -96,9 +96,28 @@ var CPDateReferenceDate = new Date(Date.UTC(2001,1,1,0,0,0,0));
|
||||
return self;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a CPDate initialized with a date and time specified by the given
|
||||
string in international date format YYYY-MM-DD HH:MM:SS ±HHMM (e.g.
|
||||
2009-11-17 17:52:04 +0000).
|
||||
*/
|
||||
- (id)initWithString:(CPString)description
|
||||
{
|
||||
self = new Date(description); // FIXME: not same format as NSString
|
||||
var format = /(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2}) ([-+])(\d{2})(\d{2})/,
|
||||
d = description.match(new RegExp(format));
|
||||
|
||||
if (!d || d.length != 10)
|
||||
[CPException raise:CPInvalidArgumentException
|
||||
reason:"initWithString: the string must be of YYYY-MM-DD HH:MM:SS ±HHMM format"];
|
||||
|
||||
var date = new Date(d[1], d[2]-1, d[3]),
|
||||
timeZoneOffset = (Number(d[8]) * 60 + Number(d[9])) * (d[7] === '-' ? -1 : 1);
|
||||
|
||||
date.setHours(d[4]);
|
||||
date.setMinutes(d[5]);
|
||||
date.setSeconds(d[6]);
|
||||
|
||||
self = new Date(date.getTime() + (timeZoneOffset - date.getTimezoneOffset()) * 60 * 1000);
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -152,9 +171,37 @@ var CPDateReferenceDate = new Date(Date.UTC(2001,1,1,0,0,0,0));
|
||||
return (self > anotherDate) ? self : anotherDate;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the date as a string in the international format
|
||||
YYYY-MM-DD HH:MM:SS ±HHMM.
|
||||
*/
|
||||
- (CPString)description
|
||||
{
|
||||
return self.toString(); // FIXME: not same format as NSDate
|
||||
var hours = Math.floor(self.getTimezoneOffset() / 60),
|
||||
minutes = self.getTimezoneOffset() - hours * 60;
|
||||
|
||||
return [CPString stringWithFormat:@"%04d-%02d-%02d %02d:%02d:%02d +%02d%02d", self.getFullYear(), self.getMonth()+1, self.getDate(), self.getHours(), self.getMinutes(), self.getSeconds(), hours, minutes];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPDateTimeKey = @"CPDateTimeKey";
|
||||
|
||||
@implementation CPDate (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
if (self)
|
||||
{
|
||||
self.setTime([aCoder decodeIntForKey:CPDateTimeKey]);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeInt:self.getTime() forKey:CPDateTimeKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -148,11 +148,32 @@
|
||||
|
||||
for (var key in object)
|
||||
{
|
||||
if (!object.hasOwnProperty(key))
|
||||
continue;
|
||||
|
||||
var value = object[key];
|
||||
|
||||
if (recursively && value.constructor === Object)
|
||||
value = [CPDictionary dictionaryWithJSObject:value recursively:YES];
|
||||
|
||||
if (recursively)
|
||||
{
|
||||
if (value.constructor === Object)
|
||||
value = [CPDictionary dictionaryWithJSObject:value recursively:YES];
|
||||
else if ([value isKindOfClass:CPArray])
|
||||
{
|
||||
var newValue = [];
|
||||
for (var i = 0, count = value.length; i < count; i++)
|
||||
{
|
||||
var thisValue = value[i];
|
||||
|
||||
if (thisValue.constructor === Object)
|
||||
newValue.push([CPDictionary dictionaryWithJSObject:thisValue recursively:YES]);
|
||||
else
|
||||
newValue.push(thisValue);
|
||||
}
|
||||
|
||||
value = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
[dictionary setObject:value forKey:key];
|
||||
}
|
||||
|
||||
@@ -609,3 +630,7 @@
|
||||
@end
|
||||
|
||||
objj_dictionary.prototype.isa = CPDictionary;
|
||||
objj_dictionary.prototype.toString = function()
|
||||
{
|
||||
return [this description];
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ if (input == nil)
|
||||
if (self)
|
||||
{
|
||||
name = aName;
|
||||
reason = aReason;
|
||||
message = aReason;
|
||||
userInfo = aUserInfo;
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ if (input == nil)
|
||||
*/
|
||||
- (CPString)reason
|
||||
{
|
||||
return reason;
|
||||
return message;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -128,7 +128,7 @@ if (input == nil)
|
||||
*/
|
||||
- (CPString)description
|
||||
{
|
||||
return reason;
|
||||
return message;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -145,7 +145,7 @@ if (input == nil)
|
||||
|
||||
- (id)copy
|
||||
{
|
||||
return [[self class] exceptionWithName:name reason:reason userInfo:userInfo];
|
||||
return [[self class] exceptionWithName:name reason:message userInfo:userInfo];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -168,7 +168,7 @@ var CPExceptionNameKey = "CPExceptionNameKey",
|
||||
if (self)
|
||||
{
|
||||
name = [aCoder decodeObjectForKey:CPExceptionNameKey];
|
||||
reason = [aCoder decodeObjectForKey:CPExceptionReasonKey];
|
||||
message = [aCoder decodeObjectForKey:CPExceptionReasonKey];
|
||||
userInfo = [aCoder decodeObjectForKey:CPExceptionUserInfoKey];
|
||||
}
|
||||
|
||||
@@ -182,13 +182,16 @@ var CPExceptionNameKey = "CPExceptionNameKey",
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:name forKey:CPExceptionNameKey];
|
||||
[aCoder encodeObject:reason forKey:CPExceptionReasonKey];
|
||||
[aCoder encodeObject:message forKey:CPExceptionReasonKey];
|
||||
[aCoder encodeObject:userInfo forKey:CPExceptionUserInfoKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
objj_exception.prototype.isa = CPException;
|
||||
// toll-free bridge Error to CPException
|
||||
// [CPException alloc] uses an objj_exception, which is a subclass of Error
|
||||
Error.prototype.isa = CPException;
|
||||
|
||||
[CPException initialize];
|
||||
|
||||
function _CPRaiseInvalidAbstractInvocation(anObject, aSelector)
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* CPFunctionOperation.j
|
||||
*
|
||||
* Created by Johannes Fahrenkrug.
|
||||
* Copyright 2009, Springenwerk.
|
||||
*
|
||||
* 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 "CPOperation.j"
|
||||
|
||||
|
||||
/*!
|
||||
@class CPFunctionOperation
|
||||
@brief Represents an operation using a JavaScript function that can be run in an CPOperationQueue
|
||||
*/
|
||||
@implementation CPFunctionOperation : CPOperation
|
||||
{
|
||||
CPArray _functions;
|
||||
}
|
||||
|
||||
|
||||
- (void)main
|
||||
{
|
||||
if (_functions && [_functions count] > 0)
|
||||
{
|
||||
var i = 0;
|
||||
for (i = 0; i < [_functions count]; i++)
|
||||
{
|
||||
var func = [_functions objectAtIndex:i];
|
||||
func();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_functions = [];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
/*!
|
||||
Adds the specified JS function to the receiver’s list of functions to perform.
|
||||
*/
|
||||
- (void)addExecutionFunction:(JSObject)jsFunction
|
||||
{
|
||||
[_functions addObject:jsFunction];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns an array containing the functions associated with the receiver.
|
||||
*/
|
||||
- (CPArray)executionFunctions
|
||||
{
|
||||
return _functions;
|
||||
}
|
||||
|
||||
/*!
|
||||
Creates and returns an NSFunctionOperation object and adds the specified function to it.
|
||||
*/
|
||||
+ (id)functionOperationWithFunction:(JSObject)jsFunction
|
||||
{
|
||||
functionOp = [[CPFunctionOperation alloc] init];
|
||||
[functionOp addExecutionFunction:jsFunction];
|
||||
|
||||
return functionOp;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* CPInvocationOperation.j
|
||||
*
|
||||
* Created by Johannes Fahrenkrug.
|
||||
* Copyright 2009, Springenwerk.
|
||||
*
|
||||
* 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/CPInvocation.j>
|
||||
@import "CPOperation.j"
|
||||
|
||||
|
||||
/*!
|
||||
@class CPInvocationOperation
|
||||
@brief Represents an operation using an invocation that can be run in an CPOperationQueue
|
||||
*/
|
||||
@implementation CPInvocationOperation : CPOperation
|
||||
{
|
||||
CPInvocation _invocation;
|
||||
}
|
||||
|
||||
|
||||
- (void)main
|
||||
{
|
||||
if (_invocation)
|
||||
{
|
||||
[_invocation invoke];
|
||||
}
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_invocation = nil;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a CPInvocationOperation object initialized with the specified invocation object.
|
||||
@param inv the invocation
|
||||
*/
|
||||
- (id)initWithInvocation:(CPInvocation)inv
|
||||
{
|
||||
if (self = [self init])
|
||||
{
|
||||
_invocation = inv;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns an NSInvocationOperation object initialized with the specified target and selector.
|
||||
@param target the target
|
||||
@param sel the selector that should be called on the target
|
||||
@param arg the arguments
|
||||
*/
|
||||
- (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg
|
||||
{
|
||||
var inv = [[CPInvocation alloc] initWithMethodSignature:nil];
|
||||
[inv setTarget:target];
|
||||
[inv setSelector:sel];
|
||||
[inv setArgument:arg atIndex:2];
|
||||
|
||||
return [self initWithInvocation:inv];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the receiver’s invocation object.
|
||||
*/
|
||||
- (CPInvocation)invocation
|
||||
{
|
||||
return _invocation;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the result of the invocation or method.
|
||||
*/
|
||||
- (id)result
|
||||
{
|
||||
if ([self isFinished] && _invocation)
|
||||
{
|
||||
return [_invocation returnValue];
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@end
|
||||
@@ -77,6 +77,81 @@
|
||||
return [CPSet set];
|
||||
}
|
||||
|
||||
- (void)applyChange:(CPDictionary)aChange toKeyPath:(CPString)aKeyPath
|
||||
{
|
||||
var changeKind = [aChange objectForKey:CPKeyValueChangeKindKey];
|
||||
|
||||
if (changeKind === CPKeyValueChangeSetting)
|
||||
{
|
||||
var value = [aChange objectForKey:CPKeyValueChangeNewKey];
|
||||
|
||||
[self setValue:value === [CPNull null] ? nil : value forKeyPath:aKeyPath];
|
||||
}
|
||||
|
||||
else if (changeKind === CPKeyValueChangeInsertion)
|
||||
[[self mutableArrayValueForKeyPath:aKeyPath]
|
||||
insertObjects:[aChange objectForKey:CPKeyValueChangeNewKey]
|
||||
atIndexes:[aChange objectForKey:CPKeyValueChangeIndexesKey]];
|
||||
|
||||
else if (changeKind === CPKeyValueChangeRemoval)
|
||||
[[self mutableArrayValueForKeyPath:aKeyPath]
|
||||
removeObjectsAtIndexes:[aChange objectForKey:CPKeyValueChangeIndexesKey]];
|
||||
|
||||
else if (changeKind === CPKeyValueChangeReplacement)
|
||||
[[self mutableArrayValueForKeyPath:aKeyPath]
|
||||
replaceObjectAtIndexes:[aChange objectForKey:CPKeyValueChangeIndexesKey]
|
||||
withObjects:[aChange objectForKey:CPKeyValueChangeNewKey]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPDictionary (KeyValueObserving)
|
||||
|
||||
- (CPDictionary)inverseChangeDictionary
|
||||
{
|
||||
var inverseChangeDictionary = [self mutableCopy],
|
||||
changeKind = [self objectForKey:CPKeyValueChangeKindKey];
|
||||
|
||||
if (changeKind === CPKeyValueChangeSetting || changeKind === CPKeyValueChangeReplacement)
|
||||
{
|
||||
[inverseChangeDictionary
|
||||
setObject:[self objectForKey:CPKeyValueChangeOldKey]
|
||||
forKey:CPKeyValueChangeNewKey];
|
||||
|
||||
[inverseChangeDictionary
|
||||
setObject:[self objectForKey:CPKeyValueChangeNewKey]
|
||||
forKey:CPKeyValueChangeOldKey];
|
||||
}
|
||||
|
||||
else if (changeKind === CPKeyValueChangeInsertion)
|
||||
{
|
||||
[inverseChangeDictionary
|
||||
setObject:CPKeyValueChangeRemoval
|
||||
forKey:CPKeyValueChangeKindKey];
|
||||
|
||||
[inverseChangeDictionary
|
||||
setObject:[self objectForKey:CPKeyValueChangeNewKey]
|
||||
forKey:CPKeyValueChangeOldKey];
|
||||
|
||||
[inverseChangeDictionary removeObjectForKey:CPKeyValueChangeNewKey];
|
||||
}
|
||||
|
||||
else if (changeKind === CPKeyValueChangeRemoval)
|
||||
{
|
||||
[inverseChangeDictionary
|
||||
setObject:CPKeyValueChangeInsertion
|
||||
forKey:CPKeyValueChangeKindKey];
|
||||
|
||||
[inverseChangeDictionary
|
||||
setObject:[self objectForKey:CPKeyValueChangeOldKey]
|
||||
forKey:CPKeyValueChangeNewKey];
|
||||
|
||||
[inverseChangeDictionary removeObjectForKey:CPKeyValueChangeOldKey];
|
||||
}
|
||||
|
||||
return inverseChangeDictionary;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
// KVO Options
|
||||
@@ -311,11 +386,11 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew|CPKeyValueObservingOptionOld,
|
||||
var type = [changes objectForKey:CPKeyValueChangeKindKey];
|
||||
|
||||
// for to-many relationships, oldvalue is only sensible for replace and remove
|
||||
if (type == CPKeyValueChangeReplacement || type == CPKeyValueChangeRemoval)
|
||||
if (type === CPKeyValueChangeReplacement || type === CPKeyValueChangeRemoval)
|
||||
{
|
||||
//FIXME: do we need to go through and replace "" with CPNull?
|
||||
var oldValues = [[_targetObject mutableArrayValueForKeyPath:aKey] objectsAtIndexes:indexes];
|
||||
[changes setValue:oldValues forKey:CPKeyValueChangeOldKey];
|
||||
var newValues = [[_targetObject mutableArrayValueForKeyPath:aKey] objectsAtIndexes:indexes];
|
||||
[changes setValue:newValues forKey:CPKeyValueChangeOldKey];
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -373,19 +448,28 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew|CPKeyValueObservingOptionOld,
|
||||
else if (!isBefore)
|
||||
[observerInfo.observer observeValueForKeyPath:aKey ofObject:_targetObject change:changes context:observerInfo.context];
|
||||
}
|
||||
|
||||
|
||||
var dependentKeysMap = _nativeClass[DependentKeysKey];
|
||||
|
||||
if (!dependentKeysMap)
|
||||
return;
|
||||
|
||||
var keysComposedOfKey = [dependentKeysMap[aKey] allObjects];
|
||||
|
||||
if (!keysComposedOfKey)
|
||||
var dependentKeyPaths = [dependentKeysMap[aKey] allObjects];
|
||||
|
||||
if (!dependentKeyPaths)
|
||||
return;
|
||||
|
||||
for (var i=0, count=keysComposedOfKey.length; i<count; i++)
|
||||
[self _sendNotificationsForKey:keysComposedOfKey[i] changeOptions:changeOptions isBefore:isBefore];
|
||||
var index = 0,
|
||||
count = [dependentKeyPaths count];
|
||||
|
||||
for (; index < count; ++index)
|
||||
{
|
||||
var keyPath = dependentKeyPaths[index];
|
||||
|
||||
[self _sendNotificationsForKey:keyPath
|
||||
changeOptions:isBefore ? [changeOptions copy] : _changesForKey[keyPath]
|
||||
isBefore:isBefore];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -501,7 +585,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew|CPKeyValueObservingOptionOld,
|
||||
|
||||
- (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)changes context:(id)aContext
|
||||
{
|
||||
if (anObject == _object)
|
||||
if (aKeyPath === _firstPart)
|
||||
{
|
||||
[_observer observeValueForKeyPath:_firstPart ofObject:_object change:changes context:_context];
|
||||
|
||||
@@ -514,7 +598,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew|CPKeyValueObservingOptionOld,
|
||||
if (_value)
|
||||
[_value addObserver:self forKeyPath:_secondPart options:kvoNewAndOld context:nil];
|
||||
}
|
||||
else /* if (anObject == _value || !_value) */
|
||||
else
|
||||
{
|
||||
//a is the same, but a.b has changed -- nothing to do but forward this message along
|
||||
[_observer observeValueForKeyPath:_firstPart+"."+aKeyPath ofObject:_object change:changes context:_context];
|
||||
|
||||
@@ -227,6 +227,7 @@ CPLog(@"Got some class: %@", inst);
|
||||
*/
|
||||
- (BOOL)respondsToSelector:(SEL)aSelector
|
||||
{
|
||||
// isa is isa.isa in class case.
|
||||
return !!class_getInstanceMethod(isa, aSelector);
|
||||
}
|
||||
|
||||
@@ -269,7 +270,7 @@ CPLog(@"Got some class: %@", inst);
|
||||
*/
|
||||
- (CPString)description
|
||||
{
|
||||
return "<" + isa.name + " 0x" + [CPString stringWithHash:[self UID]] + ">";
|
||||
return "<" + class_getName(isa) + " 0x" + [CPString stringWithHash:[self UID]] + ">";
|
||||
}
|
||||
|
||||
// Sending Messages
|
||||
@@ -322,6 +323,7 @@ CPLog(@"Got some class: %@", inst);
|
||||
Used for forwarding of messages to other objects.
|
||||
@ignore
|
||||
*/
|
||||
// FIXME: This should be moved to the runtime?
|
||||
- (void)forward:(SEL)aSelector :(marg_list)args
|
||||
{
|
||||
var signature = [self methodSignatureForSelector:aSelector];
|
||||
@@ -468,12 +470,12 @@ CPLog(@"Got some class: %@", inst);
|
||||
return [self UID];
|
||||
}
|
||||
|
||||
- (unsigned)UID
|
||||
- (CPString)UID
|
||||
{
|
||||
if (typeof self.__address === "undefined")
|
||||
self.__address = _objj_generateObjectHash();
|
||||
|
||||
return __address;
|
||||
return __address + "";
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
@@ -0,0 +1,293 @@
|
||||
/*
|
||||
* CPOperation.j
|
||||
*
|
||||
* Created by Johannes Fahrenkrug.
|
||||
* Copyright 2009, Springenwerk.
|
||||
*
|
||||
* 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>
|
||||
|
||||
/*!
|
||||
Operations receive very low priority for execution.
|
||||
@global
|
||||
@group CPOperationQueuePriority
|
||||
*/
|
||||
CPOperationQueuePriorityVeryLow = -8;
|
||||
|
||||
/*!
|
||||
Operations receive low priority for execution.
|
||||
@global
|
||||
@group CPOperationQueuePriority
|
||||
*/
|
||||
CPOperationQueuePriorityLow = -4;
|
||||
|
||||
/*!
|
||||
Operations receive normal priority for execution.
|
||||
@global
|
||||
@group CPOperationQueuePriority
|
||||
*/
|
||||
CPOperationQueuePriorityNormal = 0;
|
||||
|
||||
/*!
|
||||
Operations receive high priority for execution.
|
||||
@global
|
||||
@group CPOperationQueuePriority
|
||||
*/
|
||||
CPOperationQueuePriorityHigh = 4;
|
||||
|
||||
/*!
|
||||
Operations receive very high priority for execution.
|
||||
@global
|
||||
@group CPOperationQueuePriority
|
||||
*/
|
||||
CPOperationQueuePriorityVeryHigh = 8;
|
||||
|
||||
|
||||
/*!
|
||||
@class CPOperation
|
||||
@brief Represents an operation that can be run in an CPOperationQueue
|
||||
|
||||
It should be subclassed an the subclass should implement its own main method to do the actual work.
|
||||
*/
|
||||
@implementation CPOperation : CPObject
|
||||
{
|
||||
CPArray operations;
|
||||
BOOL _cancelled;
|
||||
BOOL _executing;
|
||||
BOOL _finished;
|
||||
BOOL _ready;
|
||||
int _queuePriority;
|
||||
JSObject _completionFunction;
|
||||
CPArray _dependencies;
|
||||
}
|
||||
|
||||
|
||||
- (void)main
|
||||
{
|
||||
// should be overridden in child class
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_cancelled = NO;
|
||||
_executing = NO;
|
||||
_finished = NO;
|
||||
_ready = YES;
|
||||
_dependencies = [[CPArray alloc] init];
|
||||
_queuePriority = CPOperationQueuePriorityNormal;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
/*!
|
||||
Starts the operation (runs the main method), sets all the status flags and runs the completion function if it's set
|
||||
*/
|
||||
- (void)start
|
||||
{
|
||||
if (!_cancelled)
|
||||
{
|
||||
[self willChangeValueForKey:@"isExecuting"];
|
||||
_executing = YES;
|
||||
[self didChangeValueForKey:@"isExecuting"];
|
||||
[self main];
|
||||
if (_completionFunction)
|
||||
{
|
||||
_completionFunction();
|
||||
}
|
||||
[self willChangeValueForKey:@"isExecuting"];
|
||||
_executing = NO;
|
||||
[self didChangeValueForKey:@"isExecuting"];
|
||||
[self willChangeValueForKey:@"isFinished"];
|
||||
_finished = YES;
|
||||
[self didChangeValueForKey:@"isFinished"];
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Indicates if this operation has been cancelled
|
||||
@return if this operation has been cancelled
|
||||
*/
|
||||
- (BOOL)isCancelled
|
||||
{
|
||||
return _cancelled;
|
||||
}
|
||||
|
||||
/*!
|
||||
Indicates if this operation is currently executing
|
||||
@return if this operation is currently executing
|
||||
*/
|
||||
- (BOOL)isExecuting
|
||||
{
|
||||
return _executing;
|
||||
}
|
||||
|
||||
/*!
|
||||
Indicates if this operation has finished running
|
||||
@return if this operation has finished running
|
||||
*/
|
||||
- (BOOL)isFinished
|
||||
{
|
||||
return _finished;
|
||||
}
|
||||
|
||||
/*!
|
||||
Just added for Cocoa compatibility
|
||||
@return always false
|
||||
*/
|
||||
- (BOOL)isConcurrent
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
/*!
|
||||
Indicates if this operation is ready to be executed. Takes the "isFinished" state of dependent operations into account
|
||||
@return if this operation is ready to run
|
||||
*/
|
||||
- (BOOL)isReady
|
||||
{
|
||||
return _ready;
|
||||
}
|
||||
|
||||
/*!
|
||||
The JS function that should be run after the main method
|
||||
@return JS function
|
||||
*/
|
||||
– (JSObject)completionFunction
|
||||
{
|
||||
return _completionFunction;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the JS function that should be run after the main method
|
||||
*/
|
||||
- (void)setCompletionFunction:(JSObject)aJavaScriptFunction
|
||||
{
|
||||
_completionFunction = aJavaScriptFunction;
|
||||
}
|
||||
|
||||
/*!
|
||||
Makes the receiver dependent on the completion of the specified operation.
|
||||
@param anOperation the operation that the receiver should depend on
|
||||
*/
|
||||
- (void)addDependency:(CPOperation)anOperation
|
||||
{
|
||||
[self willChangeValueForKey:@"dependencies"];
|
||||
[anOperation addObserver:self
|
||||
forKeyPath:@"isFinished"
|
||||
options:(CPKeyValueObservingOptionNew)
|
||||
context:NULL];
|
||||
[_dependencies addObject:anOperation];
|
||||
[self didChangeValueForKey:@"dependencies"];
|
||||
[self _updateIsReadyState];
|
||||
}
|
||||
|
||||
/*!
|
||||
Removes the receiver’s dependence on the specified operation.
|
||||
@param anOperation the operation that the receiver should no longer depend on
|
||||
*/
|
||||
- (void)removeDependency:(CPOperation)anOperation
|
||||
{
|
||||
[self willChangeValueForKey:@"dependencies"];
|
||||
[_dependencies removeObject:anOperation];
|
||||
[anOperation removeObserver:self
|
||||
forKeyPath:@"isFinished"];
|
||||
[self didChangeValueForKey:@"dependencies"];
|
||||
[self _updateIsReadyState];
|
||||
}
|
||||
|
||||
/*!
|
||||
The operations that the receiver depends on
|
||||
@return array of operations
|
||||
*/
|
||||
- (CPArray)dependencies
|
||||
{
|
||||
return _dependencies;
|
||||
}
|
||||
|
||||
/*!
|
||||
Just added for Cocoa compatibility, doesn't do anything
|
||||
*/
|
||||
- (void)waitUntilFinished
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Advises the operation object that it should stop executing its task.
|
||||
*/
|
||||
- (void)cancel
|
||||
{
|
||||
[self willChangeValueForKey:@"isCancelled"];
|
||||
_cancelled = YES;
|
||||
[self didChangeValueForKey:@"isCancelled"];
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the priority of the operation when used in an operation queue.
|
||||
@param priority the priority
|
||||
*/
|
||||
- (void)setQueuePriority:(int)priority
|
||||
{
|
||||
_queuePriority = priority;
|
||||
}
|
||||
|
||||
/*!
|
||||
The priority of the operation when used in an operation queue.
|
||||
@return the priority
|
||||
*/
|
||||
- (int)queuePriority
|
||||
{
|
||||
return _queuePriority;
|
||||
}
|
||||
|
||||
// We need to observe the "isFinished" key of our dependent operations so we can update our own "isReady" state
|
||||
- (void)observeValueForKeyPath:(CPString)keyPath
|
||||
ofObject:(id)object
|
||||
change:(CPDictionary)change
|
||||
context:(void)context
|
||||
{
|
||||
if (keyPath == @"isFinished")
|
||||
{
|
||||
[self _updateIsReadyState];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_updateIsReadyState
|
||||
{
|
||||
var newReady = YES;
|
||||
if (_dependencies && [_dependencies count] > 0)
|
||||
{
|
||||
var i = 0;
|
||||
for (i = 0; i < [_dependencies count]; i++)
|
||||
{
|
||||
if (![[_dependencies objectAtIndex:i] isFinished])
|
||||
{
|
||||
newReady = NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (newReady != _ready)
|
||||
{
|
||||
[self willChangeValueForKey:@"isReady"];
|
||||
_ready = newReady;
|
||||
[self didChangeValueForKey:@"isReady"];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,293 @@
|
||||
/*
|
||||
* CPOperationQueue.j
|
||||
*
|
||||
* Created by Johannes Fahrenkrug.
|
||||
* Copyright 2009, Springenwerk.
|
||||
*
|
||||
* 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 "CPOperation.j"
|
||||
@import "CPInvocationOperation.j"
|
||||
@import "CPFunctionOperation.j"
|
||||
|
||||
// the global queue (mainQueue)
|
||||
var cpOperationMainQueue = nil;
|
||||
|
||||
/*!
|
||||
@class CPOperationQueue
|
||||
@brief Represents an operation queue that can run CPOperations
|
||||
*/
|
||||
@implementation CPOperationQueue : CPObject
|
||||
{
|
||||
CPArray _operations;
|
||||
BOOL _suspended;
|
||||
CPString _name @accessors(property=name);
|
||||
CPTimer _timer;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_operations = [[CPArray alloc] init];
|
||||
_suspended = NO;
|
||||
_currentlyModifyingOps = NO;
|
||||
_timer = [CPTimer scheduledTimerWithTimeInterval:0.01
|
||||
target:self
|
||||
selector:@selector(_runNextOpsInQueue)
|
||||
userInfo:nil
|
||||
repeats:YES];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)_runNextOpsInQueue
|
||||
{
|
||||
if (!_suspended && [self operationCount] > 0)
|
||||
{
|
||||
var i = 0;
|
||||
for (i = 0; i < [_operations count]; i++)
|
||||
{
|
||||
var op = [_operations objectAtIndex:i];
|
||||
if ([op isReady] && ![op isCancelled] && ![op isFinished] && ![op isExecuting])
|
||||
{
|
||||
[op start];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_enableTimer:(BOOL)enable
|
||||
{
|
||||
if (!enable)
|
||||
{
|
||||
if (_timer)
|
||||
{
|
||||
[_timer invalidate];
|
||||
_timer = nil;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!_timer)
|
||||
{
|
||||
_timer = [CPTimer scheduledTimerWithTimeInterval:0.01
|
||||
target:self
|
||||
selector:@selector(_runNextOpsInQueue)
|
||||
userInfo:nil
|
||||
repeats:YES];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Adds the specified operation object to the receiver.
|
||||
@param anOperation the operation that should be scheduled for execution
|
||||
*/
|
||||
- (void)addOperation:(CPOperation)anOperation
|
||||
{
|
||||
[self willChangeValueForKey:@"operations"];
|
||||
[self willChangeValueForKey:@"operationCount"];
|
||||
[_operations addObject:anOperation];
|
||||
[self _sortOpsByPriority:_operations];
|
||||
[self didChangeValueForKey:@"operations"];
|
||||
[self didChangeValueForKey:@"operationCount"];
|
||||
}
|
||||
|
||||
/*!
|
||||
Adds the specified array of operations to the queue.
|
||||
@param ops The array of CPOperation objects that you want to add to the receiver.
|
||||
@param wait If YES, the method only returns once all of the specified operations finish executing. If NO, the operations are added to the queue and control returns immediately to the caller.
|
||||
*/
|
||||
- (void)addOperations:(CPArray)ops waitUntilFinished:(BOOL)wait
|
||||
{
|
||||
if (ops)
|
||||
{
|
||||
if (wait)
|
||||
{
|
||||
[self _sortOpsByPriority:ops];
|
||||
[self _runOpsSynchronously:ops];
|
||||
}
|
||||
|
||||
[_operations addObjectsFromArray:ops];
|
||||
[self _sortOpsByPriority:_operations];
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Wraps the given js function in a CPOperation and adds it to the queue
|
||||
@param aFunction the JS function to add
|
||||
*/
|
||||
- (void)addOperationWithFunction:(JSObject)aFunction
|
||||
{
|
||||
[self addOperation:[CPFunctionOperation functionOperationWithFunction:aFunction]];
|
||||
}
|
||||
|
||||
- (CPArray)operations
|
||||
{
|
||||
return _operations;
|
||||
}
|
||||
|
||||
- (int)operationCount
|
||||
{
|
||||
if (_operations)
|
||||
{
|
||||
return [_operations count];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
Cancels all queued and executing operations.
|
||||
*/
|
||||
- (void)cancelAllOperations
|
||||
{
|
||||
if (_operations)
|
||||
{
|
||||
var i = 0;
|
||||
for (i = 0; i < [_operations count]; i++)
|
||||
{
|
||||
[[_operations objectAtIndex:i] cancel];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Blocks until all of the receiver’s queued and executing operations finish executing.
|
||||
*/
|
||||
- (void)waitUntilAllOperationsAreFinished
|
||||
{
|
||||
// lets first stop the timer so it won't interfere
|
||||
[self _enableTimer:NO];
|
||||
[self _runOpsSynchronously:_operations];
|
||||
if (!_suspended)
|
||||
{
|
||||
[self _enableTimer:YES];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
Returns the maximum number of concurrent operations that the receiver can execute.
|
||||
Always returns 1 because JS doesn't have threads
|
||||
*/
|
||||
- (int)maxConcurrentOperationCount
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*!
|
||||
Modifies the execution of pending operations
|
||||
@param suspend if YES, queue execution is suspended. If NO, it is resumed
|
||||
*/
|
||||
- (void)setSuspended:(BOOL)suspend
|
||||
{
|
||||
_suspended = suspend;
|
||||
[self _enableTimer:!suspend];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a Boolean value indicating whether the receiver is scheduling queued operations for execution.
|
||||
*/
|
||||
- (BOOL)isSuspended
|
||||
{
|
||||
return _suspended;
|
||||
}
|
||||
|
||||
- (void)_sortOpsByPriority:(CPArray)someOps
|
||||
{
|
||||
if (someOps)
|
||||
{
|
||||
[someOps sortUsingFunction:function(lhs, rhs)
|
||||
{
|
||||
if ([lhs queuePriority] < [rhs queuePriority])
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ([lhs queuePriority] > [rhs queuePriority])
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
context:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_runOpsSynchronously:(CPArray)ops
|
||||
{
|
||||
if (ops)
|
||||
{
|
||||
var keepGoing = YES;
|
||||
while (keepGoing)
|
||||
{
|
||||
var i = 0;
|
||||
keepGoing = NO;
|
||||
|
||||
// start the ones that are ready
|
||||
for (i = 0; i < [ops count]; i++)
|
||||
{
|
||||
var op = [ops objectAtIndex:i];
|
||||
if ([op isReady] && ![op isCancelled] && ![op isFinished] && ![op isExecuting])
|
||||
{
|
||||
[op start];
|
||||
}
|
||||
}
|
||||
|
||||
// make sure they are all done
|
||||
for (i = 0; i < [ops count]; i++)
|
||||
{
|
||||
var op = [ops objectAtIndex:i];
|
||||
if (![op isFinished] && ![op isCancelled])
|
||||
{
|
||||
keepGoing = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Convenience method for one system wide singelton queue. Returns the same queue as currentQueue.
|
||||
*/
|
||||
+ (CPOperationQueue)mainQueue
|
||||
{
|
||||
if (!cpOperationMainQueue)
|
||||
{
|
||||
cpOperationMainQueue = [[CPOperationQueue alloc] init];
|
||||
[cpOperationMainQueue setName:@"main"];
|
||||
}
|
||||
|
||||
return cpOperationMainQueue;
|
||||
}
|
||||
|
||||
/*!
|
||||
Convenience method for one system wide singelton queue. Returns the same queue as mainQueue.
|
||||
*/
|
||||
+ (CPOperationQueue)currentQueue
|
||||
{
|
||||
return [CPOperationQueue mainQueue];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* CPProxy.j
|
||||
* Foundation
|
||||
*
|
||||
* Created by Francisco Tolmasky.
|
||||
* Copyright 2009, 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 "CPException.j"
|
||||
@import "CPInvocation.j"
|
||||
@import "CPString.j"
|
||||
|
||||
|
||||
@implementation CPProxy
|
||||
{
|
||||
}
|
||||
|
||||
+ (void)load
|
||||
{
|
||||
}
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
}
|
||||
|
||||
+ (Class)class
|
||||
{
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (id)alloc
|
||||
{
|
||||
return class_createInstance(self);
|
||||
}
|
||||
|
||||
+ (BOOL)respondsToSelector:(SEL)selector
|
||||
{
|
||||
return !!class_getInstanceMethod(isa, aSelector);
|
||||
}
|
||||
|
||||
- (CPMethodSignature)methodSignatureForSelector:(SEL)aSelector
|
||||
{
|
||||
[CPException raise:CPInvalidArgumentException
|
||||
reason:@"-methodSignatureForSelector: called on abstract CPProxy class."];
|
||||
}
|
||||
|
||||
- (void)forwardInvocation:(CPInvocation)anInvocation
|
||||
{
|
||||
[CPException raise:CPInvalidArgumentException
|
||||
reason:@"-methodSignatureForSelector: called on abstract CPProxy class."];
|
||||
}
|
||||
|
||||
// FIXME: This should be moved to the runtime?
|
||||
- (void)forward:(SEL)aSelector :(marg_list)args
|
||||
{
|
||||
[CPObject methodForSelector:_cmd](self, _cmd, aSelector, args);
|
||||
}
|
||||
|
||||
- (unsigned)hash
|
||||
{
|
||||
return [self UID];
|
||||
}
|
||||
|
||||
- (unsigned)UID
|
||||
{
|
||||
if (typeof self.__address === "undefined")
|
||||
self.__address = _objj_generateObjectHash();
|
||||
|
||||
return __address;
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)anObject
|
||||
{
|
||||
return self === object;
|
||||
}
|
||||
|
||||
- (id)self
|
||||
{
|
||||
return self;
|
||||
}
|
||||
|
||||
- (Class)class
|
||||
{
|
||||
return isa;
|
||||
}
|
||||
|
||||
- (Class)superclass
|
||||
{
|
||||
return class_getSuperclass(isa);
|
||||
}
|
||||
|
||||
- (id)performSelector:(SEL)aSelector
|
||||
{
|
||||
return objj_msgSend(self, aSelector);
|
||||
}
|
||||
|
||||
- (id)performSelector:(SEL)aSelector withObject:(id)anObject
|
||||
{
|
||||
return objj_msgSend(self, aSelector, anObject);
|
||||
}
|
||||
|
||||
- (id)performSelector:(SEL)aSelector withObject:(id)anObject withObject:(id)anotherObject
|
||||
{
|
||||
return objj_msgSend(self, aSelector, anObject, anotherObject);
|
||||
}
|
||||
|
||||
- (BOOL)isProxy
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)isKindOfClass:(Class)aClass
|
||||
{
|
||||
var signature = [self methodSignatureForSelector:_cmd],
|
||||
invocation = [CPInvocation invocationWithMethodSignature:signature];
|
||||
|
||||
[self forwardInvocation:invocation];
|
||||
|
||||
return [invocation returnValue];
|
||||
}
|
||||
|
||||
-(BOOL)isMemberOfClass:(Class)aClass
|
||||
{
|
||||
var signature = [self methodSignatureForSelector:_cmd],
|
||||
invocation = [CPInvocation invocationWithMethodSignature:signature];
|
||||
|
||||
[self forwardInvocation:invocation];
|
||||
|
||||
return [invocation returnValue];
|
||||
}
|
||||
|
||||
- (BOOL)respondsToSelector:(SEL)aSelector
|
||||
{
|
||||
var signature = [self methodSignatureForSelector:_cmd],
|
||||
invocation = [CPInvocation invocationWithMethodSignature:signature];
|
||||
|
||||
[self forwardInvocation:invocation];
|
||||
|
||||
return [invocation returnValue];
|
||||
}
|
||||
|
||||
- (CPString)description
|
||||
{
|
||||
return "<" + class_getName(isa) + " 0x" + [CPString stringWithHash:[self UID]] + ">";
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -232,7 +232,9 @@
|
||||
*/
|
||||
- (BOOL)containsObject:(id)anObject
|
||||
{
|
||||
if (_contents[[anObject UID]] && [_contents[[anObject UID]] isEqual:anObject])
|
||||
var obj = _contents[[anObject UID]];
|
||||
|
||||
if (obj !== undefined && [obj isEqual:anObject])
|
||||
return YES;
|
||||
|
||||
return NO;
|
||||
|
||||
@@ -537,6 +537,44 @@ var CPStringRegexSpecialCharacters = [
|
||||
return hash;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a string containing characters the receiver and a given string have in common, starting from
|
||||
the beginning of each up to the first characters that aren't equivalent.
|
||||
@param aString the string with which to compare the receiver
|
||||
*/
|
||||
- (CPString)commonPrefixWithString:(CPString)aString
|
||||
{
|
||||
return [self commonPrefixWithString: aString options: 0];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a string containing characters the receiver and a given string have in common, starting from
|
||||
the beginning of each up to the first characters that aren't equivalent.
|
||||
@param aString the string with which to compare the receiver
|
||||
@param aMask options for comparision
|
||||
*/
|
||||
- (CPString)commonPrefixWithString:(CPString)aString options:(int)aMask
|
||||
{
|
||||
var len = 0, // length of common prefix
|
||||
lhs = self,
|
||||
rhs = aString,
|
||||
min = MIN([lhs length], [rhs length]);
|
||||
|
||||
if (aMask & CPCaseInsensitiveSearch)
|
||||
{
|
||||
lhs = [lhs lowercaseString];
|
||||
rhs = [rhs lowercaseString];
|
||||
}
|
||||
|
||||
for (; len < min; len++ )
|
||||
{
|
||||
if ( [lhs characterAtIndex:len] !== [rhs characterAtIndex:len] )
|
||||
break;
|
||||
}
|
||||
|
||||
return [self substringToIndex:len];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a copy of the receiver with all the first letters of words capitalized.
|
||||
*/
|
||||
|
||||
@@ -159,7 +159,7 @@ var CPURLConnectionDelegate = nil;
|
||||
|
||||
// Browsers use "file:", Titanium uses "app:"
|
||||
_isLocalFileConnection = scheme === "file" ||
|
||||
((scheme !== "http" || scheme !== "https:") &&
|
||||
((scheme === "http" || scheme === "https:") &&
|
||||
window.location &&
|
||||
(window.location.protocol === "file:" || window.location.protocol === "app:"));
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
@import "CPObject.j"
|
||||
@import "CPInvocation.j"
|
||||
@import "CPProxy.j"
|
||||
|
||||
|
||||
var CPUndoManagerNormal = 0,
|
||||
@@ -170,15 +171,17 @@ var _CPUndoGroupingParentKey = @"_CPUndoGroupingParentKey",
|
||||
{
|
||||
CPMutableArray _redoStack;
|
||||
CPMutableArray _undoStack;
|
||||
|
||||
|
||||
BOOL _groupsByEvent;
|
||||
int _disableCount;
|
||||
int _levelsOfUndo;
|
||||
id _currentGrouping;
|
||||
int _state;
|
||||
CPString _actionName;
|
||||
|
||||
id _preparedTarget;
|
||||
|
||||
id _undoManagerProxy;
|
||||
|
||||
CPArray _runLoopModes;
|
||||
BOOL _registeredWithRunLoop;
|
||||
}
|
||||
@@ -200,12 +203,29 @@ var _CPUndoGroupingParentKey = @"_CPUndoGroupingParentKey",
|
||||
|
||||
[self setRunLoopModes:[CPDefaultRunLoopMode]];
|
||||
[self setGroupsByEvent:YES];
|
||||
_performRegistered = NO;
|
||||
|
||||
_undoManagerProxy = [_CPUndoManagerProxy alloc];
|
||||
_undoManagerProxy._undoManager = self;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)_addUndoInvocation:(CPInvocation)anInvocation
|
||||
{
|
||||
if (!_currentGrouping)
|
||||
// Remember that we create these lazily...
|
||||
if ([self groupsByEvent])
|
||||
[self _beginUndoGroupingForEvent];
|
||||
else
|
||||
[CPException raise:CPInternalInconsistencyException reason:"No undo group is currently open"];
|
||||
|
||||
[_currentGrouping addInvocation:anInvocation];
|
||||
|
||||
if (_state === CPUndoManagerNormal)
|
||||
[_redoStack removeAllObjects];
|
||||
}
|
||||
|
||||
// Registering Undo Operations
|
||||
/*!
|
||||
Registers an undo operation. You invoke this method with the target of the undo action providing the selector which can perform the undo with the provided object. The object is often a dictionary of the identifying the attribute and their values before the change. The invocation will be added to the current grouping. If the registrations have been disabled through \c -disableUndoRegistration, this method does nothing.
|
||||
@@ -216,9 +236,7 @@ var _CPUndoGroupingParentKey = @"_CPUndoGroupingParentKey",
|
||||
*/
|
||||
- (void)registerUndoWithTarget:(id)aTarget selector:(SEL)aSelector object:(id)anObject
|
||||
{
|
||||
if (!_currentGrouping)
|
||||
[CPException raise:CPInternalInconsistencyException reason:"No undo group is currently open"];
|
||||
|
||||
// Don't do anything if we're disabled.
|
||||
if (_disableCount > 0)
|
||||
return;
|
||||
|
||||
@@ -230,10 +248,7 @@ var _CPUndoGroupingParentKey = @"_CPUndoGroupingParentKey",
|
||||
[invocation setSelector:aSelector];
|
||||
[invocation setArgument:anObject atIndex:2];
|
||||
|
||||
[_currentGrouping addInvocation:invocation];
|
||||
|
||||
if (_state == CPUndoManagerNormal)
|
||||
[_redoStack removeAllObjects];
|
||||
[self _addUndoInvocation:invocation];
|
||||
}
|
||||
/*!
|
||||
Prepares the specified target for the undo action.
|
||||
@@ -244,14 +259,14 @@ var _CPUndoGroupingParentKey = @"_CPUndoGroupingParentKey",
|
||||
{
|
||||
_preparedTarget = aTarget;
|
||||
|
||||
return self;
|
||||
return _undoManagerProxy;
|
||||
}
|
||||
|
||||
/*
|
||||
FIXME This method doesn't seem to do anything right
|
||||
@ignore
|
||||
*/
|
||||
-(CPMethodSignature)methodSignatureForSelector:(SEL)aSelector
|
||||
- (CPMethodSignature)_methodSignatureOfPreparedTargetForSelector:(SEL)aSelector
|
||||
{
|
||||
if ([_preparedTarget respondsToSelector:aSelector])
|
||||
return 1;
|
||||
@@ -264,23 +279,20 @@ var _CPUndoGroupingParentKey = @"_CPUndoGroupingParentKey",
|
||||
target on the invocation, and adds it to the current grouping.
|
||||
@param anInvocation the message to record
|
||||
*/
|
||||
- (void)forwardInvocation:(CPInvocation)anInvocation
|
||||
- (void)_forwardInvocationToPreparedTarget:(CPInvocation)anInvocation
|
||||
{
|
||||
// Don't do anything if we're disabled.
|
||||
if (_disableCount > 0)
|
||||
return;
|
||||
|
||||
/* if (_preparedTarget == nil)
|
||||
[NSException raise:NSInternalInconsistencyException
|
||||
format:@"forwardInvocation called without first preparing a target"];
|
||||
if (_currentGroup == nil)
|
||||
|
||||
/*
|
||||
if (_currentGroup == nil)
|
||||
[NSException raise:NSInternalInconsistencyException
|
||||
format:@"forwardInvocation called without first opening an undo group"];
|
||||
*/
|
||||
[anInvocation setTarget:_preparedTarget];
|
||||
[_currentGrouping addInvocation:anInvocation];
|
||||
|
||||
if (_state == CPUndoManagerNormal)
|
||||
[_redoStack removeAllObjects];
|
||||
[self _addUndoInvocation:anInvocation];
|
||||
|
||||
_preparedTarget = nil;
|
||||
}
|
||||
@@ -291,7 +303,11 @@ var _CPUndoGroupingParentKey = @"_CPUndoGroupingParentKey",
|
||||
*/
|
||||
- (BOOL)canRedo
|
||||
{
|
||||
return _redoStack.length > 0;
|
||||
[[CPNotificationCenter defaultCenter]
|
||||
postNotificationName:CPUndoManagerCheckpointNotification
|
||||
object:self];
|
||||
|
||||
return [_redoStack count] > 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -311,9 +327,9 @@ var _CPUndoGroupingParentKey = @"_CPUndoGroupingParentKey",
|
||||
*/
|
||||
- (void)undo
|
||||
{
|
||||
if ([self groupingLevel] == 1)
|
||||
if ([self groupingLevel] === 1)
|
||||
[self endUndoGrouping];
|
||||
|
||||
|
||||
[self undoNestedGroup];
|
||||
}
|
||||
|
||||
@@ -322,28 +338,31 @@ var _CPUndoGroupingParentKey = @"_CPUndoGroupingParentKey",
|
||||
*/
|
||||
- (void)undoNestedGroup
|
||||
{
|
||||
if (_undoStack.length == 0)
|
||||
if ([_undoStack count] <= 0)
|
||||
return;
|
||||
|
||||
|
||||
var defaultCenter = [CPNotificationCenter defaultCenter];
|
||||
/* [[NSNotificationCenter defaultCenter] postNotificationName:NSUndoManagerCheckpointNotification
|
||||
object:self];
|
||||
*/
|
||||
[defaultCenter postNotificationName:CPUndoManagerWillUndoChangeNotification object:self];
|
||||
|
||||
[defaultCenter postNotificationName:CPUndoManagerCheckpointNotification
|
||||
object:self];
|
||||
|
||||
[defaultCenter postNotificationName:CPUndoManagerWillUndoChangeNotification
|
||||
object:self];
|
||||
|
||||
var undoGrouping = _undoStack.pop();
|
||||
|
||||
|
||||
_state = CPUndoManagerUndoing;
|
||||
|
||||
[self beginUndoGrouping];
|
||||
[self _beginUndoGrouping];
|
||||
[undoGrouping invoke];
|
||||
[self endUndoGrouping];
|
||||
|
||||
|
||||
[_CPUndoGrouping _poolUndoGrouping:undoGrouping];
|
||||
|
||||
|
||||
_state = CPUndoManagerNormal;
|
||||
|
||||
[defaultCenter postNotificationName:CPUndoManagerDidUndoChangeNotification object:self];
|
||||
[defaultCenter postNotificationName:CPUndoManagerDidUndoChangeNotification
|
||||
object:self];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -352,19 +371,21 @@ var _CPUndoGroupingParentKey = @"_CPUndoGroupingParentKey",
|
||||
- (void)redo
|
||||
{
|
||||
// Don't do anything if we have no redos.
|
||||
if (_redoStack.length == 0)
|
||||
if ([_redoStack count] <= 0)
|
||||
return;
|
||||
|
||||
/* if (_state == NSUndoManagerUndoing)
|
||||
[NSException raise:NSInternalInconsistencyException
|
||||
format:@"redo called while undoing"];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:NSUndoManagerCheckpointNotification
|
||||
object:self];
|
||||
*/
|
||||
|
||||
var defaultCenter = [CPNotificationCenter defaultCenter];
|
||||
|
||||
[defaultCenter postNotificationName:CPUndoManagerWillRedoChangeNotification object:self];
|
||||
[defaultCenter postNotificationName:CPUndoManagerCheckpointNotification
|
||||
object:self];
|
||||
|
||||
[defaultCenter postNotificationName:CPUndoManagerWillRedoChangeNotification
|
||||
object:self];
|
||||
|
||||
var oldUndoGrouping = _currentGrouping,
|
||||
undoGrouping = _redoStack.pop();
|
||||
@@ -372,7 +393,7 @@ var _CPUndoGroupingParentKey = @"_CPUndoGroupingParentKey",
|
||||
_currentGrouping = nil;
|
||||
_state = CPUndoManagerRedoing;
|
||||
|
||||
[self beginUndoGrouping];
|
||||
[self _beginUndoGrouping];
|
||||
[undoGrouping invoke];
|
||||
[self endUndoGrouping];
|
||||
|
||||
@@ -389,6 +410,29 @@ var _CPUndoGroupingParentKey = @"_CPUndoGroupingParentKey",
|
||||
Starts a new grouping of undo tasks, and makes it the current grouping.
|
||||
*/
|
||||
- (void)beginUndoGrouping
|
||||
{
|
||||
// It doesn't matter that the user is creating a group themselves, we are
|
||||
// pretending to have opened the group at the beginning of the run loop,
|
||||
// so create an implicit one here.
|
||||
if (!_currentGrouping && [self groupsByEvent])
|
||||
[self _beginUndoGroupingForEvent];
|
||||
|
||||
[[CPNotificationCenter defaultCenter]
|
||||
postNotificationName:CPUndoManagerCheckpointNotification
|
||||
object:self];
|
||||
|
||||
[self _beginUndoGrouping];
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (void)_beginUndoGroupingForEvent
|
||||
{
|
||||
[self _beginUndoGrouping];
|
||||
[self _registerWithRunLoop];
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (void)_beginUndoGrouping
|
||||
{
|
||||
_currentGrouping = [_CPUndoGrouping undoGroupingWithParent:_currentGrouping];
|
||||
}
|
||||
@@ -402,17 +446,22 @@ var _CPUndoGroupingParentKey = @"_CPUndoGroupingParentKey",
|
||||
if (!_currentGrouping)
|
||||
[CPException raise:CPInternalInconsistencyException reason:"endUndoGrouping. No undo group is currently open."];
|
||||
|
||||
var defaultCenter = [CPNotificationCenter defaultCenter];
|
||||
|
||||
[defaultCenter postNotificationName:CPUndoManagerCheckpointNotification
|
||||
object:self];
|
||||
|
||||
var parent = [_currentGrouping parent];
|
||||
|
||||
if (!parent && [_currentGrouping invocations].length > 0)
|
||||
{
|
||||
[[CPNotificationCenter defaultCenter]
|
||||
[defaultCenter
|
||||
postNotificationName:CPUndoManagerWillCloseUndoGroupNotification
|
||||
object:self];
|
||||
|
||||
// Put this group on the redo stack if we are currently undoing, otherwise
|
||||
// Put this group on the redo stack if we are currently undoing, otherwise
|
||||
// put it on the undo stack. That way, "undos" become "redos".
|
||||
var stack = _state == CPUndoManagerUndoing ? _redoStack : _undoStack;
|
||||
var stack = _state === CPUndoManagerUndoing ? _redoStack : _undoStack;
|
||||
|
||||
stack.push(_currentGrouping);
|
||||
|
||||
@@ -460,21 +509,14 @@ var _CPUndoGroupingParentKey = @"_CPUndoGroupingParentKey",
|
||||
*/
|
||||
- (void)setGroupsByEvent:(BOOL)aFlag
|
||||
{
|
||||
if (_groupsByEvent == aFlag)
|
||||
aFlag = !!aFlag;
|
||||
|
||||
if (_groupsByEvent === aFlag)
|
||||
return;
|
||||
|
||||
_groupsByEvent = aFlag;
|
||||
|
||||
if (_groupsByEvent)
|
||||
{
|
||||
[self _registerWithRunLoop];
|
||||
|
||||
// There is a chance that the event loop selector won't fire before our first register,
|
||||
// so kick it off here.
|
||||
if (!_currentGrouping)
|
||||
[self beginUndoGrouping];
|
||||
}
|
||||
else
|
||||
|
||||
if (![self groupsByEvent])
|
||||
[self _unregisterWithRunLoop];
|
||||
}
|
||||
|
||||
@@ -515,7 +557,7 @@ var _CPUndoGroupingParentKey = @"_CPUndoGroupingParentKey",
|
||||
*/
|
||||
- (BOOL)isUndoing
|
||||
{
|
||||
return _state == CPUndoManagerUndoing;
|
||||
return _state === CPUndoManagerUndoing;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -523,7 +565,7 @@ var _CPUndoGroupingParentKey = @"_CPUndoGroupingParentKey",
|
||||
*/
|
||||
- (BOOL)isRedoing
|
||||
{
|
||||
return _state == CPUndoManagerRedoing;
|
||||
return _state === CPUndoManagerRedoing;
|
||||
}
|
||||
|
||||
// Clearing Undo Operations
|
||||
@@ -623,27 +665,19 @@ var _CPUndoGroupingParentKey = @"_CPUndoGroupingParentKey",
|
||||
*/
|
||||
- (void)setRunLoopModes:(CPArray)modes
|
||||
{
|
||||
_runLoopModes = modes;
|
||||
|
||||
[self _unregisterWithRunLoop];
|
||||
|
||||
if (_groupsByEvent)
|
||||
_runLoopModes = [modes copy];
|
||||
|
||||
if (_registeredWithRunLoop)
|
||||
{
|
||||
[self _unregisterWithRunLoop];
|
||||
[self _registerWithRunLoop];
|
||||
}
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (void)beginUndoGroupingForEvent
|
||||
- (void)_runLoopEndUndoGrouping
|
||||
{
|
||||
if (!_groupsByEvent)
|
||||
return;
|
||||
|
||||
if (_currentGrouping != nil)
|
||||
[self endUndoGrouping];
|
||||
|
||||
[self beginUndoGrouping];
|
||||
|
||||
[[CPRunLoop currentRunLoop] performSelector:@selector(beginUndoGroupingForEvent)
|
||||
target:self argument:nil order:CPUndoCloseGroupingRunLoopOrdering modes:_runLoopModes];
|
||||
[self endUndoGrouping];
|
||||
_registeredWithRunLoop = NO;
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
@@ -653,8 +687,12 @@ var _CPUndoGroupingParentKey = @"_CPUndoGroupingParentKey",
|
||||
return;
|
||||
|
||||
_registeredWithRunLoop = YES;
|
||||
[[CPRunLoop currentRunLoop] performSelector:@selector(beginUndoGroupingForEvent)
|
||||
target:self argument:nil order:CPUndoCloseGroupingRunLoopOrdering modes:_runLoopModes];
|
||||
[[CPRunLoop currentRunLoop]
|
||||
performSelector:@selector(_runLoopEndUndoGrouping)
|
||||
target:self
|
||||
argument:nil
|
||||
order:CPUndoCloseGroupingRunLoopOrdering
|
||||
modes:_runLoopModes];
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
@@ -664,7 +702,33 @@ var _CPUndoGroupingParentKey = @"_CPUndoGroupingParentKey",
|
||||
return;
|
||||
|
||||
_registeredWithRunLoop = NO;
|
||||
[[CPRunLoop currentRunLoop] cancelPerformSelector:@selector(beginUndoGroupingForEvent) target:self argument:nil];
|
||||
[[CPRunLoop currentRunLoop]
|
||||
cancelPerformSelector:@selector(_runLoopEndUndoGrouping)
|
||||
target:self
|
||||
argument:nil];
|
||||
}
|
||||
|
||||
- (void)observeChangesForKeyPath:(CPString)aKeyPath ofObject:(id)anObject
|
||||
{
|
||||
[anObject addObserver:self
|
||||
forKeyPath:aKeyPath
|
||||
options:CPKeyValueObservingOptionOld | CPKeyValueObservingOptionNew
|
||||
context:NULL];
|
||||
}
|
||||
|
||||
- (void)stopObservingChangesForKeyPath:(CPString)aKeyPath ofObject:(id)anObject
|
||||
{
|
||||
[anObject removeObserver:self forKeyPath:aKeyPath];
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(CPString)aKeyPath
|
||||
ofObject:(id)anObject
|
||||
change:(CPDictionary)aChange
|
||||
context:(id)aContext
|
||||
{
|
||||
[[self prepareWithInvocationTarget:anObject]
|
||||
applyChange:[aChange inverseChangeDictionary]
|
||||
toKeyPath:aKeyPath];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -718,3 +782,20 @@ var CPUndoManagerRedoStackKey = @"CPUndoManagerRedoStackKey",
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPUndoManagerProxy : CPProxy
|
||||
{
|
||||
CPUndoManager _undoManager;
|
||||
}
|
||||
|
||||
- (CPMethodSignature)methodSignatureForSelector:(SEL)aSelector
|
||||
{
|
||||
return [_undoManager _methodSignatureOfPreparedTargetForSelector:aSelector];
|
||||
}
|
||||
|
||||
- (void)forwardInvocation:(CPInvocation)anInvocation
|
||||
{
|
||||
[_undoManager _forwardInvocationToPreparedTarget:anInvocation];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -42,6 +42,8 @@
|
||||
@import "CPNumber.j"
|
||||
@import "CPObject.j"
|
||||
@import "CPObjJRuntime.j"
|
||||
@import "CPOperation.j"
|
||||
@import "CPOperationQueue.j"
|
||||
@import "CPPropertyListSerialization.j"
|
||||
@import "CPRange.j"
|
||||
@import "CPRunLoop.j"
|
||||
|
||||
@@ -48,7 +48,7 @@ task ("install", ["CommonJS"], function()
|
||||
{
|
||||
// FIXME: require("narwhal/tusk/install").install({}, $COMMONJS);
|
||||
// Doesn't work due to some weird this.print business.
|
||||
OS.system("sudo tusk install --force " + $BUILD_CJS_OBJECTIVE_J + " " + $BUILD_CJS_CAPPUCCINO);
|
||||
OS.system(["sudo", "tusk", "install", "--force", $BUILD_CJS_OBJECTIVE_J, $BUILD_CJS_CAPPUCCINO]);
|
||||
});
|
||||
|
||||
// Documentation
|
||||
@@ -61,7 +61,7 @@ task ("documentation", function()
|
||||
{
|
||||
if (executableExists("doxygen"))
|
||||
{
|
||||
if (OS.system("doxygen " + FILE.join("Tools", "Documentation", "Cappuccino.doxygen")))
|
||||
if (OS.system(["doxygen", FILE.join("Tools", "Documentation", "Cappuccino.doxygen")]))
|
||||
OS.exit(1); //rake abort if ($? != 0)
|
||||
|
||||
rm_rf($DOCUMENTATION_BUILD);
|
||||
@@ -95,7 +95,7 @@ filedir ($STARTER_DOWNLOAD_APPLICATION, ["CommonJS"], function()
|
||||
rm_rf($STARTER_DOWNLOAD_APPLICATION);
|
||||
FILE.mkdirs($STARTER_DOWNLOAD);
|
||||
|
||||
if (OS.system("capp gen " + $STARTER_DOWNLOAD_APPLICATION + " -t Application --noconfig"))
|
||||
if (OS.system(["capp", "gen", $STARTER_DOWNLOAD_APPLICATION, "-t", "Application", "--noconfig"]))
|
||||
// FIXME: uncomment this: we get conversion errors
|
||||
//OS.exit(1); // rake abort if ($? != 0)
|
||||
{}
|
||||
@@ -161,13 +161,14 @@ task ("deploy", ["downloads"], function()
|
||||
|
||||
// Testing
|
||||
|
||||
task ("test", ["build"], function()
|
||||
task("test", ["build", "test-only"]);
|
||||
|
||||
task("test-only", function()
|
||||
{
|
||||
var tests = new FileList('Tests/**/*Test.j');
|
||||
var cmd = ["ojtest"].concat(tests.items());
|
||||
var cmdString = cmd.map(OS.enquote).join(" ");
|
||||
|
||||
var code = OS.system(cmdString);
|
||||
var code = OS.system(cmd);
|
||||
if (code !== 0)
|
||||
OS.exit(code);
|
||||
});
|
||||
@@ -198,8 +199,7 @@ function pushPackage(path, remote)
|
||||
["git", "push", "origin", "master"]
|
||||
];
|
||||
|
||||
var cmdString = cmds.map(function(cmd)
|
||||
{
|
||||
var cmdString = cmds.map(function(cmd) {
|
||||
return cmd.map(OS.enquote).join(" ");
|
||||
}).join(" && ");
|
||||
|
||||
|
||||
@@ -8,12 +8,12 @@ function printUsage()
|
||||
print("this is where you say the usage");
|
||||
}
|
||||
|
||||
function main()
|
||||
exports.main = function(args)
|
||||
{
|
||||
// FIXME: ARGS
|
||||
system.args.shift();
|
||||
// TODO: args parser
|
||||
args.shift();
|
||||
|
||||
if (system.args.length < 1)
|
||||
if (args.length < 1)
|
||||
return printUsage();
|
||||
|
||||
var allFiles = NO,
|
||||
@@ -23,11 +23,11 @@ function main()
|
||||
outExtension = nil;
|
||||
|
||||
index = 0,
|
||||
count = system.args.length;
|
||||
count = args.length;
|
||||
|
||||
for (; index < count; ++index)
|
||||
{
|
||||
var argument = system.args[index];
|
||||
var argument = args[index];
|
||||
|
||||
if (argument.charAt(0) === '-' && !allFiles)
|
||||
{
|
||||
@@ -35,13 +35,13 @@ function main()
|
||||
return printUsage();
|
||||
|
||||
else if (argument === "-convert")
|
||||
format = system.args[++index];
|
||||
format = args[++index];
|
||||
|
||||
else if (argument === "-o")
|
||||
outPath = system.args[++index];
|
||||
outPath = args[++index];
|
||||
|
||||
else if (argument === "-e")
|
||||
outExtension = system.args[++index];
|
||||
outExtension = args[++index];
|
||||
|
||||
else if (argument === "--")
|
||||
allFiles = YES;
|
||||
@@ -50,7 +50,7 @@ function main()
|
||||
return printUsage();
|
||||
}
|
||||
else
|
||||
filePaths.push(system.args[index]);
|
||||
filePaths.push(args[index]);
|
||||
}
|
||||
|
||||
index = 0;
|
||||
@@ -76,4 +76,5 @@ function main()
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
if (require.main == module.id)
|
||||
exports.main(system.args);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
var FILE = require("file"),
|
||||
readline = require("readline").readline;
|
||||
var FILE = require("file");
|
||||
|
||||
var window = require("browser/window");
|
||||
var window = exports.window = require("browser/window");
|
||||
|
||||
if (system.engine === "rhino")
|
||||
{
|
||||
@@ -63,12 +62,14 @@ if (system.env["OBJJ_INCLUDE_PATHS"])
|
||||
// TODO: somehow make window object the top scope?
|
||||
with (window)
|
||||
{
|
||||
var objectivejSource = FILE.read(objectivejPath, { charset:"UTF-8" });
|
||||
|
||||
// read and eval Objective-J.js with the module's scope
|
||||
if (system.engine === "rhino")
|
||||
Packages.org.mozilla.javascript.Context.getCurrentContext().evaluateString(window, FILE.read(objectivejPath, { charset:"UTF-8" }), "Objective-J.js", 0, null);
|
||||
Packages.org.mozilla.javascript.Context.getCurrentContext().evaluateString(window, objectivejSource, objectivejPath, 0, null);
|
||||
else
|
||||
eval(FILE.read(objectivejPath, { charset:"UTF-8" }));
|
||||
|
||||
eval(objectivejSource);
|
||||
|
||||
// export desired variables. must eval variable name to obtain a reference.
|
||||
[
|
||||
"objj_preprocess",
|
||||
@@ -105,37 +106,26 @@ with (window)
|
||||
exports.IS_FILE = function(aFragment) { return (aFragment.type & FRAGMENT_FILE); }
|
||||
exports.IS_LOCAL = function(aFragment) { return (aFragment.type & FRAGMENT_LOCAL); }
|
||||
exports.IS_IMPORT = function(aFragment) { return (aFragment.type & FRAGMENT_IMPORT); }
|
||||
|
||||
/*
|
||||
objj_set_evaluator(function(code) {
|
||||
return function(OBJJ_CURRENT_BUNDLE) {
|
||||
with (window) {
|
||||
return eval("function(OBJJ_CURRENT_BUNDLE){"+code+"}");
|
||||
}
|
||||
}
|
||||
});
|
||||
*/
|
||||
|
||||
// runs the objj repl or file provided in args
|
||||
exports.run = function(args)
|
||||
{
|
||||
args = args || [];
|
||||
window.args = args;
|
||||
|
||||
// FIXME: ARGS
|
||||
args.shift();
|
||||
|
||||
if (args.length > 0)
|
||||
if (args && args.length > 1)
|
||||
{
|
||||
while (args.length && args[0].indexOf('-I') === 0)
|
||||
OBJJ_INCLUDE_PATHS = args.shift().substr(2).split(':').concat(OBJJ_INCLUDE_PATHS);
|
||||
|
||||
var mainFilePath = FILE.canonical(args.shift());
|
||||
|
||||
var arg0 = args.slice(0,1),
|
||||
argv = args.slice(1);
|
||||
|
||||
while (argv.length && argv[0].indexOf('-I') === 0)
|
||||
OBJJ_INCLUDE_PATHS = argv.shift().substr(2).split(':').concat(OBJJ_INCLUDE_PATHS);
|
||||
|
||||
var mainFilePath = FILE.canonical(argv.shift());
|
||||
|
||||
objj_import(mainFilePath, YES, function() {
|
||||
if (typeof main === "function")
|
||||
main.apply(main, args);
|
||||
main(arg0.concat(argv));
|
||||
});
|
||||
|
||||
require("browser/timeout").serviceTimeouts();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -144,9 +134,8 @@ exports.run = function(args)
|
||||
try {
|
||||
system.stdout.write("objj> ").flush();
|
||||
|
||||
var input = readline(),
|
||||
result = objj_eval(input);
|
||||
|
||||
var result = objj_eval(require("readline").readline());
|
||||
|
||||
if (result !== undefined)
|
||||
print(result);
|
||||
|
||||
@@ -157,8 +146,6 @@ exports.run = function(args)
|
||||
require("browser/timeout").serviceTimeouts();
|
||||
}
|
||||
}
|
||||
|
||||
require("browser/timeout").serviceTimeouts();
|
||||
}
|
||||
|
||||
// synchronously evals Objective-J code
|
||||
|
||||
@@ -27,7 +27,7 @@ var compressor = null;
|
||||
function sharedCompressor()
|
||||
{
|
||||
if (!compressor)
|
||||
compressor = OS.popen("java -server -Dfile.encoding=UTF-8 -classpath " + RHINO_PATH + ":" + SHRINKSAFE_PATH + " org.dojotoolkit.shrinksafe.Main");
|
||||
compressor = OS.popen("java -server -Dfile.encoding=UTF-8 -classpath " + RHINO_PATH + ":" + SHRINKSAFE_PATH + " org.dojotoolkit.shrinksafe.Main", { charset:"UTF-8" });
|
||||
|
||||
return compressor;
|
||||
}
|
||||
@@ -43,6 +43,7 @@ function compress(/*String*/ aCode, /*String*/ FIXME)
|
||||
chunk = "";
|
||||
|
||||
compressor.stdin.write(tmpFile + "\n");
|
||||
compressor.stdin.flush();
|
||||
|
||||
while ((chunk = compressor.stdout.readLine()) !== "/*----*/\n")
|
||||
output += chunk;
|
||||
@@ -64,7 +65,7 @@ function compileWithResolvedFlags(aFilePath, objjcFlags, gccFlags)
|
||||
else
|
||||
{
|
||||
// GCC preprocess the file.
|
||||
var gcc = OS.popen("gcc -E -x c -P " + (gccFlags ? gccFlags.join(" ") : "") + " " + OS.enquote(aFilePath)),
|
||||
var gcc = OS.popen("gcc -E -x c -P " + (gccFlags ? gccFlags.join(" ") : "") + " " + OS.enquote(aFilePath), { charset:"UTF-8" }),
|
||||
chunk = "";
|
||||
|
||||
while (chunk = gcc.stdout.read())
|
||||
@@ -82,11 +83,11 @@ function compileWithResolvedFlags(aFilePath, objjcFlags, gccFlags)
|
||||
for (var index = 0; index < fragments.length; index++)
|
||||
{
|
||||
var fragment = fragments[index];
|
||||
|
||||
|
||||
if (IS_FILE(fragment))
|
||||
preprocessed += (IS_LOCAL(fragment) ? MARKER_IMPORT_LOCAL : MARKER_IMPORT_STD) + ';' + GET_PATH(fragment).length + ';' + GET_PATH(fragment);
|
||||
else
|
||||
{
|
||||
{
|
||||
var code = GET_CODE(fragment);
|
||||
|
||||
if (shouldCheckSyntax)
|
||||
@@ -98,23 +99,25 @@ function compileWithResolvedFlags(aFilePath, objjcFlags, gccFlags)
|
||||
catch (e)
|
||||
{
|
||||
var lines = code.split("\n"),
|
||||
PAD = 3;
|
||||
|
||||
print("Syntax error in "+GET_FILE(fragment).path+
|
||||
" on preprocessed line number "+e.lineNumber+"\n"+
|
||||
"\t"+lines.slice(Math.max(0, e.lineNumber - 1 - PAD), e.lineNumber+PAD).join("\n\t"));
|
||||
|
||||
OS.exit(1);
|
||||
PAD = 3,
|
||||
lineNumber = e.lineNumber || e.line,
|
||||
errorInfo = "Syntax error in "+GET_FILE(fragment).path+
|
||||
" on preprocessed line number "+lineNumber+"\n\n"+
|
||||
"\t"+lines.slice(Math.max(0, lineNumber - 1 - PAD), lineNumber+PAD).join("\n\t");
|
||||
|
||||
print(errorInfo);
|
||||
|
||||
throw errorInfo;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (shouldCompress)
|
||||
{
|
||||
code = compress("function(){" + code + '}', FILE.basename(aFilePath));
|
||||
|
||||
|
||||
code = code.substr("function(){".length, code.length - "function(){};\n\n".length);
|
||||
}
|
||||
|
||||
|
||||
preprocessed += MARKER_CODE + ';' + code.length + ';' + code;
|
||||
}
|
||||
}
|
||||
@@ -180,7 +183,7 @@ exports.compile = function(aFilePath, flags)
|
||||
|
||||
exports.main = function(args)
|
||||
{
|
||||
// FIXME: ARGS
|
||||
// TODO: args parser
|
||||
args.shift();
|
||||
|
||||
var resolved = resolveFlags(args),
|
||||
@@ -197,4 +200,4 @@ exports.main = function(args)
|
||||
}
|
||||
|
||||
if (require.main == module.id)
|
||||
exports.main(system.args);
|
||||
exports.main(system.args);
|
||||
|
||||
@@ -509,7 +509,11 @@ function directoryInCommon(filenames)
|
||||
{
|
||||
var directory = FILE.dirname(aFilename);
|
||||
|
||||
if (!aCommonDirectory)
|
||||
if (directory === ".")
|
||||
directory = "";
|
||||
|
||||
// Empty string is an acceptable common directory.
|
||||
if (aCommonDirectory === null)
|
||||
aCommonDirectory = directory;
|
||||
|
||||
else
|
||||
@@ -665,7 +669,8 @@ BundleTask.prototype.defineSourceTasks = function()
|
||||
else if (compilerFlags.join)
|
||||
compilerFlags = compilerFlags.join(" ");
|
||||
|
||||
var environments = this.flattenedEnvironments();
|
||||
var environments = this.flattenedEnvironments(),
|
||||
flattensSources = this.flattensSources();
|
||||
|
||||
environments.forEach(function(/*Environment*/ anEnvironment)
|
||||
{
|
||||
@@ -683,7 +688,10 @@ BundleTask.prototype.defineSourceTasks = function()
|
||||
}
|
||||
|
||||
var replacedFiles = [],
|
||||
environmentCompilerFlags = anEnvironment.compilerFlags().join(" ") + " " + compilerFlags;
|
||||
environmentCompilerFlags = anEnvironment.compilerFlags().join(" ") + " " + compilerFlags,
|
||||
flattensSources = this.flattensSources(),
|
||||
basePath = directoryInCommon(environmentSources),
|
||||
basePathLength = basePath.length;
|
||||
|
||||
environmentSources.forEach(function(/*String*/ aFilename)
|
||||
{
|
||||
@@ -691,7 +699,8 @@ BundleTask.prototype.defineSourceTasks = function()
|
||||
if (!FILE.exists(aFilename) || FILE.extension(aFilename) !== '.j')
|
||||
return;
|
||||
|
||||
var compiledEnvironmentSource = FILE.join(sourcesPath, FILE.basename(aFilename));
|
||||
var relativePath = aFilename.substring(basePathLength ? basePathLength + 1 : basePathLength),
|
||||
compiledEnvironmentSource = FILE.join(sourcesPath, relativePath);
|
||||
|
||||
filedir (compiledEnvironmentSource, [aFilename], function()
|
||||
{
|
||||
@@ -701,9 +710,7 @@ BundleTask.prototype.defineSourceTasks = function()
|
||||
|
||||
filedir (staticPath, [compiledEnvironmentSource]);
|
||||
|
||||
// FIXME: how do we non flatten?
|
||||
// dir in common
|
||||
replacedFiles.push(flattensSources ? FILE.basename(aFilename) : FILE.relative(sourcesPath, aFilename));
|
||||
replacedFiles.push(flattensSources ? FILE.basename(aFilename) : relativePath);
|
||||
}, this);
|
||||
|
||||
this._replacedFiles[anEnvironment] = replacedFiles;
|
||||
|
||||