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.
450 lines
12 KiB
450 lines
12 KiB
/*****************************************************************************
|
|
* VLCStatusBarIcon.m: Mac OS X module for vlc
|
|
*****************************************************************************
|
|
* Copyright (C) 2016 VLC authors and VideoLAN
|
|
* $Id$
|
|
*
|
|
* Authors: Goran Dokic <vlc at 8hz dot com>
|
|
*
|
|
* 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 "VLCStatusBarIcon.h"
|
|
|
|
#import "MainMenu.h"
|
|
#import "intf.h"
|
|
|
|
#import <vlc_common.h>
|
|
#import <vlc_playlist.h>
|
|
#import <vlc_input.h>
|
|
#import <CoreInteraction.h>
|
|
#import <StringUtility.h>
|
|
|
|
#pragma mark -
|
|
#pragma mark Defines
|
|
|
|
#define playPauseMenuItemTag 74747
|
|
#define stopMenuItemTag 83838
|
|
#define randomMenuItemTag 63636
|
|
#define dataUpdateTimerInterval 1.0
|
|
#define NSInitialToolTipDelayIn_ms 20
|
|
// #define showURLInToolTip 1
|
|
|
|
@interface VLCStatusBarIcon ()
|
|
{
|
|
NSMenuItem *_vlcStatusBarMenuItem;
|
|
|
|
NSString *_nameToDisplay;
|
|
NSString *_timeToDisplay;
|
|
NSString *_durationToDisplay;
|
|
NSString *_urlToDisplay;
|
|
NSImage *_menuImagePlay;
|
|
NSImage *_menuImagePause;
|
|
NSImage *_menuImageStop;
|
|
}
|
|
@end
|
|
|
|
#pragma mark -
|
|
#pragma mark Implementation
|
|
|
|
@implementation VLCStatusBarIcon
|
|
|
|
#pragma mark -
|
|
#pragma mark Init
|
|
|
|
- (void)dealloc
|
|
{
|
|
// cleanup
|
|
[self.dataRefreshUpdateTimer invalidate];
|
|
self.dataRefreshUpdateTimer = nil;
|
|
}
|
|
|
|
- (void)awakeFromNib
|
|
{
|
|
[super awakeFromNib];
|
|
|
|
[self enableMenuIcon];
|
|
|
|
// add the top menu item for dynamic data
|
|
_vlcStatusBarMenuItem = [[NSMenuItem alloc] initWithTitle:@"URL/Path Options" action:@selector(updateMenuItemContent:) keyEquivalent:@""];
|
|
|
|
[_vlcStatusBarMenuItem setToolTip:@"Misc functions with media URL or Path"];
|
|
[_vlcStatusBarMenuItem setTarget:self];
|
|
|
|
[_vlcStatusBarIconMenu insertItem:_vlcStatusBarMenuItem atIndex:0];
|
|
|
|
// Set our selves up as delegate, to receive menuNeedsUpdate messages, so
|
|
// we can update our menu as needed/before it's drawn
|
|
[_vlcStatusBarIconMenu setDelegate:self];
|
|
|
|
// Disable custom menu item initially
|
|
// needs to be done with validateMenuItem (see below)
|
|
[_vlcStatusBarMenuItem setEnabled:NO];
|
|
|
|
// Increase toolTip speed, improves status usability
|
|
// Tweak delay above, with '#define NSInitialToolTipDelayIn_ms x'
|
|
|
|
[[NSUserDefaults standardUserDefaults] setObject: [NSNumber numberWithInt: NSInitialToolTipDelayIn_ms] forKey: @"NSInitialToolTipDelay"];
|
|
|
|
// init _urlToDisplay
|
|
_urlToDisplay = nil;
|
|
|
|
// Load the menu icons
|
|
_menuImagePlay = [NSImage imageNamed:@"playIcon"];
|
|
_menuImagePause = [NSImage imageNamed:@"pauseIcon"];
|
|
|
|
_menuImageStop = [NSImage imageNamed:@"stopIcon"];
|
|
NSMenuItem *menuItemToChange = [_vlcStatusBarIconMenu itemWithTag:stopMenuItemTag];
|
|
[menuItemToChange setImage:_menuImageStop];
|
|
|
|
// I'd rather not use a timer and only update when mouse comes near
|
|
// status icon in bar. But one can't tell without evil sourcery :(
|
|
// Tweak update frequency above (#define)
|
|
|
|
[self setDataUpdateTimer:dataUpdateTimerInterval];
|
|
}
|
|
|
|
|
|
#pragma mark -
|
|
#pragma mark Various callback functions
|
|
|
|
//---
|
|
// Menu delegate/callback for cocoa - called before menu is opened/displayed
|
|
// fire off menu item updates (dynamic item 0, play/pause, random)
|
|
- (void)menuNeedsUpdate:(NSMenu *)menu
|
|
{
|
|
// update dynamic menu 'item 0' (follows data gathered by timer handler)
|
|
[self updateDynamicMenuItemText];
|
|
|
|
// update play/pause status in status bar menu
|
|
[self updateMenuItemPlayPause];
|
|
|
|
// update random status in status bar menu
|
|
[self updateMenuItemRandom];
|
|
}
|
|
|
|
|
|
//---
|
|
// Make sure we can enable/disable menu items (in our case index 0)
|
|
// override class method. Called every time before menu is drawn.
|
|
- (BOOL)validateMenuItem:(NSMenuItem *)menuItem
|
|
{
|
|
// disable the URL/Path options menu if there is no meaningful data
|
|
if ((menuItem.action == @selector(updateMenuItemContent:)) && (!_urlToDisplay)) {
|
|
return NO;
|
|
}
|
|
return YES;
|
|
}
|
|
|
|
|
|
//---
|
|
// callback for tooltip update timer
|
|
//
|
|
- (void)dataRefreshTimeHandler:(NSTimer *)timer
|
|
{
|
|
[self gatherDataToDisplay];
|
|
[self updateToolTipText];
|
|
}
|
|
|
|
|
|
#pragma mark -
|
|
#pragma mark Various functions
|
|
|
|
|
|
//---
|
|
// enables menu icon/status item, sets proporties like image, attach menu
|
|
//
|
|
- (void)enableMenuIcon
|
|
{
|
|
_statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
|
|
[_statusItem setHighlightMode:YES];
|
|
[_statusItem setEnabled:YES];
|
|
[_statusItem setTarget:self];
|
|
|
|
NSImage *menuIcon;
|
|
// set black/white icon (or color, like originally)
|
|
menuIcon = [NSImage imageNamed:@"statusBarIcon"];
|
|
// Make sure the b/w statusItem icon is inverted on dark/light mode
|
|
menuIcon.template = YES;
|
|
[_statusItem setImage:menuIcon];
|
|
|
|
// Attach pull-down menu
|
|
[_statusItem setMenu:_vlcStatusBarIconMenu];
|
|
}
|
|
|
|
|
|
//---
|
|
// Make sure data is fresh, before displaying
|
|
//
|
|
- (void)gatherDataToDisplay
|
|
{
|
|
mtime_t pos;
|
|
|
|
// get name of current item - clear first!
|
|
_nameToDisplay = nil;
|
|
_nameToDisplay = [[VLCCoreInteraction sharedInstance] nameOfCurrentPlaylistItem];
|
|
|
|
// If status is 'stopped' there is no useful data
|
|
// Otherwise could have used 'if (_nameToDisplay == nil)'
|
|
|
|
if ([self vlcPlayingStatus] == PLAYLIST_STOPPED) {
|
|
_urlToDisplay = nil;
|
|
} else {
|
|
input_thread_t * p_input;
|
|
p_input = pl_CurrentInput(getIntf());
|
|
if (p_input) {
|
|
pos = var_GetInteger(p_input, "time") / CLOCK_FREQ;
|
|
vlc_object_release(p_input); // must release or get segfault on quit
|
|
}
|
|
|
|
// update our time counter
|
|
_timeToDisplay = [[VLCStringUtility sharedInstance] stringForTime:(long long) pos];
|
|
|
|
// get the duration (if it's there)
|
|
int duration = [[VLCCoreInteraction sharedInstance] durationOfCurrentPlaylistItem];
|
|
_durationToDisplay = [[VLCStringUtility sharedInstance] stringForTime:(long long) duration];
|
|
|
|
// update the playing item's URL/Path
|
|
_urlToDisplay = [[[VLCCoreInteraction sharedInstance] URLOfCurrentPlaylistItem] absoluteString];
|
|
}
|
|
}
|
|
|
|
|
|
//---
|
|
// Call for periodic updates of tooltip text
|
|
//
|
|
- (void)updateToolTipText
|
|
{
|
|
NSString *toolTipText = nil;
|
|
|
|
// craft the multiline string, for the tooltip, depending on play status
|
|
|
|
if ([self vlcPlayingStatus] == PLAYLIST_STOPPED) {
|
|
// nothing playing
|
|
toolTipText = @"VLC media player\nNothing playing";
|
|
} else {
|
|
#ifdef showURLInToolTip
|
|
toolTipText = [NSString stringWithFormat:@"VLC media player\nName: %@\nDuration: %@\nTime: %@\nURL/Path: %@", _nameToDisplay, _durationToDisplay, _timeToDisplay, _urlToDisplay];
|
|
#else
|
|
toolTipText = [NSString stringWithFormat:@"VLC media player\nName: %@\nDuration: %@\nTime: %@", _nameToDisplay, _durationToDisplay, _timeToDisplay];
|
|
#endif
|
|
}
|
|
|
|
[_statusItem setToolTip:toolTipText];
|
|
}
|
|
|
|
|
|
|
|
//---
|
|
// Call for updating of dynamic menu item
|
|
//
|
|
- (void)updateDynamicMenuItemText
|
|
{
|
|
NSString *menuString = nil;
|
|
|
|
// create string for dynamic menu bit (sync?)
|
|
if ([self vlcPlayingStatus] == PLAYLIST_STOPPED) {
|
|
// put back our disabled menu item text.
|
|
menuString = @"URL/Path Options";
|
|
} else {
|
|
if ([_urlToDisplay hasPrefix:@"file://"]) {
|
|
// offer to show 'file://' in finder
|
|
menuString = [NSString stringWithFormat:@"Select File In Finder"];
|
|
} else {
|
|
// offer to copy URL to clipboard
|
|
menuString = [NSString stringWithFormat:@"Copy URL to clipboard"];
|
|
}
|
|
}
|
|
|
|
[_vlcStatusBarMenuItem setTitle:menuString];
|
|
}
|
|
|
|
|
|
//---
|
|
// set timer for tooltips updates and flee
|
|
//
|
|
- (void)setDataUpdateTimer:(float)interval
|
|
{
|
|
self.dataRefreshUpdateTimer = [NSTimer scheduledTimerWithTimeInterval:interval
|
|
target:self
|
|
selector:@selector(dataRefreshTimeHandler:)
|
|
userInfo:nil
|
|
repeats:YES];
|
|
}
|
|
|
|
|
|
//---
|
|
//
|
|
//
|
|
- (void)updateMenuItemRandom
|
|
{
|
|
// get current random status
|
|
bool b_value;
|
|
playlist_t *p_playlist = pl_Get(getIntf());
|
|
b_value = var_GetBool(p_playlist, "random");
|
|
|
|
// get menuitem 'Random'
|
|
NSMenuItem* menuItemToChange = [_vlcStatusBarIconMenu itemWithTag:randomMenuItemTag];
|
|
if (b_value) {
|
|
[menuItemToChange setState:NSOnState];
|
|
} else {
|
|
[menuItemToChange setState:NSOffState];
|
|
}
|
|
}
|
|
|
|
|
|
//---
|
|
//
|
|
//
|
|
- (void)updateMenuItemPlayPause
|
|
{
|
|
NSMenuItem* menuItemToChange = [_vlcStatusBarIconMenu itemWithTag:playPauseMenuItemTag];
|
|
|
|
if ([self vlcPlayingStatus] == PLAYLIST_RUNNING) {
|
|
[menuItemToChange setTitle:@"Pause"];
|
|
[menuItemToChange setImage:_menuImagePause];
|
|
} else {
|
|
[menuItemToChange setTitle:@"Play"];
|
|
[menuItemToChange setImage:_menuImagePlay];
|
|
}
|
|
}
|
|
|
|
|
|
#pragma mark -
|
|
#pragma mark Utility functions
|
|
|
|
//---
|
|
// Returns VLC playlist status
|
|
// Check for: constants PLAYLIST_RUNNING, PLAYLIST_STOPPED, PLAYLIST_PAUSED.
|
|
- (int)vlcPlayingStatus
|
|
{
|
|
int res;
|
|
// get the playlist 'playing' status
|
|
playlist_t *p_playlist = pl_Get(getIntf());
|
|
|
|
PL_LOCK;
|
|
res = playlist_Status( p_playlist );
|
|
PL_UNLOCK;
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
//---
|
|
// Returns true if playing, false in all other cases.
|
|
//
|
|
- (BOOL)isVLCPlaying
|
|
{
|
|
bool vlcPlaying = false;
|
|
|
|
// get the playlist 'playing' status
|
|
playlist_t *p_playlist = pl_Get(getIntf());
|
|
|
|
PL_LOCK;
|
|
if (playlist_Status( p_playlist ) == PLAYLIST_RUNNING) {
|
|
vlcPlaying = true;
|
|
}
|
|
PL_UNLOCK;
|
|
|
|
return vlcPlaying;
|
|
}
|
|
|
|
|
|
|
|
#pragma mark -
|
|
#pragma mark Menu item Actions
|
|
|
|
//-- action for dynamic menu index 0
|
|
|
|
- (IBAction) updateMenuItemContent:(id)sender
|
|
{
|
|
// Here we offer to copy the url to the clipboard or
|
|
// select/show a local file in the finder..(useful imo ;-)
|
|
|
|
if ([self vlcPlayingStatus] != PLAYLIST_STOPPED) {
|
|
if ([_urlToDisplay hasPrefix:@"file://"]) {
|
|
// show local file in finder
|
|
NSString *path=[_urlToDisplay substringFromIndex:7];
|
|
[[NSWorkspace sharedWorkspace] selectFile:path inFileViewerRootedAtPath:path];
|
|
} else {
|
|
// copy remote URL to clipboard
|
|
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
|
|
[pasteboard clearContents];
|
|
[pasteboard setString:_urlToDisplay forType:NSStringPboardType];
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-- action for 'main window'
|
|
|
|
- (IBAction)restoreMainWindow:(id)sender
|
|
{
|
|
// force our window to go to front (huzzah) and restore window
|
|
[[VLCApplication sharedApplication] activateIgnoringOtherApps:YES];
|
|
[[[VLCMain sharedInstance] mainWindow] makeKeyAndOrderFront:sender];
|
|
}
|
|
|
|
|
|
//-- action for 'toggle play/pause'
|
|
|
|
- (IBAction)statusBarIconTogglePlayPause:(id)sender
|
|
{
|
|
[[VLCCoreInteraction sharedInstance] playOrPause];
|
|
}
|
|
|
|
|
|
//-- action for 'stop'
|
|
|
|
- (IBAction)statusBarIconStop:(id)sender
|
|
{
|
|
[[VLCCoreInteraction sharedInstance] stop];
|
|
}
|
|
|
|
|
|
//-- action for 'Next track'
|
|
|
|
- (IBAction)statusBarIconNext:(id)sender
|
|
{
|
|
[[VLCCoreInteraction sharedInstance] next];
|
|
}
|
|
|
|
|
|
//-- action for 'previous track'
|
|
|
|
- (IBAction)statusBarIconPrevious:(id)sender
|
|
{
|
|
[[VLCCoreInteraction sharedInstance] previous];
|
|
}
|
|
|
|
|
|
//-- action to actually 'toggle VLC randomize playorder status'
|
|
|
|
- (IBAction)statusBarIconToggleRandom:(id)sender
|
|
{
|
|
[[VLCCoreInteraction sharedInstance] shuffle];
|
|
}
|
|
|
|
|
|
//-- action voor 'quit'
|
|
|
|
- (IBAction)quitAction:(id)sender
|
|
{
|
|
// clean timer, quit
|
|
[self.dataRefreshUpdateTimer invalidate];
|
|
[[NSApplication sharedApplication] terminate:nil];
|
|
}
|
|
|
|
@end
|
|
|