1 /////////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/mac/carbon/taskbar.cpp 
   3 // Purpose:     wxTaskBarIcon 
   8 // Copyright:   (c) 2004 Ryan Norton 
   9 // Licence:     wxWindows licence 
  10 /////////////////////////////////////////////////////////////////////////////// 
  12 #include "wx/wxprec.h" 
  14 #ifdef wxHAS_TASK_BAR_ICON 
  16 #include "wx/taskbar.h" 
  19     #include "wx/dcmemory.h" 
  21     #include "wx/toplevel.h" 
  25 #include "wx/mac/private.h" 
  27 class wxTaskBarIconImpl
 
  30     wxTaskBarIconImpl(wxTaskBarIcon
* parent
); 
  31     virtual ~wxTaskBarIconImpl(); 
  33     virtual bool IsIconInstalled() const = 0; 
  34     virtual bool SetIcon(const wxIcon
& icon
, const wxString
& tooltip
) = 0; 
  35     virtual bool RemoveIcon() = 0; 
  36     virtual bool PopupMenu(wxMenu 
*menu
) = 0; 
  38     wxMenu 
* CreatePopupMenu() 
  39     { return m_parent
->CreatePopupMenu(); } 
  41     wxTaskBarIcon 
*m_parent
; 
  42     class wxTaskBarIconWindow 
*m_menuEventWindow
; 
  44     DECLARE_NO_COPY_CLASS(wxTaskBarIconImpl
) 
  47 //----------------------------------------------------------------------------- 
  49 //  wxTaskBarIconWindow 
  51 //  Event handler for menus 
  52 //  NB: Since wxWindows in Mac HAVE to have parents we need this to be 
  53 //  a top level window... 
  54 //----------------------------------------------------------------------------- 
  56 class wxTaskBarIconWindow 
: public wxTopLevelWindow
 
  59     wxTaskBarIconWindow(wxTaskBarIconImpl 
*impl
) 
  60         : wxTopLevelWindow(NULL
, wxID_ANY
, wxEmptyString
), m_impl(impl
) 
  63             -1, wxEVT_COMMAND_MENU_SELECTED
, 
  64             wxCommandEventHandler(wxTaskBarIconWindow::OnMenuEvent
) ); 
  67     void OnMenuEvent(wxCommandEvent
& event
) 
  69         m_impl
->m_parent
->ProcessEvent(event
); 
  73     wxTaskBarIconImpl 
*m_impl
; 
  76 class wxDockTaskBarIcon 
: public wxTaskBarIconImpl
 
  79     wxDockTaskBarIcon(wxTaskBarIcon
* parent
); 
  80     virtual ~wxDockTaskBarIcon(); 
  82     virtual bool IsIconInstalled() const; 
  83     virtual bool SetIcon(const wxIcon
& icon
, const wxString
& tooltip
); 
  84     virtual bool RemoveIcon(); 
  85     virtual bool PopupMenu(wxMenu 
*menu
); 
  87     wxMenu
* DoCreatePopupMenu(); 
  89     EventHandlerRef     m_eventHandlerRef
; 
  90     EventHandlerUPP     m_eventupp
; 
  91     wxWindow           
*m_eventWindow
; 
  93     MenuRef             m_theLastMenu
; 
  97 // Forward declarations for utility functions for dock implementation 
  98 pascal OSStatus 
wxDockEventHandler( 
  99     EventHandlerCallRef inHandlerCallRef
, 
 100     EventRef inEvent
, void* pData 
); 
 101 wxMenu 
* wxDeepCopyMenu( wxMenu 
*menu 
); 
 104 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
 108 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
 110 wxTaskBarIconImpl::wxTaskBarIconImpl(wxTaskBarIcon
* parent
) 
 111     : m_parent(parent
), m_menuEventWindow(new wxTaskBarIconWindow(this)) 
 115 wxTaskBarIconImpl::~wxTaskBarIconImpl() 
 117     delete m_menuEventWindow
; 
 120 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
 124 //  OS X Dock implementation of wxTaskBarIcon using Carbon 
 125 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
 127 //----------------------------------------------------------------------------- 
 128 // wxDockEventHandler 
 130 // This is the global Mac/Carbon event handler for the dock. 
 131 // We need this for two reasons: 
 132 // 1) To handle wxTaskBarIcon menu events (see below for why) 
 133 // 2) To handle events from the dock when it requests a menu 
 134 //----------------------------------------------------------------------------- 
 135 pascal OSStatus 
wxDockEventHandler( EventHandlerCallRef inHandlerCallRef
, 
 136                                     EventRef inEvent
, void *pData 
) 
 138     // Get the parameters we want from the event 
 139     wxDockTaskBarIcon
