PageRenderTime 26ms CodeModel.GetById 17ms app.highlight 6ms RepoModel.GetById 1ms app.codeStats 0ms

/AppKit/CPButtonBar.j

http://github.com/cacaodev/cappuccino
Unknown | 340 lines | 273 code | 67 blank | 0 comment | 0 complexity | c5be201767d2b6b9cf37c5ea1bb6d4ea MD5 | raw file
  1/*
  2 * CPButtonBar.j
  3 * AppKit
  4 *
  5 * Created by Francisco Tolmasky.
  6 * Copyright 2009, 280 North, Inc.
  7 *
  8 * This library is free software; you can redistribute it and/or
  9 * modify it under the terms of the GNU Lesser General Public
 10 * License as published by the Free Software Foundation; either
 11 * version 2.1 of the License, or (at your option) any later version.
 12 *
 13 * This library is distributed in the hope that it will be useful,
 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 16 * Lesser General Public License for more details.
 17 *
 18 * You should have received a copy of the GNU Lesser General Public
 19 * License along with this library; if not, write to the Free Software
 20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 21 */
 22
 23@import "CPView.j"
 24@import "CPWindow_Constants.j"
 25
 26@class CPSplitView
 27
 28@global CPPopUpButtonStatePullsDown
 29@global CPKeyValueChangeOldKey
 30@global CPKeyValueChangeNewKey
 31@global CPKeyValueObservingOptionNew
 32@global CPKeyValueObservingOptionOld
 33
 34@implementation CPButtonBar : CPView
 35{
 36    BOOL    _hasResizeControl;
 37    BOOL    _resizeControlIsLeftAligned;
 38    CPArray _buttons;
 39}
 40
 41+ (id)plusButton
 42{
 43    var button = [[CPButton alloc] initWithFrame:CGRectMake(0, 0, 35, 25)],
 44        image = [[CPTheme defaultTheme] valueForAttributeWithName:@"button-image-plus" forClass:[CPButtonBar class]];
 45
 46    [button setBordered:NO];
 47    [button setImage:image];
 48    [button setImagePosition:CPImageOnly];
 49
 50    return button;
 51}
 52
 53+ (id)minusButton
 54{
 55    var button = [[CPButton alloc] initWithFrame:CGRectMake(0, 0, 35, 25)],
 56        image = [[CPTheme defaultTheme] valueForAttributeWithName:@"button-image-minus" forClass:[CPButtonBar class]];
 57
 58    [button setBordered:NO];
 59    [button setImage:image];
 60    [button setImagePosition:CPImageOnly];
 61
 62    return button;
 63}
 64
 65+ (id)actionPopupButton
 66{
 67    var button = [[CPPopUpButton alloc] initWithFrame:CGRectMake(0, 0, 35, 25)],
 68        image = [[CPTheme defaultTheme] valueForAttributeWithName:@"button-image-action" forClass:[CPButtonBar class]];
 69
 70    [button addItemWithTitle:nil];
 71    [[button lastItem] setImage:image];
 72    [button setImagePosition:CPImageOnly];
 73    [button setValue:CGInsetMake(0, 0, 0, 0) forThemeAttribute:"content-inset"];
 74
 75    [button setPullsDown:YES];
 76
 77    return button;
 78}
 79
 80+ (CPString)defaultThemeClass
 81{
 82    return @"button-bar";
 83}
 84
 85+ (CPDictionary)themeAttributes
 86{
 87    return @{
 88            @"resize-control-inset": CGInsetMake(0.0, 0.0, 0.0, 0.0),
 89            @"resize-control-size": CGSizeMakeZero(),
 90            @"resize-control-color": [CPNull null],
 91            @"bezel-color": [CPNull null],
 92            @"button-bezel-color": [CPNull null],
 93            @"button-text-color": [CPNull null],
 94            @"button-image-plus": [CPNull null],
 95            @"button-image-minus": [CPNull null],
 96            @"button-image-action": [CPNull null],
 97        };
 98}
 99
