]> git.sesse.net Git - vlc/blob - modules/gui/macosx_dialog_provider/VLCUIWidgets.m
macosx dialog provider: moved UI widgets out of the interface implementation
[vlc] / modules / gui / macosx_dialog_provider / VLCUIWidgets.m
1 /*****************************************************************************
2  * VLCUIWidgets.m: Widgets for VLC's Minimal Dialog Provider for Mac OS X
3  *****************************************************************************
4  * Copyright (C) 2009-2010 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Pierre d'Herbemont <pdherbemont # videolan dot>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 #import "VLCUIWidgets.h"
25
26 @implementation VLCDialogButton
27 @synthesize widget;
28 @end
29
30
31 @implementation VLCDialogPopUpButton
32 @synthesize widget;
33 @end
34
35
36 @implementation VLCDialogTextField
37 @synthesize widget;
38 @end
39
40
41 @implementation VLCDialogWindow
42 @synthesize dialog;
43 @end
44
45
46 @implementation VLCDialogList
47 @synthesize widget;
48 @synthesize contentArray;
49
50 - (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView
51 {
52     return [contentArray count];
53 }
54
55 - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
56 {
57     return [[contentArray objectAtIndex:rowIndex] objectForKey:@"text"];
58 }
59 @end
60
61
62 @implementation VLCDialogGridView
63
64 - (void)dealloc
65 {
66     [_gridedViews release];
67     [super dealloc];
68 }
69
70 - (void)recomputeCount
71 {
72     _colCount = 0;
73     _rowCount = 0;
74     for (NSDictionary *obj in _gridedViews)
75     {
76         NSUInteger row = [[obj objectForKey:@"row"] intValue];
77         NSUInteger col = [[obj objectForKey:@"col"] intValue];
78         if (col + 1 > _colCount)
79             _colCount = col + 1;
80         if (row + 1 > _rowCount)
81             _rowCount = row + 1;
82     }
83 }
84
85 - (void)recomputeWindowSize
86 {
87     [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(recomputeWindowSize) object:nil];
88     
89     NSWindow *window = [self window];
90     NSRect frame = [window frame];
91     NSRect contentRect = [window contentRectForFrameRect:frame];
92     contentRect.size = [self flexSize:frame.size];
93     NSRect newFrame = [window frameRectForContentRect:contentRect];
94     newFrame.origin.y -= newFrame.size.height - frame.size.height;
95     newFrame.origin.x -= (newFrame.size.width - frame.size.width) / 2;
96     [window setFrame:newFrame display:YES animate:YES];
97 }
98
99 - (NSSize)objectSizeToFit:(NSView *)view
100 {
101     if ([view isKindOfClass:[NSControl class]]) {
102         NSControl *control = (NSControl *)view;
103         return [[control cell] cellSize];
104     }
105     return [view frame].size;
106 }
107
108 - (CGFloat)marginX
109 {
110     return 16;
111 }
112 - (CGFloat)marginY
113 {
114     return 8;
115 }
116
117 - (CGFloat)constrainedHeightOfRow:(NSUInteger)targetRow
118 {
119     CGFloat height = 0;
120     for(NSDictionary *obj in _gridedViews) {
121         NSUInteger row = [[obj objectForKey:@"row"] intValue];
122         if (row != targetRow)
123             continue;
124         NSUInteger rowSpan = [[obj objectForKey:@"rowSpan"] intValue];
125         if (rowSpan != 1)
126             continue;
127         NSView *view = [obj objectForKey:@"view"];
128         if ([view autoresizingMask] & NSViewHeightSizable)
129             continue;
130         NSSize sizeToFit = [self objectSizeToFit:view];
131         if (height < sizeToFit.height)
132             height = sizeToFit.height;
133     }
134     return height;
135 }
136
137 - (CGFloat)remainingRowsHeight
138 {
139     NSUInteger height = [self marginY];
140     if (!_rowCount)
141         return 0;
142     NSUInteger autosizedRows = 0;
143     for (NSUInteger i = 0; i < _rowCount; i++) {
144         CGFloat constrainedHeight = [self constrainedHeightOfRow:i];
145         if (!constrainedHeight)
146             autosizedRows++;
147         height += constrainedHeight + [self marginY];
148     }
149     CGFloat remaining = 0;
150     if (height < self.bounds.size.height && autosizedRows)
151         remaining = (self.bounds.size.height - height) / autosizedRows;
152     if (remaining < 0)
153         remaining = 0;
154     
155     return remaining;
156 }
157
158 - (CGFloat)heightOfRow:(NSUInteger)targetRow
159 {
160     NSAssert(targetRow < _rowCount, @"accessing a non existing row");
161     CGFloat height = [self constrainedHeightOfRow:targetRow];
162     if (!height)
163         height = [self remainingRowsHeight];
164     return height;
165 }
166
167
168 - (CGFloat)topOfRow:(NSUInteger)targetRow
169 {
170     CGFloat top = [self marginY];
171     for (NSUInteger i = 1; i < _rowCount - targetRow; i++)
172     {
173         top += [self heightOfRow:_rowCount - i] + [self marginY];
174     }
175     return top;
176 }
177
178 - (CGFloat)constrainedWidthOfColumn:(NSUInteger)targetColumn
179 {
180     CGFloat width = 0;
181     for(NSDictionary *obj in _gridedViews) {
182         NSUInteger col = [[obj objectForKey:@"col"] intValue];
183         if (col != targetColumn)
184             continue;
185         NSUInteger colSpan = [[obj objectForKey:@"colSpan"] intValue];
186         if (colSpan != 1)
187             continue;
188         NSView *view = [obj objectForKey:@"view"];
189         if ([view autoresizingMask] & NSViewWidthSizable)
190             return 0;
191         NSSize sizeToFit = [self objectSizeToFit:view];
192         if (width < sizeToFit.width)
193             width = sizeToFit.width;
194     }
195     return width;
196 }
197
198 - (CGFloat)remainingColumnWidth
199 {
200     NSUInteger width = [self marginX];
201     if (!_colCount)
202         return 0;
203     NSUInteger autosizedCol = 0;
204     for (NSUInteger i = 0; i < _colCount; i++) {
205         CGFloat constrainedWidth = [self constrainedWidthOfColumn:i];
206         if (!constrainedWidth)
207             autosizedCol++;
208         width += constrainedWidth + [self marginX];
209         
210     }
211     CGFloat remaining = 0;
212     if (width < self.bounds.size.width && autosizedCol)
213         remaining = (self.bounds.size.width - width) / autosizedCol;
214     if (remaining < 0)
215         remaining = 0;
216     return remaining;
217 }
218
219 - (CGFloat)widthOfColumn:(NSUInteger)targetColumn
220 {
221     CGFloat width = [self constrainedWidthOfColumn:targetColumn];
222     if (!width)
223         width = [self remainingColumnWidth];
224     return width;
225 }
226
227
228 - (CGFloat)leftOfColumn:(NSUInteger)targetColumn
229 {
230     CGFloat left = [self marginX];
231     for (NSUInteger i = 0; i < targetColumn; i++)
232     {
233         left += [self widthOfColumn:i] + [self marginX];
234         
235     }
236     return left;
237 }
238
239 - (void)relayout
240 {
241     for(NSDictionary *obj in _gridedViews) {
242         NSUInteger row = [[obj objectForKey:@"row"] intValue];
243         NSUInteger col = [[obj objectForKey:@"col"] intValue];
244         NSUInteger rowSpan = [[obj objectForKey:@"rowSpan"] intValue];
245         NSUInteger colSpan = [[obj objectForKey:@"colSpan"] intValue];
246         NSView *view = [obj objectForKey:@"view"];
247         NSRect rect;
248         
249         // Get the height
250         if ([view autoresizingMask] & NSViewHeightSizable || rowSpan > 1) {
251             CGFloat height = 0;
252             for (NSUInteger r = 0; r < rowSpan; r++) {
253                 if (row + r >= _rowCount)
254                     break;
255                 height += [self heightOfRow:row + r] + [self marginY];
256             }
257             rect.size.height = height - [self marginY];
258         }
259         else
260             rect.size.height = [self objectSizeToFit:view].height;
261         
262         // Get the width
263         if ([view autoresizingMask] & NSViewWidthSizable) {
264             CGFloat width = 0;
265             for (NSUInteger c = 0; c < colSpan; c++)
266                 width += [self widthOfColumn:col + c] + [self marginX];
267             rect.size.width = width - [self marginX];
268         }
269         else
270             rect.size.width = [self objectSizeToFit:view].width;
271         
272         // Top corner
273         rect.origin.y = [self topOfRow:row] + ([self heightOfRow:row] - rect.size.height) / 2;
274         rect.origin.x = [self leftOfColumn:col];
275         
276         [view setFrame:rect];
277         [view setNeedsDisplay:YES];
278     }
279 }
280
281 - (NSMutableDictionary *)objectForView:(NSView *)view
282 {
283     for (NSMutableDictionary *dict in _gridedViews)
284     {
285         if ([dict objectForKey:@"view"] == view)
286             return dict;
287     }
288     return nil;
289 }
290
291 - (void)addSubview:(NSView *)view atRow:(NSUInteger)row column:(NSUInteger)column rowSpan:(NSUInteger)rowSpan colSpan:(NSUInteger)colSpan
292 {
293     if (row + 1 > _rowCount)
294         _rowCount = row + 1;
295     if (column + 1 > _colCount)
296         _colCount = column + 1;
297     
298     if (!_gridedViews)
299         _gridedViews = [[NSMutableArray alloc] init];
300     
301     NSMutableDictionary *dict = [self objectForView:view];
302     if (!dict) {
303         dict = [NSMutableDictionary dictionary];
304         [dict setObject:view forKey:@"view"];
305         [_gridedViews addObject:dict];
306     }
307     [dict setObject:[NSNumber numberWithInt:rowSpan] forKey:@"rowSpan"];
308     [dict setObject:[NSNumber numberWithInt:colSpan] forKey:@"colSpan"];
309     [dict setObject:[NSNumber numberWithInt:row] forKey:@"row"];
310     [dict setObject:[NSNumber numberWithInt:column] forKey:@"col"];
311     
312     
313     [self addSubview:view];
314     [self relayout];
315     
316     // Recompute the size of the window after making sure we won't see anymore update
317     [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(recomputeWindowSize) object:nil];
318     [self performSelector:@selector(recomputeWindowSize) withObject:nil afterDelay:0.1];
319 }
320
321 - (void)removeSubview:(NSView *)view
322 {
323     NSDictionary *dict = [self objectForView:view];
324     if (dict)
325         [_gridedViews removeObject:dict];
326     [view removeFromSuperview];
327     
328     [self recomputeCount];
329     [self recomputeWindowSize];
330     
331     [self relayout];
332     [self setNeedsDisplay:YES];
333 }
334
335 - (void)setFrame:(NSRect)frameRect
336 {
337     [super setFrame:frameRect];
338     [self relayout];
339 }
340
341 - (NSSize)flexSize:(NSSize)size
342 {
343     if (!_rowCount || !_colCount)
344         return size;
345     
346     CGFloat minHeight = [self marginY];
347     BOOL canFlexHeight = NO;
348     for (NSUInteger i = 0; i < _rowCount; i++) {
349         CGFloat constrained = [self constrainedHeightOfRow:i];
350         if (!constrained) {
351             canFlexHeight = YES;
352             constrained = 128;
353         }
354         minHeight += constrained + [self marginY];
355     }
356     
357     CGFloat minWidth = [self marginX];
358     BOOL canFlexWidth = NO;
359     for (NSUInteger i = 0; i < _colCount; i++) {
360         CGFloat constrained = [self constrainedWidthOfColumn:i];
361         if (!constrained) {
362             canFlexWidth = YES;
363             constrained = 128;
364         }
365         minWidth += constrained + [self marginX];
366     }
367     if (size.width < minWidth)
368         size.width = minWidth;
369     if (size.height < minHeight)
370         size.height = minHeight;
371     if (!canFlexHeight)
372         size.height = minHeight;
373     if (!canFlexWidth)
374         size.width = minWidth;
375     return size;
376 }
377
378 @end