* pTB 
= (wxDockTaskBarIcon
*) pData
; 
 140     const UInt32 eventClass 
= GetEventClass(inEvent
); 
 141     const UInt32 eventKind 
= GetEventKind(inEvent
); 
 143     // Handle wxTaskBar menu events (note that this is a global event handler 
 144     // so it will actually get called by all commands/menus) 
 145     if ((eventClass 
== kEventClassCommand
) && (eventKind 
== kEventCommandProcess
)) 
 147         // if we have no taskbar menu quickly pass it back to wxApp 
 148         if (pTB
->m_pMenu 
== NULL
) 
 149             return eventNotHandledErr
; 
 151         // This is the real reason why we need this. Normally menus 
 152         // get handled in wxMacAppEventHandler 
 154         // pascal OSStatus wxMacAppEventHandler(EventHandlerCallRef handler, 
 155         //                                      EventRef event, void *data) 
 157         // However, in the case of a taskbar menu call 
 158         // command.menu.menuRef IS NULL! 
 159         // Which causes the wxApp handler just to skip it. 
 160         MenuRef taskbarMenuRef 
= MAC_WXHMENU(pTB
->m_pMenu
->GetHMenu()); 
 163         // get the HICommand from the event 
 165         err 
= GetEventParameter( 
 166             inEvent
, kEventParamDirectObject
, 
 168             sizeof(HICommand
), NULL
, &command 
); 
 171             // Obtain the REAL menuRef and the menuItemIndex in the real menuRef 
 173             // NOTE: menuRef is generally used here for submenus, as 
 174             // GetMenuItemRefCon could give an incorrect wxMenuItem if we pass 
 175             // just the top level wxTaskBar menu 
 176             MenuItemIndex menuItemIndex
; 
 179             err 
= GetIndMenuItemWithCommandID( 
 182                 1, &menuRef
, &menuItemIndex 
); 
 185                 MenuCommand id 
= command
.commandID
; 
 186                 wxMenuItem 
*item 
= NULL
; 
 188                 if (id 
!= 0) // get the wxMenuItem reference from the MenuRef 
 189                     GetMenuItemRefCon( menuRef
, menuItemIndex
, (URefCon
*) &item 
); 
 193                     // Handle items that are checkable 
 194                     // FIXME: Doesn't work (at least on 10.2)! 
 195                     if (item
->IsCheckable()) 
 196                         item
->Check( !item
->IsChecked() ); 
 198                     // send the wxEvent to the wxMenu 
 199                     item
->GetMenu()->SendEvent( id
, item
->IsCheckable() ? item
->IsChecked() : -1 ); 
 201                     // successfully handled the event 
 205         } //end if noErr on getting HICommand from event 
 207         // return whether we handled the event or not 
 211     // We better have a kEventClassApplication/kEventAppGetDockTileMenu combo here, 
 212     // otherwise something is truly funky 
 213     wxASSERT(eventClass 
== kEventClassApplication 
&& 
 214              eventKind 
== kEventAppGetDockTileMenu
); 
 216     // process the right click events 
 217     // NB: This may result in double or even triple-creation of the menus 
 218     // We need to do this for 2.4 compat, however 
 219     wxTaskBarIconEvent 
downevt(wxEVT_TASKBAR_RIGHT_DOWN
, NULL
); 
 220     pTB
->m_parent
->ProcessEvent(downevt
); 
 222     wxTaskBarIconEvent 
upevt(wxEVT_TASKBAR_RIGHT_UP
, NULL
); 
 223     pTB
->m_parent
->ProcessEvent(upevt
); 
 226     wxMenu
* menu 
= pTB
->DoCreatePopupMenu(); 
 228     OSStatus err 
= eventNotHandledErr
; 
 232         // note to self - a MenuRef *is* a MenuHandle 
 233         MenuRef hMenu 
= MAC_WXHMENU(menu
->GetHMenu()); 
 235         // When SetEventParameter is called it will decrement 
 236         // the reference count of the menu - we need to make 
 237         // sure it stays around in the wxMenu class here 
 240         // set the actual dock menu 
 241         err 
= SetEventParameter( 
 242             inEvent
, kEventParamMenuRef
, 
 243             typeMenuRef
, sizeof(MenuRef
), &hMenu 
); 
 250 //----------------------------------------------------------------------------- 
 253 // Performs a top-to-bottom copy of the input menu and all of its 
 256 // This is mostly needed for 2.4 compatability. However wxPython and others 
 257 // still use this way of setting the taskbarmenu. 
 258 //----------------------------------------------------------------------------- 
 259 wxMenu 
