From 6d7fcdd2b544fa4eb29988bd627af94aa8238d6c Mon Sep 17 00:00:00 2001 From: Thierry Crozat Date: Sun, 26 Jan 2014 20:40:36 +0000 Subject: OSX: Implement TaskbarManager for Mac OS X This implements count badge, progress bar, and icon overlay. It uses the NSDockTile API which is available since OS X 10.5. The code compiles and run on older system but without doing anything. --- backends/module.mk | 3 +- backends/platform/sdl/macosx/macosx.cpp | 11 ++ backends/platform/sdl/macosx/macosx.h | 1 + backends/taskbar/macosx/macosx-taskbar.h | 57 +++++++ backends/taskbar/macosx/macosx-taskbar.mm | 238 ++++++++++++++++++++++++++++++ 5 files changed, 309 insertions(+), 1 deletion(-) create mode 100644 backends/taskbar/macosx/macosx-taskbar.h create mode 100644 backends/taskbar/macosx/macosx-taskbar.mm (limited to 'backends') diff --git a/backends/module.mk b/backends/module.mk index 1222d9a363..34e2928419 100644 --- a/backends/module.mk +++ b/backends/module.mk @@ -97,7 +97,8 @@ ifdef MACOSX MODULE_OBJS += \ midi/coreaudio.o \ midi/coremidi.o \ - updates/macosx/macosx-updates.o + updates/macosx/macosx-updates.o \ + taskbar/macosx/macosx-taskbar.o endif ifdef WIN32 diff --git a/backends/platform/sdl/macosx/macosx.cpp b/backends/platform/sdl/macosx/macosx.cpp index 301dc44b7b..924e33b6e3 100644 --- a/backends/platform/sdl/macosx/macosx.cpp +++ b/backends/platform/sdl/macosx/macosx.cpp @@ -31,6 +31,7 @@ #include "backends/mixer/doublebuffersdl/doublebuffersdl-mixer.h" #include "backends/platform/sdl/macosx/appmenu_osx.h" #include "backends/updates/macosx/macosx-updates.h" +#include "backends/taskbar/macosx/macosx-taskbar.h" #include "common/archive.h" #include "common/config-manager.h" @@ -45,6 +46,16 @@ OSystem_MacOSX::OSystem_MacOSX() OSystem_POSIX("Library/Preferences/ScummVM Preferences") { } +void OSystem_MacOSX::init() { +#if defined(USE_TASKBAR) + // Initialize taskbar manager + _taskbarManager = new MacOSXTaskbarManager(); +#endif + + // Invoke parent implementation of this method + OSystem_POSIX::init(); +} + void OSystem_MacOSX::initBackend() { // Create the mixer manager if (_mixer == 0) { diff --git a/backends/platform/sdl/macosx/macosx.h b/backends/platform/sdl/macosx/macosx.h index e5a72bf393..50cef60353 100644 --- a/backends/platform/sdl/macosx/macosx.h +++ b/backends/platform/sdl/macosx/macosx.h @@ -35,6 +35,7 @@ public: virtual Common::String getSystemLanguage() const; + virtual void init(); virtual void initBackend(); virtual void addSysArchivesToSearchSet(Common::SearchSet &s, int priority = 0); virtual void setupIcon(); diff --git a/backends/taskbar/macosx/macosx-taskbar.h b/backends/taskbar/macosx/macosx-taskbar.h new file mode 100644 index 0000000000..5d5b9d02cd --- /dev/null +++ b/backends/taskbar/macosx/macosx-taskbar.h @@ -0,0 +1,57 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * 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. + * + */ + +#ifndef BACKEND_MACOSX_TASKBAR_H +#define BACKEND_MACOSX_TASKBAR_H + +#if defined(MACOSX) && defined(USE_TASKBAR) + +#include "common/str.h" +#include "common/taskbar.h" + +class MacOSXTaskbarManager : public Common::TaskbarManager { +public: + MacOSXTaskbarManager(); + virtual ~MacOSXTaskbarManager(); + + virtual void setOverlayIcon(const Common::String &name, const Common::String &description); + virtual void setProgressValue(int completed, int total); + virtual void setProgressState(TaskbarProgressState state); + virtual void setCount(int count); + virtual void notifyError(); + virtual void clearError(); + +private: + Common::String getIconPath(const Common::String&); + + void initApplicationIconView(); + void clearApplicationIconView(); + + void initOverlayIconView(); + void clearOverlayIconView(); + + double _progress; +}; + +#endif + +#endif // BACKEND_MACOSX_TASKBAR_H diff --git a/backends/taskbar/macosx/macosx-taskbar.mm b/backends/taskbar/macosx/macosx-taskbar.mm new file mode 100644 index 0000000000..ae087dfb85 --- /dev/null +++ b/backends/taskbar/macosx/macosx-taskbar.mm @@ -0,0 +1,238 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * 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. + * + */ + +// Disable symbol overrides so that we can use system headers +#define FORBIDDEN_SYMBOL_ALLOW_ALL +#include "common/scummsys.h" + +#if defined(MACOSX) && defined(USE_TASKBAR) + +// NSDockTile was introduced with Mac OS X 10.5. +// Try provide backward compatibility by avoiding NSDockTile symbols. + +// TODO: Implement recent list, maybe as a custom menu on dock tile when app is not running +// See Dock Tile plug-in at https://developer.apple.com/library/mac/documentation/Carbon/Conceptual/customizing_docktile/CreatingaDockTilePlug-in/CreatingaDockTilePlug-in.html + +#include "backends/taskbar/macosx/macosx-taskbar.h" +#include "common/config-manager.h" +#include "common/file.h" + +#include +#include +#include +#include +#include +#include +#include + +id _dockTile; +NSImageView *_applicationIconView; +NSImageView *_overlayIconView; + +// Using a NSProgressIndicator as a sub-view of the NSDockTile view does not work properly. +// The progress indicator is grayed out and display no progress. So instead the bar is drawn +// manually, which is a bit more work :( + +MacOSXTaskbarManager::MacOSXTaskbarManager() : _progress(-1.0) { + if ([NSApp respondsToSelector:@selector(dockTile)]) + _dockTile = [NSApp dockTile]; + _applicationIconView = nil; + _overlayIconView = nil; +} + +MacOSXTaskbarManager::~MacOSXTaskbarManager() { + clearApplicationIconView(); +} + +void MacOSXTaskbarManager::initApplicationIconView() { + if (_dockTile == nil) + return; + if (_applicationIconView == nil) { + _applicationIconView = [[NSImageView alloc] init]; + [_applicationIconView setImage:[NSApp applicationIconImage]]; + [_dockTile performSelector:@selector(setContentView:) withObject:_applicationIconView]; + } +} + +void MacOSXTaskbarManager::clearApplicationIconView() { + if (_dockTile == nil) + return; + [_dockTile performSelector:@selector(setContentView:) withObject:nil]; + [_applicationIconView release]; + _applicationIconView = nil; +} + +void MacOSXTaskbarManager::initOverlayIconView() { + if (_dockTile == nil) + return; + if (_overlayIconView == nil) { + const double overlaySize = 0.75; + initApplicationIconView(); + NSSize size = [_applicationIconView frame].size; + _overlayIconView = [[NSImageView alloc] initWithFrame:NSMakeRect(size.width * (1.0-overlaySize), 0.0f, size.width * overlaySize, size.height * overlaySize)]; + [_overlayIconView setImageAlignment:NSImageAlignBottomRight]; + [_applicationIconView addSubview:_overlayIconView]; + [_overlayIconView release]; + } +} + +void MacOSXTaskbarManager::clearOverlayIconView() { + if (_progress < 0.0) + clearApplicationIconView(); + else + [_overlayIconView removeFromSuperview]; + _overlayIconView = nil; +} + +void MacOSXTaskbarManager::setOverlayIcon(const Common::String &name, const Common::String &description) { + if (_dockTile == nil) + return; + + if (name.empty()) { + clearOverlayIconView(); + [_dockTile performSelector:@selector(display)]; + return; + } + + Common::String path = getIconPath(name); + if (path.empty()) + return; + + initOverlayIconView(); + + CFStringRef imageFile = CFStringCreateWithCString(0, path.c_str(), kCFStringEncodingASCII); + NSImage* image = [[NSImage alloc] initWithContentsOfFile:(NSString *)imageFile]; + [_overlayIconView setImage:image]; + [image release]; + CFRelease(imageFile); + + [_dockTile performSelector:@selector(display)]; +} + +void MacOSXTaskbarManager::setProgressValue(int completed, int total) { + if (_dockTile == nil) + return; + + if (total > 0) + _progress = (double)completed / (double)total; + else if (_progress < 0) + _progress = 0.0; + + NSImage *mainIcon = [[NSApp applicationIconImage] copy]; + double barSize = [mainIcon size].width; + double progressSize = barSize * _progress; + [mainIcon lockFocus]; + [[NSColor colorWithDeviceRed:(40.0/255.0) green:(120.0/255.0) blue:(255.0/255.0) alpha:0.78] set]; + [NSBezierPath fillRect:NSMakeRect(0, 0, progressSize, 11)]; + [[NSColor colorWithDeviceRed:(241.0/255.0) green:(241.0/255.0) blue:(241.0/255.0) alpha:0.78] set]; + [NSBezierPath fillRect:NSMakeRect(progressSize, 0, barSize-progressSize, 11)]; + [mainIcon unlockFocus]; + + initApplicationIconView(); + [_applicationIconView setImage:mainIcon]; + [mainIcon release]; + + [_dockTile performSelector:@selector(display)]; +} + +void MacOSXTaskbarManager::setProgressState(TaskbarProgressState state) { + if (_dockTile == nil) + return; + + // Only support two states: visible and not visible. + if (state == kTaskbarNoProgress) { + _progress = -1.0; + if (_overlayIconView == nil) + clearApplicationIconView(); + else if (_applicationIconView != nil) + [_applicationIconView setImage:[NSApp applicationIconImage]]; + return; + } + + setProgressValue(-1, -1); +} + +void MacOSXTaskbarManager::setCount(int count) { + if (_dockTile == nil) + return; + + if (count > 0) + [_dockTile performSelector:@selector(setBadgeLabel:) withObject:[NSString stringWithFormat:@"%d", count]]; + else + [_dockTile performSelector:@selector(setBadgeLabel:) withObject:nil]; +} + +void MacOSXTaskbarManager::notifyError() { + if (_dockTile == nil) + return; + + // NSImageNameCaution was introduced in 10.6. + // For compatibility with older systems we should use something else (e.g. overlay label + // or our own icon). + //initOverlayIconView(); + //[_overlayIconView setImage:[NSImage imageNamed:NSImageNameCaution]]; + //[_dockTile performSelector:@selector(display)]; +} + +void MacOSXTaskbarManager::clearError() { + if (_dockTile == nil) + return; + + clearOverlayIconView(); + [_dockTile performSelector:@selector(display)]; + return; +} + +Common::String MacOSXTaskbarManager::getIconPath(const Common::String& target) { + // We first try to look for a iconspath configuration variable then + // fallback to the extra path + // + // Icons can be either in a subfolder named "icons" or directly in the path + + Common::String iconsPath = ConfMan.get("iconspath"); + Common::String extraPath = ConfMan.get("extrapath"); + +#define TRY_ICON_PATH(path) { \ +Common::FSNode node((path)); \ +if (node.exists()) \ +return (path); \ +} + + if (!iconsPath.empty()) { + TRY_ICON_PATH(iconsPath + "/" + target + ".png"); + TRY_ICON_PATH(iconsPath + "/" + ConfMan.get("gameid") + ".png"); + TRY_ICON_PATH(iconsPath + "/icons/" + target + ".png"); + TRY_ICON_PATH(iconsPath + "/icons/" + ConfMan.get("gameid") + ".png"); + } + + if (!extraPath.empty()) { + TRY_ICON_PATH(extraPath + "/" + target + ".png"); + TRY_ICON_PATH(extraPath + "/" + ConfMan.get("gameid") + ".png"); + TRY_ICON_PATH(extraPath + "/icons/" + target + ".png"); + TRY_ICON_PATH(extraPath + "/icons/" + ConfMan.get("gameid") + ".png"); + } + + return ""; +} + + +#endif -- cgit v1.2.3