00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 @import "CPControl.j"
00025 @import "CPStringDrawing.j"
00026 @import "CPCompatibility.j"
00027 @import "_CPImageAndTextView.j"
00028
00029 #include "CoreGraphics/CGGeometry.h"
00030 #include "Platform/Platform.h"
00031 #include "Platform/DOM/CPDOMDisplayServer.h"
00032
00033 CPTextFieldSquareBezel = 0;
00034 CPTextFieldRoundedBezel = 1;
00036 CPTextFieldDidFocusNotification = @"CPTextFieldDidFocusNotification";
00037 CPTextFieldDidBlurNotification = @"CPTextFieldDidBlurNotification";
00038
00039 #if PLATFORM(DOM)
00040
00041 var CPTextFieldDOMInputElement = nil,
00042 CPTextFieldDOMPasswordInputElement = nil,
00043 CPTextFieldDOMStandardInputElement = nil,
00044 CPTextFieldInputOwner = nil,
00045 CPTextFieldTextDidChangeValue = nil,
00046 CPTextFieldInputResigning = NO,
00047 CPTextFieldInputDidBlur = NO,
00048 CPTextFieldInputIsActive = NO,
00049 CPTextFieldCachedSelectStartFunction = nil,
00050 CPTextFieldCachedDragFunction = nil,
00051 CPTextFieldBlurFunction = nil;
00052
00053 #endif
00054
00055 var CPSecureTextFieldCharacter = "\u2022";
00056
00057 @implementation CPString (CPTextFieldAdditions)
00058
00062 - (CPString)string
00063 {
00064 return self;
00065 }
00066
00067 @end
00068
00069 CPTextFieldStateRounded = CPThemeState("rounded");
00070 CPTextFieldStatePlaceholder = CPThemeState("placeholder");
00071
00076 @implementation CPTextField : CPControl
00077 {
00078 BOOL _isEditing;
00079
00080 BOOL _isEditable;
00081 BOOL _isSelectable;
00082 BOOL _isSecure;
00083
00084 BOOL _drawsBackground;
00085
00086 CPColor _textFieldBackgroundColor;
00087
00088 id _placeholderString;
00089
00090 id _delegate;
00091
00092 CPString _textDidChangeValue;
00093
00094
00095 CPTextFieldBezelStyle _bezelStyle;
00096 BOOL _isBordered;
00097 CPControlSize _controlSize;
00098 }
00099
00100 + (CPTextField)textFieldWithStringValue:(CPString)aStringValue placeholder:(CPString)aPlaceholder width:(float)aWidth
00101 {
00102 return [self textFieldWithStringValue:aStringValue placeholder:aPlaceholder width:aWidth theme:[CPTheme defaultTheme]];
00103 }
00104
00105 + (CPTextField)textFieldWithStringValue:(CPString)aStringValue placeholder:(CPString)aPlaceholder width:(float)aWidth theme:(CPTheme)aTheme
00106 {
00107 var textField = [[self alloc] initWithFrame:CGRectMake(0.0, 0.0, aWidth, 29.0)];
00108
00109 [textField setTheme:aTheme];
00110 [textField setStringValue:aStringValue];
00111 [textField setPlaceholderString:aPlaceholder];
00112 [textField setBordered:YES];
00113 [textField setBezeled:YES];
00114 [textField setEditable:YES];
00115
00116 [textField sizeToFit];
00117
00118 return textField;
00119 }
00120
00121 + (CPTextField)roundedTextFieldWithStringValue:(CPString)aStringValue placeholder:(CPString)aPlaceholder width:(float)aWidth
00122 {
00123 return [self roundedTextFieldWithStringValue:aStringValue placeholder:aPlaceholder width:aWidth theme:[CPTheme defaultTheme]];
00124 }
00125
00126 + (CPTextField)roundedTextFieldWithStringValue:(CPString)aStringValue placeholder:(CPString)aPlaceholder width:(float)aWidth theme:(CPTheme)aTheme
00127 {
00128 var textField = [[CPTextField alloc] initWithFrame:CGRectMake(0.0, 0.0, aWidth, 29.0)];
00129
00130 [textField setTheme:aTheme];
00131 [textField setStringValue:aStringValue];
00132 [textField setPlaceholderString:aPlaceholder];
00133 [textField setBezelStyle:CPTextFieldRoundedBezel];
00134 [textField setBordered:YES];
00135 [textField setBezeled:YES];
00136 [textField setEditable:YES];
00137
00138 [textField sizeToFit];
00139
00140 return textField;
00141 }
00142
00143 + (CPTextField)labelWithTitle:(CPString)aTitle
00144 {
00145 return [self labelWithTitle:aTitle theme:[CPTheme defaultTheme]];
00146 }
00147
00148 + (CPTextField)labelWithTitle:(CPString)aTitle theme:(CPTheme)aTheme
00149 {
00150 var textField = [[self alloc] init];
00151
00152 [textField setStringValue:aTitle];
00153 [textField sizeToFit];
00154
00155 return textField;
00156 }
00157
00158 + (CPString)themeClass
00159 {
00160 return "textfield";
00161 }
00162
00163 + (id)themeAttributes
00164 {
00165 return [CPDictionary dictionaryWithObjects:[_CGInsetMakeZero(), _CGInsetMake(2.0, 2.0, 2.0, 2.0), [CPNull null]]
00166 forKeys:[@"bezel-inset", @"content-inset", @"bezel-color"]];
00167 }
00168
00169
00170 #if PLATFORM(DOM)
00171 - (DOMElement)_inputElement
00172 {
00173 if (!CPTextFieldDOMInputElement)
00174 {
00175 CPTextFieldDOMInputElement = document.createElement("input");
00176 CPTextFieldDOMInputElement.style.position = "absolute";
00177 CPTextFieldDOMInputElement.style.border = "0px";
00178 CPTextFieldDOMInputElement.style.padding = "0px";
00179 CPTextFieldDOMInputElement.style.margin = "0px";
00180 CPTextFieldDOMInputElement.style.whiteSpace = "pre";
00181 CPTextFieldDOMInputElement.style.background = "transparent";
00182 CPTextFieldDOMInputElement.style.outline = "none";
00183
00184 CPTextFieldBlurFunction = function(anEvent)
00185 {
00186 if (CPTextFieldInputOwner && CPTextFieldInputOwner._DOMElement != CPTextFieldDOMInputElement.parentNode)
00187 return;
00188
00189 if (!CPTextFieldInputResigning)
00190 {
00191 [[CPTextFieldInputOwner window] makeFirstResponder:nil];
00192 return;
00193 }
00194
00195 CPTextFieldHandleBlur(anEvent, CPTextFieldDOMInputElement);
00196 CPTextFieldInputDidBlur = YES;
00197
00198 return true;
00199 }
00200
00201 CPTextFieldHandleBlur = function(anEvent)
00202 {
00203 CPTextFieldInputOwner = nil;
00204
00205 [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
00206 }
00207
00208
00209 CPTextFieldDOMInputElement.onblur = CPTextFieldBlurFunction;
00210
00211 CPTextFieldDOMStandardInputElement = CPTextFieldDOMInputElement;
00212 }
00213
00214 if (CPFeatureIsCompatible(CPInputTypeCanBeChangedFeature))
00215 {
00216 if ([self isSecure])
00217 CPTextFieldDOMInputElement.type = "password";
00218 else
00219 CPTextFieldDOMInputElement.type = "text";
00220
00221 return CPTextFieldDOMInputElement;
00222 }
00223
00224 if ([self isSecure])
00225 {
00226 if (!CPTextFieldDOMPasswordInputElement)
00227 {
00228 CPTextFieldDOMPasswordInputElement = document.createElement("input");
00229 CPTextFieldDOMPasswordInputElement.style.position = "absolute";
00230 CPTextFieldDOMPasswordInputElement.style.border = "0px";
00231 CPTextFieldDOMPasswordInputElement.style.padding = "0px";
00232 CPTextFieldDOMPasswordInputElement.style.margin = "0px";
00233 CPTextFieldDOMPasswordInputElement.style.whiteSpace = "pre";
00234 CPTextFieldDOMPasswordInputElement.style.background = "transparent";
00235 CPTextFieldDOMPasswordInputElement.style.outline = "none";
00236 CPTextFieldDOMPasswordInputElement.type = "password";
00237
00238 CPTextFieldDOMPasswordInputElement.onblur = CPTextFieldBlurFunction;
00239 }
00240
00241 CPTextFieldDOMInputElement = CPTextFieldDOMPasswordInputElement;
00242 }
00243 else
00244 {
00245 CPTextFieldDOMInputElement = CPTextFieldDOMStandardInputElement;
00246 }
00247
00248 return CPTextFieldDOMInputElement;
00249 }
00250 #endif
00251
00252 - (id)initWithFrame:(CGRect)aFrame
00253 {
00254 self = [super initWithFrame:aFrame];
00255
00256 if (self)
00257 {
00258 [self setStringValue:@""];
00259 [self setPlaceholderString:@""];
00260
00261 _sendActionOn = CPKeyUpMask | CPKeyDownMask;
00262
00263 [self setValue:CPLeftTextAlignment forThemeAttribute:@"alignment"];
00264 }
00265
00266 return self;
00267 }
00268
00269 #pragma mark Controlling Editability and Selectability
00270
00274 - (void)setEditable:(BOOL)shouldBeEditable
00275 {
00276 _isEditable = shouldBeEditable;
00277 }
00278
00282 - (BOOL)isEditable
00283 {
00284 return _isEditable;
00285 }
00286
00291 - (void)setSelectable:(BOOL)aFlag
00292 {
00293 _isSelectable = aFlag;
00294 }
00295
00299 - (BOOL)isSelectable
00300 {
00301 return _isSelectable;
00302 }
00303
00308 - (void)setSecure:(BOOL)aFlag
00309 {
00310 _isSecure = aFlag;
00311 }
00312
00316 - (BOOL)isSecure
00317 {
00318 return _isSecure;
00319 }
00320
00321
00326 - (void)setBezeled:(BOOL)shouldBeBezeled
00327 {
00328 if (shouldBeBezeled)
00329 [self setThemeState:CPThemeStateBezeled];
00330 else
00331 [self unsetThemeState:CPThemeStateBezeled];
00332 }
00333
00337 - (BOOL)isBezeled
00338 {
00339 return [self hasThemeState:CPThemeStateBezeled];
00340 }
00341
00346 - (void)setBezelStyle:(CPTextFieldBezelStyle)aBezelStyle
00347 {
00348 var shouldBeRounded = aBezelStyle === CPTextFieldRoundedBezel;
00349
00350 if (shouldBeRounded)
00351 [self setThemeState:CPTextFieldStateRounded];
00352 else
00353 [self unsetThemeState:CPTextFieldStateRounded];
00354 }
00355
00359 - (CPTextFieldBezelStyle)bezelStyle
00360 {
00361 if ([self hasThemeState:CPTextFieldStateRounded])
00362 return CPTextFieldRoundedBezel;
00363
00364 return CPTextFieldSquareBezel;
00365 }
00366
00371 - (void)setBordered:(BOOL)shouldBeBordered
00372 {
00373 if (shouldBeBordered)
00374 [self setThemeState:CPThemeStateBordered];
00375 else
00376 [self unsetThemeState:CPThemeStateBordered];
00377 }
00378
00382 - (BOOL)isBordered
00383 {
00384 return [self hasThemeState:CPThemeStateBordered];
00385 }
00386
00391 - (void)setDrawsBackground:(BOOL)shouldDrawBackground
00392 {
00393 if (_drawsBackground == shouldDrawBackground)
00394 return;
00395
00396 _drawsBackground = shouldDrawBackground;
00397
00398 [self setNeedsLayout];
00399 [self setNeedsDisplay:YES];
00400 }
00401
00405 - (BOOL)drawsBackground
00406 {
00407 return _drawsBackground;
00408 }
00409
00414 - (void)setTextFieldBackgroundColor:(CPColor)aColor
00415 {
00416 if (_textFieldBackgroundColor == aColor)
00417 return;
00418
00419 _textFieldBackgroundColor = aColor;
00420
00421 [self setNeedsLayout];
00422 [self setNeedsDisplay:YES];
00423 }
00424
00428 - (CPColor)textFieldBackgroundColor
00429 {
00430 return _textFieldBackgroundColor;
00431 }
00432
00433
00434 - (BOOL)acceptsFirstResponder
00435 {
00436 return [self isEditable] && [self isEnabled];
00437 }
00438
00439
00440 - (BOOL)becomeFirstResponder
00441 {
00442 #if PLATFORM(DOM)
00443 if (CPTextFieldInputOwner && [CPTextFieldInputOwner window] !== [self window])
00444 [[CPTextFieldInputOwner window] makeFirstResponder:nil];
00445 #endif
00446
00447 [self setThemeState:CPThemeStateEditing];
00448
00449 [self _updatePlaceholderState];
00450
00451 [self setNeedsLayout];
00452
00453 _isEditing = NO;
00454
00455 #if PLATFORM(DOM)
00456
00457 var string = [self stringValue],
00458 element = [self _inputElement];
00459
00460 element.value = string;
00461 element.style.color = [[self currentValueForThemeAttribute:@"text-color"] cssString];
00462 element.style.font = [[self currentValueForThemeAttribute:@"font"] cssString];
00463 element.style.zIndex = 1000;
00464
00465 switch ([self alignment])
00466 {
00467 case CPCenterTextAlignment: element.style.textAlign = "center";
00468 break;
00469 case CPRightTextAlignment: element.style.textAlign = "right";
00470 break;
00471 default: element.style.textAlign = "left";
00472 }
00473
00474 var contentRect = [self contentRectForBounds:[self bounds]];
00475
00476 element.style.top = _CGRectGetMinY(contentRect) + "px";
00477 element.style.left = (_CGRectGetMinX(contentRect) - 1) + "px";
00478 element.style.width = _CGRectGetWidth(contentRect) + "px";
00479 element.style.height = _CGRectGetHeight(contentRect) + "px";
00480
00481 _DOMElement.appendChild(element);
00482
00483 window.setTimeout(function()
00484 {
00485 element.focus();
00486 [self textDidFocus:[CPNotification notificationWithName:CPTextFieldDidFocusNotification object:self userInfo:nil]];
00487 CPTextFieldInputOwner = self;
00488 }, 0.0);
00489
00490 element.value = [self stringValue];
00491
00492 [[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
00493
00494 CPTextFieldInputIsActive = YES;
00495
00496 if (document.attachEvent)
00497 {
00498 CPTextFieldCachedSelectStartFunction = [[self window] platformWindow]._DOMBodyElement.onselectstart;
00499 CPTextFieldCachedDragFunction = [[self window] platformWindow]._DOMBodyElement.ondrag;
00500
00501 [[self window] platformWindow]._DOMBodyElement.ondrag = function () {};
00502 [[self window] platformWindow]._DOMBodyElement.onselectstart = function () {};
00503 }
00504 #endif
00505
00506 return YES;
00507 }
00508
00509
00510 - (BOOL)resignFirstResponder
00511 {
00512 [self unsetThemeState:CPThemeStateEditing];
00513
00514 [self _updatePlaceholderState];
00515
00516 [self setNeedsLayout];
00517
00518 #if PLATFORM(DOM)
00519
00520 var element = [self _inputElement];
00521
00522 [self setObjectValue:element.value];
00523
00524 CPTextFieldInputResigning = YES;
00525 element.blur();
00526
00527 if (!CPTextFieldInputDidBlur)
00528 CPTextFieldBlurFunction();
00529
00530 CPTextFieldInputDidBlur = NO;
00531 CPTextFieldInputResigning = NO;
00532
00533 if (element.parentNode == _DOMElement)
00534 element.parentNode.removeChild(element);
00535
00536 CPTextFieldInputIsActive = NO;
00537
00538 if (document.attachEvent)
00539 {
00540 CPTextFieldCachedSelectStartFunction = nil;
00541 CPTextFieldCachedDragFunction = nil;
00542
00543 [[self window] platformWindow]._DOMBodyElement.ondrag = CPTextFieldCachedDragFunction;
00544 [[self window] platformWindow]._DOMBodyElement.onselectstart = CPTextFieldCachedSelectStartFunction;
00545 }
00546
00547 #endif
00548
00549
00550 if (_isEditing)
00551 {
00552 _isEditing = NO;
00553 [self textDidEndEditing:[CPNotification notificationWithName:CPControlTextDidEndEditingNotification object:self userInfo:nil]];
00554
00555 if ([self sendsActionOnEndEditing])
00556 [self sendAction:[self action] to:[self target]];
00557 }
00558
00559 [self textDidBlur:[CPNotification notificationWithName:CPTextFieldDidBlurNotification object:self userInfo:nil]];
00560
00561 return YES;
00562 }
00563
00567 - (BOOL)needsPanelToBecomeKey
00568 {
00569 return YES;
00570 }
00571
00572 - (void)mouseDown:(CPEvent)anEvent
00573 {
00574
00575 if ([self isEditable] && [self isEnabled])
00576 return [[self window] makeFirstResponder:self];
00577 else
00578 return [[self nextResponder] mouseDown:anEvent];
00579 }
00580
00581 - (void)mouseUp:(CPEvent)anEvent
00582 {
00583 if (![self isEditable] || ![self isEnabled])
00584 [[self nextResponder] mouseUp:anEvent];
00585 }
00586
00587 - (void)mouseDragged:(CPEvent)anEvent
00588 {
00589 if (![self isEditable] || ![self isEnabled])
00590 [[self nextResponder] mouseDragged:anEvent];
00591 }
00592
00593 - (void)keyUp:(CPEvent)anEvent
00594 {
00595 var oldValue = [self stringValue];
00596 [self _setStringValue:[self _inputElement].value];
00597
00598 if (oldValue !== [self stringValue])
00599 {
00600 if (!_isEditing)
00601 {
00602 _isEditing = YES;
00603 [self textDidBeginEditing:[CPNotification notificationWithName:CPControlTextDidBeginEditingNotification object:self userInfo:nil]];
00604 }
00605
00606 [self textDidChange:[CPNotification notificationWithName:CPControlTextDidChangeNotification object:self userInfo:nil]];
00607 }
00608
00609 [[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
00610 }
00611
00612 - (void)keyDown:(CPEvent)anEvent
00613 {
00614 if ([anEvent keyCode] === CPReturnKeyCode)
00615 {
00616 if (_isEditing)
00617 {
00618 _isEditing = NO;
00619 [self textDidEndEditing:[CPNotification notificationWithName:CPControlTextDidEndEditingNotification object:self userInfo:nil]];
00620 }
00621
00622 [self sendAction:[self action] to:[self target]];
00623 [self selectText:nil];
00624
00625 [[[self window] platformWindow] _propagateCurrentDOMEvent:NO];
00626 }
00627 else if ([anEvent keyCode] === CPTabKeyCode)
00628 {
00629 if ([anEvent modifierFlags] & CPShiftKeyMask)
00630 [[self window] selectPreviousKeyView:self];
00631 else
00632 [[self window] selectNextKeyView:self];
00633
00634 if ([[[self window] firstResponder] respondsToSelector:@selector(selectText:)])
00635 [[[self window] firstResponder] selectText:self];
00636
00637 [[[self window] platformWindow] _propagateCurrentDOMEvent:NO];
00638 }
00639 else
00640 [[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
00641
00642 [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
00643 }
00644
00645
00646 - (void)textDidBlur:(CPNotification)note
00647 {
00648
00649 if([note object] != self)
00650 return;
00651
00652 [[CPNotificationCenter defaultCenter] postNotification:note];
00653 }
00654
00655 - (void)textDidFocus:(CPNotification)note
00656 {
00657
00658 if([note object] != self)
00659 return;
00660
00661 [[CPNotificationCenter defaultCenter] postNotification:note];
00662 }
00663
00667 - (id)objectValue
00668 {
00669 return [super objectValue];
00670 }
00671
00672
00673
00674
00675 - (void)_setStringValue:(id)aValue
00676 {
00677 [self willChangeValueForKey:@"objectValue"];
00678 [super setObjectValue:String(aValue)];
00679 [self _updatePlaceholderState];
00680 [self didChangeValueForKey:@"objectValue"];
00681 }
00682
00683 - (void)setObjectValue:(id)aValue
00684 {
00685 [super setObjectValue:aValue];
00686
00687 #if PLATFORM(DOM)
00688
00689 if (CPTextFieldInputOwner === self || [[self window] firstResponder] === self)
00690 [self _inputElement].value = aValue;
00691 #endif
00692
00693 [self _updatePlaceholderState];
00694 }
00695
00696 - (void)_updatePlaceholderState
00697 {
00698 var string = [self stringValue];
00699
00700 if ((!string || string.length === 0) && ![self hasThemeState:CPThemeStateEditing])
00701 [self setThemeState:CPTextFieldStatePlaceholder];
00702 else
00703 [self unsetThemeState:CPTextFieldStatePlaceholder];
00704 }
00705
00710 -(void)setPlaceholderString:(CPString)aStringValue
00711 {
00712 if (_placeholderString === aStringValue)
00713 return;
00714
00715 _placeholderString = aStringValue;
00716
00717
00718 if ([self hasThemeState:CPTextFieldStatePlaceholder])
00719 {
00720 [self setNeedsLayout];
00721 [self setNeedsDisplay:YES];
00722 }
00723 }
00724
00728 - (CPString)placeholderString
00729 {
00730 return _placeholderString;
00731 }
00732
00750 - (void)sizeToFit
00751 {
00752 var size = [([self stringValue] || " ") sizeWithFont:[self currentValueForThemeAttribute:@"font"]],
00753 contentInset = [self currentValueForThemeAttribute:@"content-inset"],
00754 minSize = [self currentValueForThemeAttribute:@"min-size"],
00755 maxSize = [self currentValueForThemeAttribute:@"max-size"];
00756
00757 size.width = MAX(size.width + contentInset.left + contentInset.right, minSize.width);
00758 size.height = MAX(size.height + contentInset.top + contentInset.bottom, minSize.height);
00759
00760 if (maxSize.width >= 0.0)
00761 size.width = MIN(size.width, maxSize.width);
00762
00763 if (maxSize.height >= 0.0)
00764 size.height = MIN(size.height, maxSize.height);
00765
00766 if ([self isEditable])
00767 size.width = CGRectGetWidth([self frame]);
00768
00769 [self setFrameSize:size];
00770 }
00771
00775 - (void)selectText:(id)sender
00776 {
00777 #if PLATFORM(DOM)
00778 var element = [self _inputElement];
00779
00780 if (([self isEditable] || [self isSelectable]))
00781 {
00782 if ([[self window] firstResponder] === self)
00783 window.setTimeout(function() { element.select(); }, 0);
00784 else
00785 {
00786 [[self window] makeFirstResponder:self];
00787 window.setTimeout(function() {[self selectText:sender];}, 0);
00788 }
00789 }
00790 #endif
00791 }
00792
00793 - (void)copy:(id)sender
00794 {
00795 if (![CPPlatform isBrowser])
00796 {
00797 var selectedRange = [self selectedRange];
00798
00799 if (selectedRange.length < 1)
00800 return;
00801
00802 var pasteboard = [CPPasteboard generalPasteboard],
00803 stringValue = [self stringValue],
00804 stringForPasting = [stringValue substringWithRange:selectedRange];
00805
00806 [pasteboard declareTypes:[CPStringPboardType] owner:nil];
00807 [pasteboard setString:stringForPasting forType:CPStringPboardType];
00808 }
00809 }
00810
00811 - (void)cut:(id)sender
00812 {
00813 if (![CPPlatform isBrowser])
00814 {
00815 [self copy:sender];
00816 [self deleteBackward:sender];
00817 }
00818 }
00819
00820 - (void)paste:(id)sender
00821 {
00822 if (![CPPlatform isBrowser])
00823 {
00824 var pasteboard = [CPPasteboard generalPasteboard];
00825
00826 if (![[pasteboard types] containsObject:CPStringPboardType])
00827 return;
00828
00829 [self deleteBackward:sender];
00830
00831 var selectedRange = [self selectedRange],
00832 stringValue = [self stringValue],
00833 pasteString = [pasteboard stringForType:CPStringPboardType],
00834 newValue = [stringValue stringByReplacingCharactersInRange:selectedRange withString:pasteString];
00835
00836 [self setStringValue:newValue];
00837 [self setSelectedRange:CPMakeRange(selectedRange.location+pasteString.length, 0)];
00838 }
00839 }
00840
00841 - (CPRange)selectedRange
00842 {
00843 if ([[self window] firstResponder] !== self)
00844 return CPMakeRange(0, 0);
00845
00846
00847 try
00848 {
00849 var inputElement = [self _inputElement],
00850 selectionStart = inputElement.selectionStart,
00851 selectionEnd = inputElement.selectionEnd;
00852
00853 if ([selectionStart isKindOfClass:CPNumber])
00854 return CPMakeRange(selectionStart, selectionEnd - selectionStart);
00855
00856
00857 var theDocument = inputElement.ownerDocument || inputElement.document,
00858 selectionRange = theDocument.selection.createRange(),
00859 range = inputElement.createTextRange();
00860
00861 if (range.inRange(selectionRange))
00862 {
00863 range.setEndPoint('EndToStart', selectionRange);
00864 return CPMakeRange(range.text.length, selectionRange.text.length);
00865 }
00866 }
00867 catch (e)
00868 {
00869
00870 }
00871
00872 return CGMakeRange(0, 0);
00873 }
00874
00875 - (void)setSelectedRange:(CPRange)aRange
00876 {
00877 if (![[self window] firstResponder] === self)
00878 return;
00879
00880 var inputElement = [self _inputElement];
00881
00882 try
00883 {
00884 if ([inputElement.selectionStart isKindOfClass:CPNumber])
00885 {
00886 inputElement.selectionStart = aRange.location;
00887 inputElement.selectionEnd = CPMaxRange(aRange);
00888 }
00889 else
00890 {
00891
00892 var theDocument = inputElement.ownerDocument || inputElement.document,
00893 existingRange = theDocument.selection.createRange(),
00894 range = inputElement.createTextRange();
00895
00896 if (range.inRange(existingRange))
00897 {
00898 range.collapse(true);
00899 range.move('character', aRange.location);
00900 range.moveEnd('character', aRange.length);
00901 range.select();
00902 }
00903 }
00904 }
00905 catch (e)
00906 {
00907 }
00908 }
00909
00910 - (void)selectAll:(id)sender
00911 {
00912 [self selectText:sender];
00913 }
00914
00915 - (void)deleteBackward:(id)sender
00916 {
00917 var selectedRange = [self selectedRange],
00918 stringValue = [self stringValue],
00919 newValue = [stringValue stringByReplacingCharactersInRange:selectedRange withString:""];
00920
00921 [self setStringValue:newValue];
00922 [self setSelectedRange:CPMakeRange(selectedRange.location, 0)];
00923 }
00924
00925 #pragma mark Setting the Delegate
00926
00927 - (void)setDelegate:(id)aDelegate
00928 {
00929 var defaultCenter = [CPNotificationCenter defaultCenter];
00930
00931
00932 if (_delegate)
00933 {
00934 [defaultCenter removeObserver:_delegate name:CPControlTextDidBeginEditingNotification object:self];
00935 [defaultCenter removeObserver:_delegate name:CPControlTextDidChangeNotification object:self];
00936 [defaultCenter removeObserver:_delegate name:CPControlTextDidEndEditingNotification object:self];
00937 [defaultCenter removeObserver:_delegate name:CPTextFieldDidFocusNotification object:self];
00938 [defaultCenter removeObserver:_delegate name:CPTextFieldDidBlurNotification object:self];
00939 }
00940
00941 _delegate = aDelegate;
00942
00943 if ([_delegate respondsToSelector:@selector(controlTextDidBeginEditing:)])
00944 [defaultCenter
00945 addObserver:_delegate
00946 selector:@selector(controlTextDidBeginEditing:)
00947 name:CPControlTextDidBeginEditingNotification
00948 object:self];
00949
00950 if ([_delegate respondsToSelector:@selector(controlTextDidChange:)])
00951 [defaultCenter
00952 addObserver:_delegate
00953 selector:@selector(controlTextDidChange:)
00954 name:CPControlTextDidChangeNotification
00955 object:self];
00956
00957
00958 if ([_delegate respondsToSelector:@selector(controlTextDidEndEditing:)])
00959 [defaultCenter
00960 addObserver:_delegate
00961 selector:@selector(controlTextDidEndEditing:)
00962 name:CPControlTextDidEndEditingNotification
00963 object:self];
00964
00965 if ([_delegate respondsToSelector:@selector(controlTextDidFocus:)])
00966 [defaultCenter
00967 addObserver:_delegate
00968 selector:@selector(controlTextDidFocus:)
00969 name:CPTextFieldDidFocusNotification
00970 object:self];
00971
00972 if ([_delegate respondsToSelector:@selector(controlTextDidBlur:)])
00973 [defaultCenter
00974 addObserver:_delegate
00975 selector:@selector(controlTextDidBlur:)
00976 name:CPTextFieldDidBlurNotification
00977 object:self];
00978 }
00979
00980 - (id)delegate
00981 {
00982 return _delegate;
00983 }
00984
00985 - (CGRect)contentRectForBounds:(CGRect)bounds
00986 {
00987 var contentInset = [self currentValueForThemeAttribute:@"content-inset"];
00988
00989 if (!contentInset)
00990 return bounds;
00991
00992 bounds.origin.x += contentInset.left;
00993 bounds.origin.y += contentInset.top;
00994 bounds.size.width -= contentInset.left + contentInset.right;
00995 bounds.size.height -= contentInset.top + contentInset.bottom;
00996
00997 return bounds;
00998 }
00999
01000 - (CGRect)bezelRectForBounds:(CFRect)bounds
01001 {
01002 var bezelInset = [self currentValueForThemeAttribute:@"bezel-inset"];
01003
01004 if (_CGInsetIsEmpty(bezelInset))
01005 return bounds;
01006
01007 bounds.origin.x += bezelInset.left;
01008 bounds.origin.y += bezelInset.top;
01009 bounds.size.width -= bezelInset.left + bezelInset.right;
01010 bounds.size.height -= bezelInset.top + bezelInset.bottom;
01011
01012 return bounds;
01013 }
01014
01015 - (CGRect)rectForEphemeralSubviewNamed:(CPString)aName
01016 {
01017 if (aName === "bezel-view")
01018 return [self bezelRectForBounds:[self bounds]];
01019
01020 else if (aName === "content-view")
01021 return [self contentRectForBounds:[self bounds]];
01022
01023 return [super rectForEphemeralSubviewNamed:aName];
01024 }
01025
01026 - (CPView)createEphemeralSubviewNamed:(CPString)aName
01027 {
01028 if (aName === "bezel-view")
01029 {
01030 var view = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
01031
01032 [view setHitTests:NO];
01033
01034 return view;
01035 }
01036 else
01037 {
01038 var view = [[_CPImageAndTextView alloc] initWithFrame:_CGRectMakeZero()];
01039
01040
01041 [view setHitTests:NO];
01042
01043 return view;
01044 }
01045
01046 return [super createEphemeralSubviewNamed:aName];
01047 }
01048
01049 - (void)layoutSubviews
01050 {
01051 var bezelView = [self layoutEphemeralSubviewNamed:@"bezel-view"
01052 positioned:CPWindowBelow
01053 relativeToEphemeralSubviewNamed:@"content-view"];
01054
01055 if (bezelView)
01056 [bezelView setBackgroundColor:[self currentValueForThemeAttribute:@"bezel-color"]];
01057
01058 var contentView = [self layoutEphemeralSubviewNamed:@"content-view"
01059 positioned:CPWindowAbove
01060 relativeToEphemeralSubviewNamed:@"bezel-view"];
01061
01062 if (contentView)
01063 {
01064 [contentView setHidden:[self hasThemeState:CPThemeStateEditing]];
01065
01066 var string = "";
01067
01068 if ([self hasThemeState:CPTextFieldStatePlaceholder])
01069 string = [self placeholderString];
01070 else
01071 {
01072 string = [self stringValue];
01073
01074 if ([self isSecure])
01075 string = secureStringForString(string);
01076 }
01077
01078 [contentView setText:string];
01079
01080 [contentView setTextColor:[self currentValueForThemeAttribute:@"text-color"]];
01081 [contentView setFont:[self currentValueForThemeAttribute:@"font"]];
01082 [contentView setAlignment:[self currentValueForThemeAttribute:@"alignment"]];
01083 [contentView setVerticalAlignment:[self currentValueForThemeAttribute:@"vertical-alignment"]];
01084 [contentView setLineBreakMode:[self currentValueForThemeAttribute:@"line-break-mode"]];
01085 [contentView setTextShadowColor:[self currentValueForThemeAttribute:@"text-shadow-color"]];
01086 [contentView setTextShadowOffset:[self currentValueForThemeAttribute:@"text-shadow-offset"]];
01087 }
01088 }
01089
01090 @end
01091
01092 var secureStringForString = function(aString)
01093 {
01094
01095 if (!aString)
01096 return "";
01097
01098 return Array(aString.length+1).join(CPSecureTextFieldCharacter);
01099 }
01100
01101
01102 var CPTextFieldIsEditableKey = "CPTextFieldIsEditableKey",
01103 CPTextFieldIsSelectableKey = "CPTextFieldIsSelectableKey",
01104 CPTextFieldIsBorderedKey = "CPTextFieldIsBorderedKey",
01105 CPTextFieldIsBezeledKey = "CPTextFieldIsBezeledKey",
01106 CPTextFieldBezelStyleKey = "CPTextFieldBezelStyleKey",
01107 CPTextFieldDrawsBackgroundKey = "CPTextFieldDrawsBackgroundKey",
01108 CPTextFieldLineBreakModeKey = "CPTextFieldLineBreakModeKey",
01109 CPTextFieldBackgroundColorKey = "CPTextFieldBackgroundColorKey",
01110 CPTextFieldPlaceholderStringKey = "CPTextFieldPlaceholderStringKey";
01111
01112 @implementation CPTextField (CPCoding)
01113
01119 - (id)initWithCoder:(CPCoder)aCoder
01120 {
01121 self = [super initWithCoder:aCoder];
01122
01123 if (self)
01124 {
01125 [self setEditable:[aCoder decodeBoolForKey:CPTextFieldIsEditableKey]];
01126 [self setSelectable:[aCoder decodeBoolForKey:CPTextFieldIsSelectableKey]];
01127
01128 [self setDrawsBackground:[aCoder decodeBoolForKey:CPTextFieldDrawsBackgroundKey]];
01129
01130 [self setTextFieldBackgroundColor:[aCoder decodeObjectForKey:CPTextFieldBackgroundColorKey]];
01131
01132 [self setPlaceholderString:[aCoder decodeObjectForKey:CPTextFieldPlaceholderStringKey]];
01133 }
01134
01135 return self;
01136 }
01137
01142 - (void)encodeWithCoder:(CPCoder)aCoder
01143 {
01144 [super encodeWithCoder:aCoder];
01145
01146 [aCoder encodeBool:_isEditable forKey:CPTextFieldIsEditableKey];
01147 [aCoder encodeBool:_isSelectable forKey:CPTextFieldIsSelectableKey];
01148
01149 [aCoder encodeBool:_drawsBackground forKey:CPTextFieldDrawsBackgroundKey];
01150
01151 [aCoder encodeObject:_textFieldBackgroundColor forKey:CPTextFieldBackgroundColorKey];
01152
01153 [aCoder encodeObject:_placeholderString forKey:CPTextFieldPlaceholderStringKey];
01154 }
01155
01156 @end
01157