* wxDeepCopyMenu( wxMenu 
*menu 
) 
 264     // NB:  Here we have to perform a deep copy of the menu, 
 265     // copying each and every menu item from menu to m_pMenu. 
 266     // Other implementations use wxWindow::PopupMenu here, 
 267     // which idle execution until the user selects something, 
 268     // but since the Mac handles this internally, we can't - 
 269     // and have no way at all to idle it while the dock menu 
 270     // is being shown before menu goes out of scope (it may 
 271     // not be on the heap, and may expire right after this function 
 272     // is done - we need it to last until the carbon event is triggered - 
 273     // that's when the user right clicks). 
 275     // Also, since there is no equal (assignment) operator 
 276     // on either wxMenu or wxMenuItem, we have to do all the 
 277     // dirty work ourselves. 
 279     // perform a deep copy of the menu 
 280     wxMenuItemList
& theList 
= menu
->GetMenuItems(); 
 281     wxMenuItemList::compatibility_iterator theNode 
= theList
.GetFirst(); 
 283     // create the main menu 
 284     wxMenu 
*m_pMenu 
= new wxMenu(menu
->GetTitle()); 
 286     while (theNode 
!= NULL
) 
 288         wxMenuItem
* theItem 
= theNode
->GetData(); 
 291                 m_pMenu
, // parent menu 
 292                 theItem
->GetId(), // id 
 293                 theItem
->GetText(), // text label 
 294                 theItem
->GetHelp(), // status bar help string 
 295                 theItem
->GetKind(), // menu flags - checkable, separator, etc. 
 296                 wxDeepCopyMenu(theItem
->GetSubMenu()) )); // submenu 
 298         theNode 
= theNode
->GetNext(); 
 304 //----------------------------------------------------------------------------- 
 305 // wxDockTaskBarIcon ctor 
 307 // Initializes the dock implementation of wxTaskBarIcon. 
 309 // Here we create some Mac-specific event handlers and UPPs. 
 310 //----------------------------------------------------------------------------- 
 311 wxDockTaskBarIcon::wxDockTaskBarIcon(wxTaskBarIcon
* parent
) 
 312     :   wxTaskBarIconImpl(parent
), 
 313         m_eventHandlerRef(NULL
), m_pMenu(NULL
), 
 314         m_theLastMenu(GetApplicationDockTileMenu()), m_iconAdded(false) 
 316     // register the events that will return the dock menu 
 317     EventTypeSpec tbEventList
[] = 
 319         { kEventClassCommand
, kEventProcessCommand 
}, 
 320         { kEventClassApplication
, kEventAppGetDockTileMenu 
} 
 323     m_eventupp 
= NewEventHandlerUPP(wxDockEventHandler
); 
 324     wxASSERT(m_eventupp 
!= NULL
); 
 326     OSStatus err 
= InstallApplicationEventHandler( 
 328             GetEventTypeCount(tbEventList
), tbEventList
, 
 329             this, &m_eventHandlerRef
); 
 333 //----------------------------------------------------------------------------- 
 334 // wxDockTaskBarIcon Destructor 
 336 // Cleans up mac events and restores the old icon to the dock 
 337 //----------------------------------------------------------------------------- 
 338 wxDockTaskBarIcon::~wxDockTaskBarIcon() 
 340     // clean up event handler and event UPP 
 341     RemoveEventHandler(m_eventHandlerRef
); 
 342     DisposeEventHandlerUPP(m_eventupp
); 
 344     // restore old icon and menu to the dock 
 348 //----------------------------------------------------------------------------- 
 349 // wxDockTaskBarIcon::DoCreatePopupMenu 
 351 // Helper function that handles a request from the dock event handler 
 352 // to get the menu for the dock 
 353 //----------------------------------------------------------------------------- 
 354 wxMenu 
* wxDockTaskBarIcon::DoCreatePopupMenu() 
 356     // get the menu from the parent 
 357     wxMenu
* theNewMenu 
= CreatePopupMenu(); 
 363         m_pMenu 
= theNewMenu
; 
 364         m_pMenu
->SetInvokingWindow(m_menuEventWindow
); 
 367     // the return here can be one of three things 
 368     // (in order of priority): 
 369     // 1) User passed a menu from CreatePopupMenu override 
 370     // 2) menu sent to and copied from PopupMenu 
 371     // 3) If neither (1) or (2), then NULL 
 376 //----------------------------------------------------------------------------- 
 377 // wxDockTaskBarIcon::IsIconInstalled 
 379 // Returns whether or not the dock is not using the default image 
 380 //----------------------------------------------------------------------------- 
 381 bool wxDockTaskBarIcon::IsIconInstalled() const 
 386 //----------------------------------------------------------------------------- 
 387 // wxDockTaskBarIcon::SetIcon 
 389 // Sets the icon for the dock CGImage functions and SetApplicationDockTileImage 
 390 //----------------------------------------------------------------------------- 
 391 bool wxDockTaskBarIcon::SetIcon(const wxIcon
& icon
, const wxString
& tooltip
) 
 393     // convert the wxIcon into a wxBitmap so we can perform some 
 394     // wxBitmap operations with it 
 395     wxBitmap 
