You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
378 lines
11 KiB
378 lines
11 KiB
/*****************************************************************************
|
|
* VLCUIWidgets.m: Widgets for VLC's Minimal Dialog Provider for Mac OS X
|
|
*****************************************************************************
|
|
* Copyright (C) 2009-2010 the VideoLAN team
|
|
* $Id$
|
|
*
|
|
* Authors: Pierre d'Herbemont <pdherbemont # videolan dot>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
|
|
*****************************************************************************/
|
|
|
|
#import "VLCUIWidgets.h"
|
|
|
|
@implementation VLCDialogButton
|
|
@synthesize widget;
|
|
@end
|
|
|
|
|
|
@implementation VLCDialogPopUpButton
|
|
@synthesize widget;
|
|
@end
|
|
|
|
|
|
@implementation VLCDialogTextField
|
|
@synthesize widget;
|
|
@end
|
|
|
|
|
|
@implementation VLCDialogWindow
|
|
@synthesize dialog;
|
|
@end
|
|
|
|
|
|
@implementation VLCDialogList
|
|
@synthesize widget;
|
|
@synthesize contentArray;
|
|
|
|
- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView
|
|
{
|
|
return [contentArray count];
|
|
}
|
|
|
|
- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
|
|
{
|
|
return [[contentArray objectAtIndex:rowIndex] objectForKey:@"text"];
|
|
}
|
|
@end
|
|
|
|
|
|
@implementation VLCDialogGridView
|
|
|
|
- (void)dealloc
|
|
{
|
|
[_gridedViews release];
|
|
[super dealloc];
|
|
}
|
|
|
|
- (void)recomputeCount
|
|
{
|
|
_colCount = 0;
|
|
_rowCount = 0;
|
|
for (NSDictionary *obj in _gridedViews)
|
|
{
|
|
NSUInteger row = [[obj objectForKey:@"row"] intValue];
|
|
NSUInteger col = [[obj objectForKey:@"col"] intValue];
|
|
if (col + 1 > _colCount)
|
|
_colCount = col + 1;
|
|
if (row + 1 > _rowCount)
|
|
_rowCount = row + 1;
|
|
}
|
|
}
|
|
|
|
- (void)recomputeWindowSize
|
|
{
|
|
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(recomputeWindowSize) object:nil];
|
|
|
|
NSWindow *window = [self window];
|
|
NSRect frame = [window frame];
|
|
NSRect contentRect = [window contentRectForFrameRect:frame];
|
|
contentRect.size = [self flexSize:frame.size];
|
|
NSRect newFrame = [window frameRectForContentRect:contentRect];
|
|
newFrame.origin.y -= newFrame.size.height - frame.size.height;
|
|
newFrame.origin.x -= (newFrame.size.width - frame.size.width) / 2;
|
|
[window setFrame:newFrame display:YES animate:YES];
|
|
}
|
|
|
|
- (NSSize)objectSizeToFit:(NSView *)view
|
|
{
|
|
if ([view isKindOfClass:[NSControl class]]) {
|
|
NSControl *control = (NSControl *)view;
|
|
return [[control cell] cellSize];
|
|
}
|
|
return [view frame].size;
|
|
}
|
|
|
|
- (CGFloat)marginX
|
|
{
|
|
return 16;
|
|
}
|
|
- (CGFloat)marginY
|
|
{
|
|
return 8;
|
|
}
|
|
|
|
- (CGFloat)constrainedHeightOfRow:(NSUInteger)targetRow
|
|
{
|
|
CGFloat height = 0;
|
|
for(NSDictionary *obj in _gridedViews) {
|
|
NSUInteger row = [[obj objectForKey:@"row"] intValue];
|
|
if (row != targetRow)
|
|
continue;
|
|
NSUInteger rowSpan = [[obj objectForKey:@"rowSpan"] intValue];
|
|
if (rowSpan != 1)
|
|
continue;
|
|
NSView *view = [obj objectForKey:@"view"];
|
|
if ([view autoresizingMask] & NSViewHeightSizable)
|
|
continue;
|
|
NSSize sizeToFit = [self objectSizeToFit:view];
|
|
if (height < sizeToFit.height)
|
|
height = sizeToFit.height;
|
|
}
|
|
return height;
|
|
}
|
|
|
|
- (CGFloat)remainingRowsHeight
|
|
{
|
|
NSUInteger height = [self marginY];
|
|
if (!_rowCount)
|
|
return 0;
|
|
NSUInteger autosizedRows = 0;
|
|
for (NSUInteger i = 0; i < _rowCount; i++) {
|
|
CGFloat constrainedHeight = [self constrainedHeightOfRow:i];
|
|
if (!constrainedHeight)
|
|
autosizedRows++;
|
|
height += constrainedHeight + [self marginY];
|
|
}
|
|
CGFloat remaining = 0;
|
|
if (height < self.bounds.size.height && autosizedRows)
|
|
remaining = (self.bounds.size.height - height) / autosizedRows;
|
|
if (remaining < 0)
|
|
remaining = 0;
|
|
|
|
return remaining;
|
|
}
|
|
|
|
- (CGFloat)heightOfRow:(NSUInteger)targetRow
|
|
{
|
|
NSAssert(targetRow < _rowCount, @"accessing a non existing row");
|
|
CGFloat height = [self constrainedHeightOfRow:targetRow];
|
|
if (!height)
|
|
height = [self remainingRowsHeight];
|
|
return height;
|
|
}
|
|
|
|
|
|
- (CGFloat)topOfRow:(NSUInteger)targetRow
|
|
{
|
|
CGFloat top = [self marginY];
|
|
for (NSUInteger i = 1; i < _rowCount - targetRow; i++)
|
|
{
|
|
top += [self heightOfRow:_rowCount - i] + [self marginY];
|
|
}
|
|
return top;
|
|
}
|
|
|
|
- (CGFloat)constrainedWidthOfColumn:(NSUInteger)targetColumn
|
|
{
|
|
CGFloat width = 0;
|
|
for(NSDictionary *obj in _gridedViews) {
|
|
NSUInteger col = [[obj objectForKey:@"col"] intValue];
|
|
if (col != targetColumn)
|
|
continue;
|
|
NSUInteger colSpan = [[obj objectForKey:@"colSpan"] intValue];
|
|
if (colSpan != 1)
|
|
continue;
|
|
NSView *view = [obj objectForKey:@"view"];
|
|
if ([view autoresizingMask] & NSViewWidthSizable)
|
|
return 0;
|
|
NSSize sizeToFit = [self objectSizeToFit:view];
|
|
if (width < sizeToFit.width)
|
|
width = sizeToFit.width;
|
|
}
|
|
return width;
|
|
}
|
|
|
|
- (CGFloat)remainingColumnWidth
|
|
{
|
|
NSUInteger width = [self marginX];
|
|
if (!_colCount)
|
|
return 0;
|
|
NSUInteger autosizedCol = 0;
|
|
for (NSUInteger i = 0; i < _colCount; i++) {
|
|
CGFloat constrainedWidth = [self constrainedWidthOfColumn:i];
|
|
if (!constrainedWidth)
|
|
autosizedCol++;
|
|
width += constrainedWidth + [self marginX];
|
|
|
|
}
|
|
CGFloat remaining = 0;
|
|
if (width < self.bounds.size.width && autosizedCol)
|
|
remaining = (self.bounds.size.width - width) / autosizedCol;
|
|
if (remaining < 0)
|
|
remaining = 0;
|
|
return remaining;
|
|
}
|
|
|
|
- (CGFloat)widthOfColumn:(NSUInteger)targetColumn
|
|
{
|
|
CGFloat width = [self constrainedWidthOfColumn:targetColumn];
|
|
if (!width)
|
|
width = [self remainingColumnWidth];
|
|
return width;
|
|
}
|
|
|
|
|
|
- (CGFloat)leftOfColumn:(NSUInteger)targetColumn
|
|
{
|
|
CGFloat left = [self marginX];
|
|
for (NSUInteger i = 0; i < targetColumn; i++)
|
|
{
|
|
left += [self widthOfColumn:i] + [self marginX];
|
|
|
|
}
|
|
return left;
|
|
}
|
|
|
|
- (void)relayout
|
|
{
|
|
for(NSDictionary *obj in _gridedViews) {
|
|
NSUInteger row = [[obj objectForKey:@"row"] intValue];
|
|
NSUInteger col = [[obj objectForKey:@"col"] intValue];
|
|
NSUInteger rowSpan = [[obj objectForKey:@"rowSpan"] intValue];
|
|
NSUInteger colSpan = [[obj objectForKey:@"colSpan"] intValue];
|
|
NSView *view = [obj objectForKey:@"view"];
|
|
NSRect rect;
|
|
|
|
// Get the height
|
|
if ([view autoresizingMask] & NSViewHeightSizable || rowSpan > 1) {
|
|
CGFloat height = 0;
|
|
for (NSUInteger r = 0; r < rowSpan; r++) {
|
|
if (row + r >= _rowCount)
|
|
break;
|
|
height += [self heightOfRow:row + r] + [self marginY];
|
|
}
|
|
rect.size.height = height - [self marginY];
|
|
}
|
|
else
|
|
rect.size.height = [self objectSizeToFit:view].height;
|
|
|
|
// Get the width
|
|
if ([view autoresizingMask] & NSViewWidthSizable) {
|
|
CGFloat width = 0;
|
|
for (NSUInteger c = 0; c < colSpan; c++)
|
|
width += [self widthOfColumn:col + c] + [self marginX];
|
|
rect.size.width = width - [self marginX];
|
|
}
|
|
else
|
|
rect.size.width = [self objectSizeToFit:view].width;
|
|
|
|
// Top corner
|
|
rect.origin.y = [self topOfRow:row] + ([self heightOfRow:row] - rect.size.height) / 2;
|
|
rect.origin.x = [self leftOfColumn:col];
|
|
|
|
[view setFrame:rect];
|
|
[view setNeedsDisplay:YES];
|
|
}
|
|
}
|
|
|
|
- (NSMutableDictionary *)objectForView:(NSView *)view
|
|
{
|
|
for (NSMutableDictionary *dict in _gridedViews)
|
|
{
|
|
if ([dict objectForKey:@"view"] == view)
|
|
return dict;
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
- (void)addSubview:(NSView *)view atRow:(NSUInteger)row column:(NSUInteger)column rowSpan:(NSUInteger)rowSpan colSpan:(NSUInteger)colSpan
|
|
{
|
|
if (row + 1 > _rowCount)
|
|
_rowCount = row + 1;
|
|
if (column + 1 > _colCount)
|
|
_colCount = column + 1;
|
|
|
|
if (!_gridedViews)
|
|
_gridedViews = [[NSMutableArray alloc] init];
|
|
|
|
NSMutableDictionary *dict = [self objectForView:view];
|
|
if (!dict) {
|
|
dict = [NSMutableDictionary dictionary];
|
|
[dict setObject:view forKey:@"view"];
|
|
[_gridedViews addObject:dict];
|
|
}
|
|
[dict setObject:[NSNumber numberWithInt:rowSpan] forKey:@"rowSpan"];
|
|
[dict setObject:[NSNumber numberWithInt:colSpan] forKey:@"colSpan"];
|
|
[dict setObject:[NSNumber numberWithInt:row] forKey:@"row"];
|
|
[dict setObject:[NSNumber numberWithInt:column] forKey:@"col"];
|
|
|
|
|
|
[self addSubview:view];
|
|
[self relayout];
|
|
|
|
// Recompute the size of the window after making sure we won't see anymore update
|
|
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(recomputeWindowSize) object:nil];
|
|
[self performSelector:@selector(recomputeWindowSize) withObject:nil afterDelay:0.1];
|
|
}
|
|
|
|
- (void)removeSubview:(NSView *)view
|
|
{
|
|
NSDictionary *dict = [self objectForView:view];
|
|
if (dict)
|
|
[_gridedViews removeObject:dict];
|
|
[view removeFromSuperview];
|
|
|
|
[self recomputeCount];
|
|
[self recomputeWindowSize];
|
|
|
|
[self relayout];
|
|
[self setNeedsDisplay:YES];
|
|
}
|
|
|
|
- (void)setFrame:(NSRect)frameRect
|
|
{
|
|
[super setFrame:frameRect];
|
|
[self relayout];
|
|
}
|
|
|
|
- (NSSize)flexSize:(NSSize)size
|
|
{
|
|
if (!_rowCount || !_colCount)
|
|
return size;
|
|
|
|
CGFloat minHeight = [self marginY];
|
|
BOOL canFlexHeight = NO;
|
|
for (NSUInteger i = 0; i < _rowCount; i++) {
|
|
CGFloat constrained = [self constrainedHeightOfRow:i];
|
|
if (!constrained) {
|
|
canFlexHeight = YES;
|
|
constrained = 128;
|
|
}
|
|
minHeight += constrained + [self marginY];
|
|
}
|
|
|
|
CGFloat minWidth = [self marginX];
|
|
BOOL canFlexWidth = NO;
|
|
for (NSUInteger i = 0; i < _colCount; i++) {
|
|
CGFloat constrained = [self constrainedWidthOfColumn:i];
|
|
if (!constrained) {
|
|
canFlexWidth = YES;
|
|
constrained = 128;
|
|
}
|
|
minWidth += constrained + [self marginX];
|
|
}
|
|
if (size.width < minWidth)
|
|
size.width = minWidth;
|
|
if (size.height < minHeight)
|
|
size.height = minHeight;
|
|
if (!canFlexHeight)
|
|
size.height = minHeight;
|
|
if (!canFlexWidth)
|
|
size.width = minWidth;
|
|
return size;
|
|
}
|
|
|
|
@end
|
|
|