1///////////////////////////////////////////////////////////////////////// 2// File: src/osx/cocoa/taskbar.mm 3// Purpose: Implements wxTaskBarIcon class 4// Author: David Elliott, Stefan Csomor 5// Modified by: 6// Created: 2004/01/24 7// Copyright: (c) 2004 David Elliott, Stefan Csomor 8// Licence: wxWindows licence 9///////////////////////////////////////////////////////////////////////// 10 11#include "wx/wxprec.h" 12#if wxUSE_TASKBARICON 13 14#ifndef WX_PRECOMP 15 #include "wx/toplevel.h" 16 #include "wx/menu.h" 17 #include "wx/icon.h" 18 #include "wx/log.h" 19 #include "wx/dcclient.h" 20#endif 21 22#include "wx/taskbar.h" 23 24#include "wx/osx/private.h" 25 26class wxTaskBarIconWindow; 27 28//----------------------------------------------------------------------------- 29// 30// wxTaskBarIconWindow 31// 32// Event handler for menus 33// NB: Since wxWindows in Mac HAVE to have parents we need this to be 34// a top level window... 35//----------------------------------------------------------------------------- 36 37class wxTaskBarIconWindow : public wxTopLevelWindow 38{ 39public: 40 wxTaskBarIconWindow(wxTaskBarIconImpl *impl); 41 42 void OnMenuEvent(wxCommandEvent& event); 43 void OnUpdateUIEvent(wxUpdateUIEvent& event); 44 45private: 46 wxTaskBarIconImpl *m_impl; 47 DECLARE_EVENT_TABLE() 48}; 49 50// ============================================================================ 51// wxTaskBarIconImpl 52// Base class for the various Cocoa implementations. 53// ============================================================================ 54class wxTaskBarIconImpl 55{ 56public: 57 wxTaskBarIconImpl(wxTaskBarIcon *taskBarIcon); 58 59 virtual bool IsStatusItem() const { return false; } 60 61 virtual bool SetIcon(const wxIcon& icon, const wxString& tooltip = wxEmptyString) = 0; 62 virtual bool RemoveIcon() = 0; 63 64 bool IsIconInstalled() const { return m_icon.IsOk(); } 65 66 virtual bool PopupMenu(wxMenu *menu) = 0; 67 virtual ~wxTaskBarIconImpl(); 68 inline wxTaskBarIcon* GetTaskBarIcon() { return m_taskBarIcon; } 69 wxMenu * CreatePopupMenu() 70 { return m_taskBarIcon->CreatePopupMenu(); } 71 72 wxDECLARE_NO_COPY_CLASS(wxTaskBarIconImpl); 73 74protected: 75 wxTaskBarIcon *m_taskBarIcon; 76 wxBitmap m_icon; 77 wxTaskBarIconWindow *m_eventWindow; 78private: 79 wxTaskBarIconImpl(); 80}; 81 82// ============================================================================ 83// wxTaskBarIconDockImpl 84// An implementation using the Dock icon. 85// ============================================================================ 86class wxTaskBarIconDockImpl: public wxTaskBarIconImpl 87{ 88public: 89 wxTaskBarIconDockImpl(wxTaskBarIcon *taskBarIcon); 90 virtual ~wxTaskBarIconDockImpl(); 91 virtual bool SetIcon(const wxIcon& icon, const wxString& tooltip = wxEmptyString); 92 virtual bool RemoveIcon(); 93 virtual bool PopupMenu(wxMenu *menu); 94 95 static WX_NSMenu OSXGetDockHMenu(); 96protected: 97 WX_NSMenu OSXDoGetDockHMenu(); 98 // There can be only one Dock icon, so make sure we keep it that way 99 static wxTaskBarIconDockImpl *sm_dockIcon; 100private: 101 wxTaskBarIconDockImpl(); 102 wxMenu *m_pMenu; 103}; 104 105class wxTaskBarIconCustomStatusItemImpl; 106 107@interface wxOSXStatusItemTarget : NSObject 108{ 109 wxTaskBarIconCustomStatusItemImpl* impl; 110} 111@end 112 113// ============================================================================ 114// wxTaskBarIconCustomStatusItemImpl 115// An implementation using an NSStatusItem with a custom NSView 116// ============================================================================ 117class wxTaskBarIconCustomStatusItemImpl: public wxTaskBarIconImpl 118{ 119public: 120 wxTaskBarIconCustomStatusItemImpl(wxTaskBarIcon *taskBarIcon); 121 virtual ~wxTaskBarIconCustomStatusItemImpl(); 122 123 virtual bool IsStatusItem() const { return true; } 124 125 virtual bool SetIcon(const wxIcon& icon, const wxString& tooltip = wxEmptyString); 126 virtual bool RemoveIcon(); 127 virtual bool PopupMenu(wxMenu *menu); 128protected: 129 NSStatusItem *m_statusItem; 130 wxOSXStatusItemTarget *m_target; 131private: 132 wxTaskBarIconCustomStatusItemImpl(); 133}; 134 135// ============================================================================ 136// wxTaskBarIcon implementation 137// The facade class. 138// ============================================================================ 139IMPLEMENT_DYNAMIC_CLASS(wxTaskBarIcon, wxEvtHandler) 140 141wxTaskBarIcon::wxTaskBarIcon(wxTaskBarIconType iconType) 142{ 143 if(iconType == wxTBI_DOCK) 144 m_impl = new wxTaskBarIconDockImpl(this); 145 else if(iconType == wxTBI_CUSTOM_STATUSITEM) 146 m_impl = new wxTaskBarIconCustomStatusItemImpl(this); 147 else 148 { m_impl = NULL; 149 wxFAIL_MSG(wxT("Invalid wxTaskBarIcon type")); 150 } 151} 152 153wxTaskBarIcon::~wxTaskBarIcon() 154{ 155 if ( m_impl ) 156 { 157 if ( m_impl->IsIconInstalled() ) 158 m_impl->RemoveIcon(); 159 delete m_impl; 160 m_impl = NULL; 161 } 162} 163 164bool wxTaskBarIcon::OSXIsStatusItem() 165{ 166 if ( m_impl ) 167 return m_impl->IsStatusItem(); 168 169 return false; 170} 171 172// Operations 173 174bool wxTaskBarIcon::IsIconInstalled() const 175{ 176 if ( m_impl ) 177 return m_impl->IsIconInstalled(); 178 179 return false; 180} 181 182bool wxTaskBarIcon::SetIcon(const wxIcon& icon, const wxString& tooltip) 183{ 184 if ( m_impl ) 185 return m_impl->SetIcon(icon,tooltip); 186 187 return false; 188} 189 190bool wxTaskBarIcon::RemoveIcon() 191{ 192 if ( m_impl ) 193 return m_impl->RemoveIcon(); 194 195 return false; 196} 197 198bool wxTaskBarIcon::PopupMenu(wxMenu *menu) 199{ 200 if ( m_impl ) 201 return m_impl->PopupMenu(menu); 202 203 return false; 204} 205 206// ============================================================================ 207// wxTaskBarIconImpl 208// ============================================================================ 209 210wxTaskBarIconImpl::wxTaskBarIconImpl(wxTaskBarIcon* taskBarIcon) 211 : m_taskBarIcon(taskBarIcon), m_eventWindow(new wxTaskBarIconWindow(this)) 212{ 213} 214 215wxTaskBarIconImpl::~wxTaskBarIconImpl() 216{ 217 delete m_eventWindow; 218} 219 220// ============================================================================ 221// wxTaskBarIconDockImpl 222// ============================================================================ 223wxTaskBarIconDockImpl *wxTaskBarIconDockImpl::sm_dockIcon = NULL; 224 225wxTaskBarIconDockImpl::wxTaskBarIconDockImpl(wxTaskBarIcon *taskBarIcon) 226: wxTaskBarIconImpl(taskBarIcon) 227{ 228 wxASSERT_MSG(!sm_dockIcon, wxT("You should never have more than one dock icon!")); 229 sm_dockIcon = this; 230 m_pMenu = NULL; 231} 232 233wxTaskBarIconDockImpl::~wxTaskBarIconDockImpl() 234{ 235 if(sm_dockIcon == this) 236 sm_dockIcon = NULL; 237} 238 239WX_NSMenu wxTaskBarIconDockImpl::OSXGetDockHMenu() 240{ 241 if(sm_dockIcon) 242 return sm_dockIcon->OSXDoGetDockHMenu(); 243 244 return nil; 245} 246 247WX_NSMenu wxTaskBarIconDockImpl::OSXDoGetDockHMenu() 248{ 249 wxMenu *dockMenu = CreatePopupMenu(); 250 251 if(!dockMenu) 252 return nil; 253 254 wxDELETE(m_pMenu); 255 256 m_pMenu = dockMenu; 257 258 m_pMenu->SetInvokingWindow(m_eventWindow); 259 260 m_pMenu->UpdateUI(); 261 262 return (WX_NSMenu)dockMenu->GetHMenu(); 263} 264 265bool wxTaskBarIconDockImpl::SetIcon(const wxIcon& icon, const wxString& WXUNUSED(tooltip)) 266{ 267 m_icon.CopyFromIcon(icon); 268 [[NSApplication sharedApplication] setApplicationIconImage:m_icon.GetNSImage()]; 269 return true; 270} 271 272bool wxTaskBarIconDockImpl::RemoveIcon() 273{ 274 wxDELETE(m_pMenu); 275 m_icon = wxBitmap(); 276 [[NSApplication sharedApplication] setApplicationIconImage:nil]; 277 return true; 278} 279 280bool wxTaskBarIconDockImpl::PopupMenu(wxMenu *WXUNUSED(menu)) 281{ 282 wxFAIL_MSG(wxT("You cannot force the Dock icon menu to popup")); 283 return false; 284} 285 286@interface wxNSAppController(wxTaskBarIconNSApplicationDelegateCategory) 287- (NSMenu*)applicationDockMenu:(NSApplication *)sender; 288@end 289 290@implementation wxNSAppController(wxTaskBarIconNSApplicationDelegateCategory) 291- (NSMenu*)applicationDockMenu:(NSApplication *)sender 292{ 293 wxUnusedVar(sender); 294 295 return wxTaskBarIconDockImpl::OSXGetDockHMenu(); 296} 297@end 298 299// ============================================================================ 300// wxTaskBarIconCustomStatusItemImpl 301// ============================================================================ 302 303@implementation wxOSXStatusItemTarget 304 305- (void) clickedAction: (id) sender 306{ 307 wxUnusedVar(sender); 308 wxMenu *menu = impl->CreatePopupMenu(); 309 if (menu) 310 { 311 impl->PopupMenu(menu); 312 delete menu; 313 } 314} 315 316- (void)setImplementation: (wxTaskBarIconCustomStatusItemImpl *) theImplementation 317{ 318 impl = theImplementation; 319} 320 321- (wxTaskBarIconCustomStatusItemImpl*) implementation 322{ 323 return impl; 324} 325 326@end 327 328 329wxTaskBarIconCustomStatusItemImpl::wxTaskBarIconCustomStatusItemImpl(wxTaskBarIcon *taskBarIcon) 330: wxTaskBarIconImpl(taskBarIcon) 331{ 332 m_statusItem = nil; 333 m_target = nil; 334} 335 336wxTaskBarIconCustomStatusItemImpl::~wxTaskBarIconCustomStatusItemImpl() 337{ 338} 339 340bool wxTaskBarIconCustomStatusItemImpl::SetIcon(const wxIcon& icon, const wxString& tooltip) 341{ 342 if(!m_statusItem) 343 { 344 m_statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength]; 345 [m_statusItem retain]; 346 347 m_target = [[wxOSXStatusItemTarget alloc] init]; 348 [m_target setImplementation:this]; 349 [m_statusItem setHighlightMode:YES]; 350 [m_statusItem setTarget:m_target]; 351 [m_statusItem setAction:@selector(clickedAction:)]; 352 [m_statusItem sendActionOn:NSLeftMouseDownMask]; 353 } 354 355 m_icon.CopyFromIcon(icon); 356 357 // status item doesn't scale automatically 358 // first scale to optimal pixel resolution 359 360 int dimension = wxMax( m_icon.GetHeight(), m_icon.GetWidth() ); 361 int target_dimension = 16 * wxOSXGetMainScreenContentScaleFactor(); 362 if ( dimension > target_dimension ) 363 { 364 wxImage img = m_icon.ConvertToImage(); 365 int factor = (dimension+(target_dimension-1))/target_dimension; 366 m_icon = img.ShrinkBy(factor, factor); 367 } 368 369 NSImage* nsimage = m_icon.GetNSImage(); 370 NSSize size = [nsimage size]; 371 372 // then scale to optimal point resolution 373 374 dimension = wxMax(size.width,size.height); 375 if ( dimension > 16 ) 376 { 377 int factor = (dimension+15)/16; 378 size.width /= factor; 379 size.height /= factor; 380 [nsimage setSize:size]; 381 } 382 [m_statusItem setImage:nsimage]; 383 384 wxCFStringRef cfTooltip(tooltip); 385 [m_statusItem setToolTip:cfTooltip.AsNSString()]; 386 return true; 387} 388 389bool wxTaskBarIconCustomStatusItemImpl::RemoveIcon() 390{ 391 [m_statusItem release]; 392 m_statusItem = nil; 393 [m_target release]; 394 m_target = nil; 395 396 m_icon = wxBitmap(); 397 398 return true; 399} 400 401bool wxTaskBarIconCustomStatusItemImpl::PopupMenu(wxMenu *menu) 402{ 403 wxASSERT(menu); 404 405 menu->SetInvokingWindow(m_eventWindow); 406 menu->UpdateUI(); 407 408 [m_statusItem popUpStatusItemMenu:(NSMenu*)menu->GetHMenu()]; 409 410 menu->SetInvokingWindow(NULL); 411 return true; 412} 413 414// ============================================================================ 415// wxTaskBarIconWindow 416// ============================================================================ 417 418BEGIN_EVENT_TABLE(wxTaskBarIconWindow, wxWindow) 419EVT_MENU(-1, wxTaskBarIconWindow::OnMenuEvent) 420EVT_UPDATE_UI(-1, wxTaskBarIconWindow::OnUpdateUIEvent) 421END_EVENT_TABLE() 422 423wxTaskBarIconWindow::wxTaskBarIconWindow(wxTaskBarIconImpl *impl) 424: m_impl(impl) 425{ 426} 427 428void wxTaskBarIconWindow::OnMenuEvent(wxCommandEvent& event) 429{ 430 m_impl->GetTaskBarIcon()->ProcessEvent(event); 431} 432 433void wxTaskBarIconWindow::OnUpdateUIEvent(wxUpdateUIEvent& event) 434{ 435 m_impl->GetTaskBarIcon()->ProcessEvent(event); 436} 437 438#endif //def wxHAS_TASK_BAR_ICON 439