bmp( icon 
); 
 396     wxASSERT( bmp
.Ok() ); 
 398     // get the CGImageRef for the wxBitmap: 
 399     // OSX builds only, but then the dock only exists in OSX 
 400     CGImageRef pImage 
= (CGImageRef
) bmp
.CGImageCreate(); 
 401     wxASSERT( pImage 
!= NULL 
); 
 403     // actually set the dock image 
 404     OSStatus err 
= SetApplicationDockTileImage( pImage 
); 
 407     // free the CGImage, now that it's referenced by the dock 
 409         CGImageRelease( pImage 
); 
 411     bool success 
= (err 
== noErr
); 
 412     m_iconAdded 
= success
; 
 417 //----------------------------------------------------------------------------- 
 418 // wxDockTaskBarIcon::RemoveIcon 
 420 // Restores the old image for the dock via RestoreApplicationDockTileImage 
 421 //----------------------------------------------------------------------------- 
 422 bool wxDockTaskBarIcon::RemoveIcon() 
 430     // restore old icon to the dock 
 431     OSStatus err 
= RestoreApplicationDockTileImage(); 
 434     // restore the old menu to the dock 
 435     SetApplicationDockTileMenu( m_theLastMenu 
); 
 437     bool success 
= (err 
== noErr
); 
 438     m_iconAdded 
= !success
; 
 443 //----------------------------------------------------------------------------- 
 444 // wxDockTaskBarIcon::PopupMenu 
 446 // 2.4 and wxPython method that "pops of the menu in the taskbar". 
 448 // In reality because of the way the dock menu works in carbon 
 449 // we just save the menu, and if the user didn't override CreatePopupMenu 
 450 // return the menu passed here, thus sort of getting the same effect. 
 451 //----------------------------------------------------------------------------- 
 452 bool wxDockTaskBarIcon::PopupMenu(wxMenu 
*menu
) 
 454     wxASSERT(menu 
!= NULL
); 
 459     // start copy of menu 
 460     m_pMenu 
= wxDeepCopyMenu(menu
); 
 463     m_pMenu
->SetInvokingWindow(m_menuEventWindow
); 
 468 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
 472 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
 474 IMPLEMENT_DYNAMIC_CLASS(wxTaskBarIcon
, wxEvtHandler
) 
 476 //----------------------------------------------------------------------------- 
 477 // wxTaskBarIcon Constructor 
 479 // Creates the backend 
 481 // Note that we only support DOCK currently as others require cocoa and 
 482 // also some require hacks and other such things. (MenuExtras are 
 483 // actually seperate programs that also require a special undocumented id 
 484 // hack and other such fun stuff). 
 485 //----------------------------------------------------------------------------- 
 486 wxTaskBarIcon::wxTaskBarIcon(wxTaskBarIconType nType
) 
 490         wxT("Only the DOCK implementation of wxTaskBarIcon on Mac-Carbon is currently supported!") ); 
 492     m_impl 
= new wxDockTaskBarIcon(this); 
 495 //----------------------------------------------------------------------------- 
 496 // wxTaskBarIcon Destructor 
 498 // Destroys the backend 
 499 //----------------------------------------------------------------------------- 
 500 wxTaskBarIcon::~wxTaskBarIcon() 
 505 //----------------------------------------------------------------------------- 
 506 // wxTaskBarIcon::SetIcon 
 507 // wxTaskBarIcon::RemoveIcon 
 508 // wxTaskBarIcon::PopupMenu 
 510 // Just calls the backend version of the said function. 
 511 //----------------------------------------------------------------------------- 
 512 bool wxTaskBarIcon::IsIconInstalled() const 
 513 { return m_impl
->IsIconInstalled(); } 
 515 bool wxTaskBarIcon::SetIcon(const wxIcon
& icon
, const wxString
& tooltip
) 
 516 { return m_impl
->SetIcon(icon
, tooltip
); } 
 518 bool wxTaskBarIcon::RemoveIcon() 
 519 { return m_impl
->RemoveIcon(); } 
 521 bool wxTaskBarIcon::PopupMenu(wxMenu 
*menu
) 
 522 { return m_impl
->PopupMenu(menu
); } 
 524 #endif // wxHAS_TASK_BAR_ICON