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