100- (id)initWithFrame:(CGRect)aFrame
101{
102    self = [super initWithFrame:aFrame];
103
104    if (self)
105    {
106        _buttons = [];
107        [self setNeedsLayout];
108    }
109
110    return self;
111}
112
113- (void)awakeFromCib
114{
115    var view = [self superview],
116        subview = self;
117
118    while (view)
119    {
120        if ([view isKindOfClass:[CPSplitView class]])
121        {
122            var viewIndex = [[view subviews] indexOfObject:subview];
123            [view setButtonBar:self forDividerAtIndex:viewIndex];
124
125            break;
126        }
127
128        subview = view;
129        view = [view superview];
130    }
131}
132
133- (void)setButtons:(CPArray)buttons
134{
135    for (var i = [_buttons count] - 1; i >= 0; i--)
136    {
137        [_buttons[i] removeFromSuperview];
138        [_buttons[i] removeObserver:self forKeyPath:@"hidden"];
139    }
140
141
142    _buttons = [CPArray arrayWithArray:buttons];
143
144    for (var i = [_buttons count] - 1; i >= 0; i--)
145    {
146        [_buttons[i] addObserver:self forKeyPath:@"hidden" options:CPKeyValueObservingOptionNew | CPKeyValueObservingOptionOld context:nil];
147        [_buttons[i] setBordered:YES];
148    }
149
150    [self setNeedsLayout];
151}
152
153- (CPArray)buttons
154{
155    return [CPArray arrayWithArray:_buttons];
156}
157
158- (void)setHasResizeControl:(BOOL)shouldHaveResizeControl
159{
160    if (_hasResizeControl === shouldHaveResizeControl)
161        return;
162
163    _hasResizeControl = !!shouldHaveResizeControl;
164    [self setNeedsLayout];
165}
166
167- (BOOL)hasResizeControl
168{
169    return _hasResizeControl;
170}
171
172- (void)setResizeControlIsLeftAligned:(BOOL)shouldBeLeftAligned
173{
174    if (_resizeControlIsLeftAligned === shouldBeLeftAligned)
175        return;
176
177    _resizeControlIsLeftAligned = !!shouldBeLeftAligned;
178    [self setNeedsLayout];
179}
180
181- (BOOL)resizeControlIsLeftAligned
182{
183    return _resizeControlIsLeftAligned;
184}
185
186- (CGRect)resizeControlFrame
187{
188    var inset = [self currentValueForThemeAttribute:@"resize-control-inset"],
189        size = [self currentValueForThemeAttribute:@"resize-control-size"],
190        currentSize = [self bounds],
191        leftOrigin = _resizeControlIsLeftAligned ? 0 : currentSize.size.width - size.width - inset.right - inset.left;
192
193    return CGRectMake(leftOrigin, 0, size.width + inset.left + inset.right, size.height + inset.top + inset.bottom);
194}
195
196- (CGRect)rectForEphemeralSubviewNamed:(CPString)aName
197{
198    if (aName === "resize-control-view")
199    {
200        var inset = [self currentValueForThemeAttribute:@"resize-control-inset"],
201            size = [self currentValueForThemeAttribute:@"resize-control-size"],
202            currentSize = [self bounds];
203
204        if (_resizeControlIsLeftAligned)
205            return CGRectMake(inset.left, inset.top, size.width, size.height);
206        else
207            return CGRectMake(currentSize.size.width - size.width - inset.right, inset.top, size.width, size.height);
208    }
209
210    return [super rectForEphemeralSubviewNamed:aName];
211}
212
213- (CPView)createEphemeralSubviewNamed:(CPString)aName
214{
215    if (aName === "resize-control-view")
216        return [[CPView alloc] initWithFrame:CGRectMakeZero()];
217
218    return [super createEphemeralSubviewNamed:aName];
219}
220
221- (void)layoutSubviews
222{
223    [self setBackgroundColor:[self currentValueForThemeAttribute:@"bezel-color"]];
224
225    var normalColor = [self valueForThemeAttribute:@"button-bezel-color" inState:CPThemeStateNormal],
226        highlightedColor = [self valueForThemeAttribute:@"button-bezel-color" inState:CPThemeStateHighlighted],
227        disabledColor = [self valueForThemeAttribute:@"button-bezel-color" inState:CPThemeStateDisabled],
228        textColor = [self valueForThemeAttribute:@"button-text-color" inState:CPThemeStateNormal];
229
230    var buttonsNotHidden = [CPArray arrayWithArray:_buttons],
231        count = [buttonsNotHidden count];
232
233    while (count--)
234    {
235        var button = buttonsNotHidden[count];
236
237        if ([button isHidden])
238        {
239            [button removeFromSuperview];
240            [buttonsNotHidden removeObject:button];
241        }
242    }
243
244    var currentButtonOffset = _resizeControlIsLeftAligned ? CGRectGetMaxX([self bounds]) + 1 : -1,
245        bounds = [self bounds],
246        height = CGRectGetHeight(bounds) - 1,
247        frameWidth = CGRectGetWidth(bounds),
248        resizeRect = _hasResizeControl ? [self rectForEphemeralSubviewNamed:"resize-control-view"] : CGRectMakeZero(),
249        resizeWidth = CGRectGetWidth(resizeRect),
250        availableWidth = frameWidth - resizeWidth - 1;
251
252    for (var i = 0, count = [buttonsNotHidden count]; i < count; i++)
253    {
254        var button = buttonsNotHidden[i],
255            width = CGRectGetWidth([button frame]);
256
257        if (availableWidth > width)
258            availableWidth -= width;
259        else
260            break;
261
262        if (_resizeControlIsLeftAligned)
263        {
264            [button setFrame:CGRectMake(currentButtonOffset - width, 1, width, height)];
265            currentButtonOffset -= width - 1;
266        }
267        else
268        {
269            [button setFrame:CGRectMake(currentButtonOffset, 1, width, height)];
270            currentButtonOffset += width - 1;
271        }
272
273        [button setValue:normalColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateNormal, CPThemeStateBordered]];
274        [button setValue:highlightedColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateHighlighted,  CPThemeStateBordered, ]];
275        [button setValue:disabledColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateDisabled, CPThemeStateBordered]];
276        [button setValue:textColor forThemeAttribute:@"text-color" inState:CPThemeStateBordered];
277
278        // FIXME shouldn't need this
279        [button setValue:normalColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateNormal, CPThemeStateBordered, CPPopUpButtonStatePullsDown]];
280        [button setValue:highlightedColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateHighlighted, CPThemeStateBordered, CPPopUpButtonStatePullsDown]];
281        [button setValue:disabledColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateDisabled, CPThemeStateBordered, CPPopUpButtonStatePullsDown]];
282
283        [self addSubview:button];
284    }
285
286    if (_hasResizeControl)
287    {
288        var resizeControlView = [self layoutEphemeralSubviewNamed:@"resize-control-view"
289                                                       positioned:CPWindowAbove
290                                  relativeToEphemeralSubviewNamed:nil];
291
292        [resizeControlView setAutoresizingMask: _resizeControlIsLeftAligned ? CPViewMaxXMargin : CPViewMinXMargin];
293        [resizeControlView setBackgroundColor:[self currentValueForThemeAttribute:@"resize-control-color"]];
294    }
295}
296
297- (void)observeValueForKeyPath:(CPString)keyPath ofObject:(id)object change:(CPDictionary)change context:(id)context
298{
299    if ([change objectForKey:CPKeyValueChangeOldKey] == [change objectForKey:CPKeyValueChangeNewKey])
300        return;
301
302    [self setNeedsLayout];
303}
304
305- (void)setFrameSize:(CGSize)aSize
306{
307    [super setFrameSize:aSize];
308    [self setNeedsLayout];
309}
310
311@end
312
313var CPButtonBarHasResizeControlKey = @"CPButtonBarHasResizeControlKey",
314    CPButtonBarResizeControlIsLeftAlignedKey = @"CPButtonBarResizeControlIsLeftAlignedKey",
315    CPButtonBarButtonsKey = @"CPButtonBarButtonsKey";
316
317@implementation CPButtonBar (CPCoding)
318
319- (void)encodeWithCoder:(CPCoder)aCoder
320{
321    [super encodeWithCoder:aCoder];
322
323    [aCoder encodeBool:_hasResizeControl forKey:CPButtonBarHasResizeControlKey];
324    [aCoder encodeBool:_resizeControlIsLeftAligned forKey:CPButtonBarResizeControlIsLeftAlignedKey];
325    [aCoder encodeObject:_buttons forKey:CPButtonBarButtonsKey];
326}
327
328- (id)initWithCoder:(CPCoder)aCoder
329{
330    if (self = [super initWithCoder:aCoder])
331    {
332        _buttons = [aCoder decodeObjectForKey:CPButtonBarButtonsKey] || [];
333        _hasResizeControl = [aCoder decodeBoolForKey:CPButtonBarHasResizeControlKey];
334        _resizeControlIsLeftAligned = [aCoder decodeBoolForKey:CPButtonBarResizeControlIsLeftAlignedKey];
335    }
336
337    return self;
338}
339
340@end