1///////////////////////////////////////////////////////////////////////// 2// File: src/cocoa/taskbar.mm 3// Purpose: Implements wxTaskBarIcon class 4// Author: David Elliott 5// Modified by: 6// Created: 2004/01/24 7// Copyright: (c) 2004 David Elliott 8// Licence: wxWindows licence 9///////////////////////////////////////////////////////////////////////// 10 11#include "wx/wxprec.h" 12#ifdef wxHAS_TASK_BAR_ICON 13 14#ifndef WX_PRECOMP 15 #include "wx/menu.h" 16 #include "wx/icon.h" 17 #include "wx/log.h" 18 #include "wx/dcclient.h" 19#endif 20 21#include "wx/taskbar.h" 22 23#import <AppKit/NSApplication.h> 24#import <AppKit/NSImage.h> 25#import <AppKit/NSMenu.h> 26#import <AppKit/NSMenuItem.h> 27#import <AppKit/NSStatusBar.h> 28#import <AppKit/NSStatusItem.h> 29#import <AppKit/NSView.h> 30#import <Foundation/NSArray.h> 31#import <Foundation/NSEnumerator.h> 32 33#import <AppKit/NSEvent.h> 34#import <AppKit/NSWindow.h> 35#import <AppKit/NSGraphicsContext.h> 36 37#include "wx/cocoa/NSApplication.h" 38#include "wx/cocoa/autorelease.h" 39 40// A category for methods that are only present in Panther's SDK 41@interface NSStatusItem(wxNSStatusItemPrePantherCompatibility) 42- (void)popUpStatusItemMenu:(NSMenu *)menu; 43@end 44 45class wxTaskBarIconWindow; 46 47// ============================================================================ 48// wxTaskBarIconCocoaImpl 49// Base class for the various Cocoa implementations. 50// ============================================================================ 51class wxTaskBarIconCocoaImpl 52{ 53public: 54 wxTaskBarIconCocoaImpl(wxTaskBarIcon *taskBarIcon) 55 : m_taskBarIcon(taskBarIcon) 56 , m_iconWindow(NULL) 57 {} 58 virtual bool SetIcon(const wxIcon& icon, const wxString& tooltip = wxEmptyString) = 0; 59 virtual bool RemoveIcon() = 0; 60 virtual bool PopupMenu(wxMenu *menu) = 0; 61 virtual ~wxTaskBarIconCocoaImpl(); 62 inline wxTaskBarIcon* GetTaskBarIcon() { return m_taskBarIcon; } 63protected: 64 inline wxMenu* CreatePopupMenu() 65 { wxASSERT(m_taskBarIcon); 66 return m_taskBarIcon->CreatePopupMenu(); 67 } 68 wxTaskBarIcon *m_taskBarIcon; 69 wxTaskBarIconWindow *m_iconWindow; 70private: 71 wxTaskBarIconCocoaImpl(); 72}; 73 74// ============================================================================ 75// wxTaskBarIconDockImpl 76// An implementation using the Dock icon. 77// ============================================================================ 78class wxTaskBarIconDockImpl: public wxTaskBarIconCocoaImpl 79{ 80public: 81 wxTaskBarIconDockImpl(wxTaskBarIcon *taskBarIcon); 82 virtual ~wxTaskBarIconDockImpl(); 83 virtual bool SetIcon(const wxIcon& icon, const wxString& tooltip = wxEmptyString); 84 virtual bool RemoveIcon(); 85 virtual bool PopupMenu(wxMenu *menu); 86 87 static WX_NSMenu CocoaGetDockNSMenu(); 88protected: 89 WX_NSMenu CocoaDoGetDockNSMenu(); 90 WX_NSImage m_originalDockIcon; 91 // There can be only one Dock icon, so make sure we keep it that way 92 static wxTaskBarIconDockImpl *sm_dockIcon; 93private: 94 wxTaskBarIconDockImpl(); 95}; 96 97// ============================================================================ 98// wxTaskBarIconCustomStatusItemImpl 99// An implementation using an NSStatusItem with a custom NSView 100// ============================================================================ 101class wxTaskBarIconCustomStatusItemImpl: public wxTaskBarIconCocoaImpl 102{ 103public: 104 wxTaskBarIconCustomStatusItemImpl(wxTaskBarIcon *taskBarIcon); 105 virtual ~wxTaskBarIconCustomStatusItemImpl(); 106 virtual bool SetIcon(const wxIcon& icon, const wxString& tooltip = wxEmptyString); 107 virtual bool RemoveIcon(); 108 virtual bool PopupMenu(wxMenu *menu); 109protected: 110 NSStatusItem *m_cocoaNSStatusItem; 111private: 112 wxTaskBarIconCustomStatusItemImpl(); 113}; 114 115// ============================================================================ 116// wxTaskBarIconWindow 117// Used by all implementations to forward events from the wxMenu 118// ============================================================================ 119class wxTaskBarIconWindow: public wxWindow 120{ 121 DECLARE_EVENT_TABLE() 122public: 123 wxTaskBarIconWindow(wxTaskBarIconCocoaImpl *taskBarIconImpl) 124 : wxWindow(NULL,-1) 125 , m_taskBarIconImpl(taskBarIconImpl) 126 { wxASSERT(m_taskBarIconImpl); } 127 128 void OnMenuEvent(wxCommandEvent& event); 129protected: 130 wxTaskBarIconCocoaImpl *m_taskBarIconImpl; 131}; 132 133// ============================================================================ 134// wxTaskBarIconWindowCustom 135// Used by the CustomStatusIcon implementation for the custom NSView. 136// ============================================================================ 137class wxTaskBarIconWindowCustom: public wxTaskBarIconWindow 138{ 139 DECLARE_EVENT_TABLE() 140public: 141 wxTaskBarIconWindowCustom(wxTaskBarIconCocoaImpl *taskBarIconImpl) 142 : wxTaskBarIconWindow(taskBarIconImpl) 143 {} 144 void SetIcon(const wxIcon& icon) 145 { m_icon = icon; } 146 void OnMouseEvent(wxMouseEvent &event); 147 void OnPaint(wxPaintEvent &event); 148protected: 149 wxIcon m_icon; 150}; 151 152// ============================================================================ 153// wxTaskBarIcon implementation 154// The facade class. 155// ============================================================================ 156IMPLEMENT_DYNAMIC_CLASS(wxTaskBarIcon, wxEvtHandler) 157 158wxTaskBarIcon::wxTaskBarIcon(wxTaskBarIconType iconType) 159{ 160 if(iconType == DOCK) 161 m_impl = new wxTaskBarIconDockImpl(this); 162 else if(iconType == CUSTOM_STATUSITEM) 163 m_impl = new wxTaskBarIconCustomStatusItemImpl(this); 164 else 165 { m_impl = NULL; 166 wxFAIL_MSG(wxT("Invalid wxTaskBarIcon type")); 167 } 168} 169 170wxTaskBarIcon::~wxTaskBarIcon() 171{ 172 delete m_impl; 173} 174 175// Operations 176bool wxTaskBarIcon::SetIcon(const wxIcon& icon, const wxString& tooltip) 177{ 178 return m_impl->SetIcon(icon,tooltip); 179} 180 181bool wxTaskBarIcon::RemoveIcon() 182{ 183 return m_impl->RemoveIcon(); 184} 185 186bool wxTaskBarIcon::PopupMenu(wxMenu *menu) 187{ 188 return m_impl->PopupMenu(menu); 189} 190 191// ============================================================================ 192// wxTaskBarIconCocoaImpl 193// ============================================================================ 194 195#if 0 196wxTaskBarIconCocoaImpl::wxTaskBarIconCocoaImpl(wxTaskBarIcon *taskBarIcon) 197: m_taskBarIcon(taskBarIcon) 198, m_iconWindow(NULL) 199{ 200} 201#endif 202 203wxTaskBarIconCocoaImpl::~wxTaskBarIconCocoaImpl() 204{ 205// wxAutoNSAutoreleasePool pool; 206 delete m_iconWindow; 207} 208 209// ============================================================================ 210// wxTaskBarIconDockImpl 211// ============================================================================ 212wxTaskBarIconDockImpl *wxTaskBarIconDockImpl::sm_dockIcon = NULL; 213 214wxTaskBarIconDockImpl::wxTaskBarIconDockImpl(wxTaskBarIcon *taskBarIcon) 215: wxTaskBarIconCocoaImpl(taskBarIcon) 216{ 217 m_originalDockIcon = nil; 218 wxASSERT_MSG(!sm_dockIcon, wxT("You should never have more than one dock icon!")); 219 sm_dockIcon = this; 220} 221 222wxTaskBarIconDockImpl::~wxTaskBarIconDockImpl() 223{ 224// wxAutoNSAutoreleasePool pool; 225 if(sm_dockIcon == this) 226 sm_dockIcon = NULL; 227} 228 229WX_NSMenu wxTaskBarIconDockImpl::CocoaGetDockNSMenu() 230{ 231 if(sm_dockIcon) 232 return sm_dockIcon->CocoaDoGetDockNSMenu(); 233 return nil; 234} 235 236WX_NSMenu wxTaskBarIconDockImpl::CocoaDoGetDockNSMenu() 237{ 238 wxMenu *dockMenu = CreatePopupMenu(); 239 if(!dockMenu) 240 return nil; 241 if(!m_iconWindow) 242 m_iconWindow = new wxTaskBarIconWindow(this); 243 dockMenu->SetInvokingWindow(m_iconWindow); 244 dockMenu->UpdateUI(); 245 dockMenu->SetCocoaDeletes(true); 246 return dockMenu->GetNSMenu(); 247} 248 249bool wxTaskBarIconDockImpl::SetIcon(const wxIcon& icon, const wxString& tooltip) 250{ 251 wxAutoNSAutoreleasePool pool; 252 m_originalDockIcon = [[[NSApplication sharedApplication] applicationIconImage] retain]; 253 [[NSApplication sharedApplication] setApplicationIconImage:icon.GetNSImage()]; 254 return true; 255} 256 257bool wxTaskBarIconDockImpl::RemoveIcon() 258{ 259 [[NSApplication sharedApplication] setApplicationIconImage:m_originalDockIcon]; 260 [m_originalDockIcon release]; 261 return true; 262} 263 264bool wxTaskBarIconDockImpl::PopupMenu(wxMenu *menu) 265{ 266 wxFAIL_MSG(wxT("You cannot force the Dock icon menu to popup")); 267 return false; 268} 269 270 271// ============================================================================ 272// wxTaskBarIconCustomStatusItemImpl 273// ============================================================================ 274wxTaskBarIconCustomStatusItemImpl::wxTaskBarIconCustomStatusItemImpl(wxTaskBarIcon *taskBarIcon) 275: wxTaskBarIconCocoaImpl(taskBarIcon) 276{ 277 m_cocoaNSStatusItem = nil; 278} 279 280wxTaskBarIconCustomStatusItemImpl::~wxTaskBarIconCustomStatusItemImpl() 281{ 282} 283 284bool wxTaskBarIconCustomStatusItemImpl::SetIcon(const wxIcon& icon, const wxString& tooltip) 285{ 286 wxAutoNSAutoreleasePool pool; 287 if(!m_cocoaNSStatusItem) 288 { 289 m_cocoaNSStatusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength]; 290 [m_cocoaNSStatusItem retain]; 291 } 292 if(!m_iconWindow) 293 m_iconWindow= new wxTaskBarIconWindowCustom(this); 294 static_cast<wxTaskBarIconWindowCustom*>(m_iconWindow)->SetIcon(icon); 295 // FIXME: no less than 10 because most icon types don't work yet 296 // and this allows us to see how task bar icons would work 297 [m_iconWindow->GetNSView() setFrame:NSMakeRect(0.0,0.0,wxMax(10,icon.GetWidth()),[[NSStatusBar systemStatusBar] thickness])]; 298 [m_cocoaNSStatusItem setView:m_iconWindow->GetNSView()]; 299 return true; 300} 301 302bool wxTaskBarIconCustomStatusItemImpl::RemoveIcon() 303{ 304 [m_cocoaNSStatusItem release]; 305 m_cocoaNSStatusItem = nil; 306 delete m_iconWindow; 307 m_iconWindow = NULL; 308 return true; 309} 310 311bool wxTaskBarIconCustomStatusItemImpl::PopupMenu(wxMenu *menu) 312{ 313 wxCHECK_MSG(menu, false, "can't popup a NULL menu"); 314 315 wxMenuInvokingWindowSetter setInvokingWin(*menu, m_iconWindow); 316 menu->UpdateUI(); 317 318 if([m_cocoaNSStatusItem respondsToSelector:@selector(popUpStatusItemMenu:)]) 319 { // OS X >= 10.3 320 [m_cocoaNSStatusItem popUpStatusItemMenu:menu->GetNSMenu()]; 321 } 322 else 323 { // pretty good fake for OS X < 10.3 324 NSEvent *nsevent = [NSEvent mouseEventWithType:NSLeftMouseDown 325 location:NSMakePoint(-1.0,-4.0) modifierFlags:0 timestamp:0 326 windowNumber:[[m_iconWindow->GetNSView() window] windowNumber] 327 context:[NSGraphicsContext currentContext] 328 eventNumber:0 clickCount:1 pressure:0.0]; 329 [NSMenu popUpContextMenu:menu->GetNSMenu() withEvent:nsevent forView:m_iconWindow->GetNSView()]; 330 } 331 return true; 332} 333 334// ============================================================================ 335// wxTaskBarIconWindow 336// ============================================================================ 337BEGIN_EVENT_TABLE(wxTaskBarIconWindow, wxWindow) 338 EVT_MENU(-1, wxTaskBarIconWindow::OnMenuEvent) 339END_EVENT_TABLE() 340 341void wxTaskBarIconWindow::OnMenuEvent(wxCommandEvent &event) 342{ 343 m_taskBarIconImpl->GetTaskBarIcon()->ProcessEvent(event); 344} 345 346// ============================================================================ 347// wxTaskBarIconWindowCustom 348// ============================================================================ 349BEGIN_EVENT_TABLE(wxTaskBarIconWindowCustom, wxTaskBarIconWindow) 350 EVT_MOUSE_EVENTS(wxTaskBarIconWindowCustom::OnMouseEvent) 351 EVT_PAINT(wxTaskBarIconWindowCustom::OnPaint) 352END_EVENT_TABLE() 353 354void wxTaskBarIconWindowCustom::OnMouseEvent(wxMouseEvent &event) 355{ 356 wxEventType tbEventType = 0; 357 if(event.GetEventType() == wxEVT_MOTION) 358 tbEventType = wxEVT_TASKBAR_MOVE; 359 else if(event.GetEventType() == wxEVT_LEFT_DOWN) 360 tbEventType = wxEVT_TASKBAR_LEFT_DOWN; 361 else if(event.GetEventType() == wxEVT_LEFT_UP) 362 tbEventType = wxEVT_TASKBAR_LEFT_UP; 363 else if(event.GetEventType() == wxEVT_RIGHT_DOWN) 364 tbEventType = wxEVT_TASKBAR_RIGHT_DOWN; 365 else if(event.GetEventType() == wxEVT_RIGHT_UP) 366 tbEventType = wxEVT_TASKBAR_RIGHT_UP; 367 else if(event.GetEventType() == wxEVT_LEFT_DCLICK) 368 tbEventType = wxEVT_TASKBAR_LEFT_DCLICK; 369 else if(event.GetEventType() == wxEVT_RIGHT_DCLICK) 370 tbEventType = wxEVT_TASKBAR_RIGHT_DCLICK; 371 else 372 return; 373 wxTaskBarIconEvent tbiEvent(tbEventType,m_taskBarIconImpl->GetTaskBarIcon()); 374 m_taskBarIconImpl->GetTaskBarIcon()->ProcessEvent(tbiEvent); 375} 376 377void wxTaskBarIconWindowCustom::OnPaint(wxPaintEvent &event) 378{ 379 wxPaintDC dc(this); 380 // FIXME: This is a temporary hack until we can see real icons 381 dc.SetBackground(wxBrush(*wxBLUE)); 382 dc.Clear(); 383 dc.DrawIcon(m_icon,0,0); 384} 385 386// ============================================================================ 387// wxTaskBarIconNSApplicationDelegateCategory 388// ============================================================================ 389 390// This neatly solves the problem of DLL separation. If the wxAdvanced 391// library (which this file is part of) is loaded then this category is 392// defined and we get dock menu behaviour without app.mm ever having to 393// know we exist. C++ did sucketh so. :-) 394 395@interface wxNSApplicationDelegate(wxTaskBarIconNSApplicationDelegateCategory) 396- (NSMenu*)applicationDockMenu:(NSApplication *)sender; 397@end 398 399@implementation wxNSApplicationDelegate(wxTaskBarIconNSApplicationDelegateCategory) 400- (NSMenu*)applicationDockMenu:(NSApplication *)sender 401{ 402 return wxTaskBarIconDockImpl::CocoaGetDockNSMenu(); 403} 404@end 405 406#endif //def wxHAS_TASK_BAR_ICON 407