macosx: Remove no longer required TimeLineSlider subclass
[vlc.git] / modules / gui / macosx / misc.m
1 /*****************************************************************************
2  * misc.m: code not specific to vlc
3  *****************************************************************************
4  * Copyright (C) 2003-2015 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
8  *          Felix Paul Kühne <fkuehne at videolan dot org>
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 "misc.h"
27 #import "VLCMain.h"                                          /* VLCApplication */
28 #import "VLCMainWindow.h"
29 #import "VLCMainMenu.h"
30 #import "VLCControlsBarCommon.h"
31 #import "VLCCoreInteraction.h"
32 #import <vlc_keys.h>
33
34 /*****************************************************************************
35  * VLCDragDropView
36  *****************************************************************************/
37
38 @implementation VLCDropDisabledImageView
39
40 - (void)awakeFromNib
41 {
42     [self unregisterDraggedTypes];
43 }
44
45 @end
46
47 /*****************************************************************************
48  * VLCDragDropView
49  *****************************************************************************/
50
51 @interface VLCDragDropView()
52 {
53     bool b_activeDragAndDrop;
54 }
55 @end
56
57 @implementation VLCDragDropView
58
59 - (id)initWithFrame:(NSRect)frame
60 {
61     self = [super initWithFrame:frame];
62     if (self) {
63         // default value
64         [self setDrawBorder:YES];
65     }
66
67     return self;
68 }
69
70 - (void)enablePlaylistItems
71 {
72     [self registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, @"VLCPlaylistItemPboardType", nil]];
73 }
74
75 - (BOOL)mouseDownCanMoveWindow
76 {
77     return YES;
78 }
79
80 - (void)dealloc
81 {
82     [self unregisterDraggedTypes];
83 }
84
85 - (void)awakeFromNib
86 {
87     [self registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
88 }
89
90 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
91 {
92     if ((NSDragOperationGeneric & [sender draggingSourceOperationMask]) == NSDragOperationGeneric) {
93         b_activeDragAndDrop = YES;
94         [self setNeedsDisplay:YES];
95
96         return NSDragOperationCopy;
97     }
98
99     return NSDragOperationNone;
100 }
101
102 - (void)draggingEnded:(id < NSDraggingInfo >)sender
103 {
104     b_activeDragAndDrop = NO;
105     [self setNeedsDisplay:YES];
106 }
107
108 - (void)draggingExited:(id < NSDraggingInfo >)sender
109 {
110     b_activeDragAndDrop = NO;
111     [self setNeedsDisplay:YES];
112 }
113
114 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
115 {
116     return YES;
117 }
118
119 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
120 {
121     BOOL b_returned;
122
123     if (_dropHandler && [_dropHandler respondsToSelector:@selector(performDragOperation:)])
124         b_returned = [_dropHandler performDragOperation:sender];
125     else // default
126         b_returned = [[VLCCoreInteraction sharedInstance] performDragOperation:sender];
127
128     [self setNeedsDisplay:YES];
129     return b_returned;
130 }
131
132 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
133 {
134     [self setNeedsDisplay:YES];
135 }
136
137 - (void)drawRect:(NSRect)dirtyRect
138 {
139     if ([self drawBorder] && b_activeDragAndDrop) {
140         NSRect frameRect = [self bounds];
141
142         [[NSColor selectedControlColor] set];
143         NSFrameRectWithWidthUsingOperation(frameRect, 2., NSCompositeSourceOver);
144     }
145
146     [super drawRect:dirtyRect];
147 }
148
149 @end
150
151
152 /*****************************************************************************
153  * MPSlider
154  *****************************************************************************/
155 @implementation MPSlider
156
157 void _drawKnobInRect(NSRect knobRect)
158 {
159     // Center knob in given rect
160     knobRect.origin.x += (int)((float)(knobRect.size.width - 7)/2.0);
161     knobRect.origin.y += (int)((float)(knobRect.size.height - 7)/2.0);
162
163     // Draw diamond
164     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 6, 1, 1), NSCompositeSourceOver);
165     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 5, 3, 1), NSCompositeSourceOver);
166     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 4, 5, 1), NSCompositeSourceOver);
167     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 0, knobRect.origin.y + 3, 7, 1), NSCompositeSourceOver);
168     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 2, 5, 1), NSCompositeSourceOver);
169     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 1, 3, 1), NSCompositeSourceOver);
170     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 0, 1, 1), NSCompositeSourceOver);
171 }
172
173 void _drawFrameInRect(NSRect frameRect)
174 {
175     // Draw frame
176     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width, 1), NSCompositeSourceOver);
177     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y + frameRect.size.height-1, frameRect.size.width, 1), NSCompositeSourceOver);
178     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
179     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x+frameRect.size.width-1, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
180 }
181
182 - (void)drawRect:(NSRect)rect
183 {
184     // Draw default to make sure the slider behaves correctly
185     [[NSGraphicsContext currentContext] saveGraphicsState];
186     NSRectClip(NSZeroRect);
187     [super drawRect:rect];
188     [[NSGraphicsContext currentContext] restoreGraphicsState];
189
190     // Full size
191     rect = [self bounds];
192     int diff = (int)(([[self cell] knobThickness] - 7.0)/2.0) - 1;
193     rect.origin.x += diff-1;
194     rect.origin.y += diff;
195     rect.size.width -= 2*diff-2;
196     rect.size.height -= 2*diff;
197
198     // Draw dark
199     NSRect knobRect = [[self cell] knobRectFlipped:NO];
200     [[[NSColor blackColor] colorWithAlphaComponent:0.6] set];
201     _drawFrameInRect(rect);
202     _drawKnobInRect(knobRect);
203
204     // Draw shadow
205     [[[NSColor blackColor] colorWithAlphaComponent:0.1] set];
206     rect.origin.x++;
207     rect.origin.y++;
208     knobRect.origin.x++;
209     knobRect.origin.y++;
210     _drawFrameInRect(rect);
211     _drawKnobInRect(knobRect);
212 }
213
214 @end
215
216 /*****************************************************************************
217  * ProgressView
218  *****************************************************************************/
219
220 @implementation VLCProgressView : NSView
221
222 - (void)scrollWheel:(NSEvent *)o_event
223 {
224     BOOL b_forward = NO;
225     CGFloat f_deltaY = [o_event deltaY];
226     CGFloat f_deltaX = [o_event deltaX];
227
228     if ([o_event isDirectionInvertedFromDevice])
229         f_deltaX = -f_deltaX; // optimisation, actually double invertion of f_deltaY here
230     else
231         f_deltaY = -f_deltaY;
232
233     // positive for left / down, negative otherwise
234     CGFloat f_delta = f_deltaX + f_deltaY;
235     CGFloat f_abs;
236     int i_vlckey;
237
238     if (f_delta > 0.0f)
239         f_abs = f_delta;
240     else {
241         b_forward = YES;
242         f_abs = -f_delta;
243     }
244
245     for (NSUInteger i = 0; i < (int)(f_abs/4.+1.) && f_abs > 0.05 ; i++) {
246         if (b_forward)
247             [[VLCCoreInteraction sharedInstance] forwardExtraShort];
248         else
249             [[VLCCoreInteraction sharedInstance] backwardExtraShort];
250     }
251 }
252
253 - (BOOL)acceptsFirstResponder
254 {
255     return YES;
256 }
257
258 @end
259
260 /*****************************************************************************
261  * VLCVolumeSliderCommon
262  *****************************************************************************/
263
264 @implementation VLCVolumeSliderCommon : NSSlider
265
266 - (void)scrollWheel:(NSEvent *)o_event
267 {
268     BOOL b_up = NO;
269     CGFloat f_deltaY = [o_event deltaY];
270     CGFloat f_deltaX = [o_event deltaX];
271
272     if ([o_event isDirectionInvertedFromDevice])
273         f_deltaX = -f_deltaX; // optimisation, actually double invertion of f_deltaY here
274     else
275         f_deltaY = -f_deltaY;
276
277     // positive for left / down, negative otherwise
278     CGFloat f_delta = f_deltaX + f_deltaY;
279     CGFloat f_abs;
280
281     if (f_delta > 0.0f)
282         f_abs = f_delta;
283     else {
284         b_up = YES;
285         f_abs = -f_delta;
286     }
287
288     for (NSUInteger i = 0; i < (int)(f_abs/4.+1.) && f_abs > 0.05 ; i++) {
289         if (b_up)
290             [[VLCCoreInteraction sharedInstance] volumeUp];
291         else
292             [[VLCCoreInteraction sharedInstance] volumeDown];
293     }
294 }
295
296 - (void)drawFullVolumeMarker
297 {
298     CGFloat maxAudioVol = self.maxValue / AOUT_VOLUME_DEFAULT;
299     if (maxAudioVol < 1.)
300         return;
301
302     NSColor *drawingColor;
303     // for bright artwork, a black color is used and vice versa
304     if (_usesBrightArtwork)
305         drawingColor = [[NSColor blackColor] colorWithAlphaComponent:.4];
306     else
307         drawingColor = [[NSColor whiteColor] colorWithAlphaComponent:.4];
308
309     NSBezierPath* bezierPath = [NSBezierPath bezierPath];
310     [self drawFullVolBezierPath:bezierPath];
311     [bezierPath closePath];
312
313     bezierPath.lineWidth = 1.;
314     [drawingColor setStroke];
315     [bezierPath stroke];
316 }
317
318 - (CGFloat)fullVolumePos
319 {
320     CGFloat maxAudioVol = self.maxValue / AOUT_VOLUME_DEFAULT;
321     CGFloat sliderRange = [self frame].size.width - [self knobThickness];
322     CGFloat sliderOrigin = [self knobThickness] / 2.;
323
324     return 1. / maxAudioVol * sliderRange + sliderOrigin;
325 }
326
327 - (void)drawFullVolBezierPath:(NSBezierPath*)bezierPath
328 {
329     CGFloat fullVolPos = [self fullVolumePos];
330     [bezierPath moveToPoint:NSMakePoint(fullVolPos, [self frame].size.height - 3.)];
331     [bezierPath lineToPoint:NSMakePoint(fullVolPos, 2.)];
332 }
333
334 @end
335
336 @implementation VolumeSliderCell
337
338 - (BOOL)continueTracking:(NSPoint)lastPoint at:(NSPoint)currentPoint inView:(NSView *)controlView
339 {
340     VLCVolumeSliderCommon *o_slider = (VLCVolumeSliderCommon *)controlView;
341     CGFloat fullVolumePos = [o_slider fullVolumePos] + 2.;
342
343     CGPoint snapToPoint = currentPoint;
344     if (ABS(fullVolumePos - currentPoint.x) <= 4.)
345         snapToPoint.x = fullVolumePos;
346
347     return [super continueTracking:lastPoint at:snapToPoint inView:controlView];
348 }
349
350 @end
351
352 /*****************************************************************************
353  * ITSlider
354  *****************************************************************************/
355
356 @interface ITSlider()
357 {
358     NSImage *img;
359     NSRect image_rect;
360 }
361 @end
362
363 @implementation ITSlider
364
365 - (void)awakeFromNib
366 {
367     BOOL b_dark = config_GetInt( getIntf(), "macosx-interfacestyle" );
368     if (b_dark)
369         img = imageFromRes(@"volume-slider-knob_dark");
370     else
371         img = imageFromRes(@"volume-slider-knob");
372
373     image_rect.size = [img size];
374     image_rect.origin.x = 0;
375
376     if (b_dark)
377         image_rect.origin.y = -1;
378     else
379         image_rect.origin.y = 0;
380 }
381
382 - (void)drawKnobInRect:(NSRect)knobRect
383 {
384     knobRect.origin.x += (knobRect.size.width - image_rect.size.width) / 2;
385     knobRect.size.width = image_rect.size.width;
386     knobRect.size.height = image_rect.size.height;
387     [img drawInRect:knobRect fromRect:image_rect operation:NSCompositeSourceOver fraction:1];
388 }
389
390 - (void)drawRect:(NSRect)rect
391 {
392     /* Draw default to make sure the slider behaves correctly */
393     [[NSGraphicsContext currentContext] saveGraphicsState];
394     NSRectClip(NSZeroRect);
395     [super drawRect:rect];
396     [[NSGraphicsContext currentContext] restoreGraphicsState];
397
398     [self drawFullVolumeMarker];
399
400     NSRect knobRect = [[self cell] knobRectFlipped:NO];
401     knobRect.origin.y+=2;
402     [self drawKnobInRect: knobRect];
403 }
404
405 @end
406
407 /*****************************************************************************
408  * VLCMainWindowSplitView implementation
409  * comment 1 + 2 taken from NSSplitView.h (10.7 SDK)
410  *****************************************************************************/
411 @implementation VLCMainWindowSplitView : NSSplitView
412 /* Return the color of the dividers that the split view is drawing between subviews. The default implementation of this method returns [NSColor clearColor] for the thick divider style. It will also return [NSColor clearColor] for the thin divider style when the split view is in a textured window. All other thin dividers are drawn with a color that looks good between two white panes. You can override this method to change the color of dividers.
413  */
414 - (NSColor *)dividerColor
415 {
416     return [NSColor colorWithCalibratedRed:.60 green:.60 blue:.60 alpha:1.];
417 }
418
419 /* Return the thickness of the dividers that the split view is drawing between subviews. The default implementation returns a value that depends on the divider style. You can override this method to change the size of dividers.
420  */
421 - (CGFloat)dividerThickness
422 {
423     return 1.0;
424 }
425 @end
426
427 /*****************************************************************************
428  * VLCThreePartImageView interface
429  *****************************************************************************/
430
431 @interface VLCThreePartImageView()
432 {
433     NSImage *_left_img;
434     NSImage *_middle_img;
435     NSImage *_right_img;
436 }
437 @end
438
439 @implementation VLCThreePartImageView
440
441 - (void)setImagesLeft:(NSImage *)left middle: (NSImage *)middle right:(NSImage *)right
442 {
443     _left_img = left;
444     _middle_img = middle;
445     _right_img = right;
446 }
447
448 - (void)drawRect:(NSRect)rect
449 {
450     NSRect bnds = [self bounds];
451     NSDrawThreePartImage( bnds, _left_img, _middle_img, _right_img, NO, NSCompositeSourceOver, 1, NO );
452 }
453
454 @end
455
456 @interface PositionFormatter()
457 {
458     NSCharacterSet *o_forbidden_characters;
459 }
460 @end
461
462 @implementation PositionFormatter
463
464 - (id)init
465 {
466     self = [super init];
467     NSMutableCharacterSet *nonNumbers = [[[NSCharacterSet decimalDigitCharacterSet] invertedSet] mutableCopy];
468     [nonNumbers removeCharactersInString:@"-:"];
469     o_forbidden_characters = [nonNumbers copy];
470
471     return self;
472 }
473
474 - (NSString*)stringForObjectValue:(id)obj
475 {
476     if([obj isKindOfClass:[NSString class]])
477         return obj;
478     if([obj isKindOfClass:[NSNumber class]])
479         return [obj stringValue];
480
481     return nil;
482 }
483
484 - (BOOL)getObjectValue:(id*)obj forString:(NSString*)string errorDescription:(NSString**)error
485 {
486     *obj = [string copy];
487     return YES;
488 }
489
490 - (BOOL)isPartialStringValid:(NSString*)partialString newEditingString:(NSString**)newString errorDescription:(NSString**)error
491 {
492     if ([partialString rangeOfCharacterFromSet:o_forbidden_characters options:NSLiteralSearch].location != NSNotFound) {
493         return NO;
494     } else {
495         return YES;
496     }
497 }
498
499 @end
500
501 @implementation NSView (EnableSubviews)
502
503 - (void)enableSubviews:(BOOL)b_enable
504 {
505     for (NSView *o_view in [self subviews]) {
506         [o_view enableSubviews:b_enable];
507
508         // enable NSControl
509         if ([o_view respondsToSelector:@selector(setEnabled:)]) {
510             [(NSControl *)o_view setEnabled:b_enable];
511         }
512         // also "enable / disable" text views
513         if ([o_view respondsToSelector:@selector(setTextColor:)]) {
514             if (b_enable == NO) {
515                 [(NSTextField *)o_view setTextColor:[NSColor disabledControlTextColor]];
516             } else {
517                 [(NSTextField *)o_view setTextColor:[NSColor controlTextColor]];
518             }
519         }
520
521     }
522 }
523
524 @end
525
526 /*****************************************************************************
527  * VLCByteCountFormatter addition
528  *****************************************************************************/
529
530 @implementation VLCByteCountFormatter
531
532 + (NSString *)stringFromByteCount:(long long)byteCount countStyle:(NSByteCountFormatterCountStyle)countStyle
533 {
534     // Use native implementation on >= mountain lion
535     Class byteFormatterClass = NSClassFromString(@"NSByteCountFormatter");
536     if (byteFormatterClass && [byteFormatterClass respondsToSelector:@selector(stringFromByteCount:countStyle:)]) {
537         return [byteFormatterClass stringFromByteCount:byteCount countStyle:NSByteCountFormatterCountStyleFile];
538     }
539
540     float devider = 0.;
541     float returnValue = 0.;
542     NSString *suffix;
543
544     NSNumberFormatter *theFormatter = [[NSNumberFormatter alloc] init];
545     [theFormatter setLocale:[NSLocale currentLocale]];
546     [theFormatter setAllowsFloats:YES];
547
548     NSString *returnString = @"";
549
550     if (countStyle != NSByteCountFormatterCountStyleDecimal)
551         devider = 1024.;
552     else
553         devider = 1000.;
554
555     if (byteCount < 1000) {
556         returnValue = byteCount;
557         suffix = _NS("B");
558         [theFormatter setMaximumFractionDigits:0];
559         goto end;
560     }
561
562     if (byteCount < 1000000) {
563         returnValue = byteCount / devider;
564         suffix = _NS("KB");
565         [theFormatter setMaximumFractionDigits:0];
566         goto end;
567     }
568
569     if (byteCount < 1000000000) {
570         returnValue = byteCount / devider / devider;
571         suffix = _NS("MB");
572         [theFormatter setMaximumFractionDigits:1];
573         goto end;
574     }
575
576     [theFormatter setMaximumFractionDigits:2];
577     if (byteCount < 1000000000000) {
578         returnValue = byteCount / devider / devider / devider;
579         suffix = _NS("GB");
580         goto end;
581     }
582
583     returnValue = byteCount / devider / devider / devider / devider;
584     suffix = _NS("TB");
585
586 end:
587     returnString = [NSString stringWithFormat:@"%@ %@", [theFormatter stringFromNumber:[NSNumber numberWithFloat:returnValue]], suffix];
588
589     return returnString;
590 }
591
592 @end