1 ///////////////////////////////////////////////////////////////////////////////
3 // Purpose: wxTaskBarIcon - OSX implementation
8 // Copyright: (c) 2004 Ryan Norton
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
12 //=============================================================================
14 //=============================================================================
16 //-----------------------------------------------------------------------------
18 //-----------------------------------------------------------------------------
20 #include "wx/wxprec.h"
22 #ifdef wxHAS_TASK_BAR_ICON
24 #include "wx/mac/private.h"
26 #include "wx/taskbar.h"
29 #include "wx/dcmemory.h"
31 //-----------------------------------------------------------------------------
35 // Superclass of wxTaskBarIcon implementations
36 //-----------------------------------------------------------------------------
38 class wxTaskBarIconImpl
41 wxTaskBarIconImpl(wxTaskBarIcon
* parent
);
42 virtual ~wxTaskBarIconImpl();
44 virtual bool IsIconInstalled() const = 0;
45 virtual bool SetIcon(const wxIcon
& icon
, const wxString
& tooltip
) = 0;
46 virtual bool RemoveIcon() = 0;
47 virtual bool PopupMenu(wxMenu
*menu
) = 0;
49 wxMenu
* CreatePopupMenu()
50 { return m_parent
->CreatePopupMenu(); }
52 wxTaskBarIcon
* m_parent
;
53 class wxTaskBarIconWindow
* m_menuEventWindow
;
55 DECLARE_NO_COPY_CLASS(wxTaskBarIconImpl
)
58 //-----------------------------------------------------------------------------
60 // wxTaskBarIconWindow
62 // Event handler for menus
63 // NB: Since wxWindows in mac HAVE to have parents we need this to be
64 // a top level window...
65 //-----------------------------------------------------------------------------
67 class wxTaskBarIconWindow
: public wxTopLevelWindow
70 wxTaskBarIconWindow(wxTaskBarIconImpl
* impl
)
71 : wxTopLevelWindow(NULL
, -1, wxT("")), m_impl(impl
)
73 Connect(-1, wxEVT_COMMAND_MENU_SELECTED
,
74 wxCommandEventHandler(wxTaskBarIconWindow::OnMenuEvent
)
78 void OnMenuEvent(wxCommandEvent
& event
)
80 m_impl
->m_parent
->ProcessEvent(event
);
84 wxTaskBarIconImpl
* m_impl
;
87 //-----------------------------------------------------------------------------
91 //-----------------------------------------------------------------------------
93 class wxDockTaskBarIcon
: public wxTaskBarIconImpl
96 wxDockTaskBarIcon(wxTaskBarIcon
* parent
);
97 virtual ~wxDockTaskBarIcon();
99 virtual bool IsIconInstalled() const;
100 virtual bool SetIcon(const wxIcon
& icon
, const wxString
& tooltip
);
101 virtual bool RemoveIcon();
102 virtual bool PopupMenu(wxMenu
*menu
);
104 wxMenu
* DoCreatePopupMenu();
106 EventHandlerRef m_eventHandlerRef
;
107 EventHandlerUPP m_eventupp
;
108 wxWindow
* m_eventWindow
;
110 MenuRef m_theLastMenu
;
114 // Forward declarations for utility functions for dock implementation
115 pascal OSStatus
wxDockEventHandler( EventHandlerCallRef inHandlerCallRef
,
116 EventRef inEvent
, void* pData
);
117 wxMenu
* wxDeepCopyMenu(wxMenu
* menu
);
120 //=============================================================================
124 //=============================================================================
126 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
130 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
132 //-----------------------------------------------------------------------------
133 // wxTaskBarIconImpl Constructor
135 // Initializes members and creates the event window
136 //-----------------------------------------------------------------------------
137 wxTaskBarIconImpl::wxTaskBarIconImpl(wxTaskBarIcon
* parent
)
138 : m_parent(parent
), m_menuEventWindow(new wxTaskBarIconWindow(this))
142 //-----------------------------------------------------------------------------
143 // wxTaskBarIconImpl Destructor
145 // Cleans up the event window
146 //-----------------------------------------------------------------------------
147 wxTaskBarIconImpl::~wxTaskBarIconImpl()
149 delete m_menuEventWindow
;
152 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
156 // OS X DOCK implementation of wxTaskBarIcon using carbon
157 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
159 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
160 // wxDockEventHandler
162 // This is the global mac/carbon event handler for the dock.
163 // We need this for two reasons:
164 // 1) To handle wxTaskBarIcon menu events (see below for why)
165 // 2) To handle events from the dock when it requests a menu
166 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
167 pascal OSStatus
wxDockEventHandler( EventHandlerCallRef inHandlerCallRef
,
168 EventRef inEvent
, void* pData
)
170 // Get the parameters we want from the event
171 wxDockTaskBarIcon
* pTB
= (wxDockTaskBarIcon
*) pData
;
172 const UInt32 eventClass
= GetEventClass(inEvent
);
173 const UInt32 eventKind
= GetEventKind(inEvent
);
175 // Handle wxTaskBar menu events (note that this is a global event handler
176 // so it will actually get called by all commands/menus)
178 if ((eventClass
== kEventClassCommand
) && (eventKind
== kEventCommandProcess
))
180 // if we have no taskbar menu quickly pass it back to wxApp
181 if (pTB
->m_pMenu
== NULL
)
182 return eventNotHandledErr
;
185 // This is the real reason why we need this. Normally menus
186 // get handled in wxMacAppEventHandler
188 // pascal OSStatus wxMacAppEventHandler(EventHandlerCallRef handler,
189 // EventRef event, void *data)
191 // However, in the case of a taskbar menu call
192 // command.menu.menuRef IS NULL!
193 // Which causes the wxApp handler just to skip it.
195 MenuRef taskbarMenuRef
= MAC_WXHMENU(pTB
->m_pMenu
->GetHMenu());
196 OSStatus result
= eventNotHandledErr
;
199 // get the HICommand from the event
201 err
= GetEventParameter(inEvent
, kEventParamDirectObject
,
203 sizeof(HICommand
), NULL
, &command
);
206 // Obtain the REAL menuRef and the menuItemIndex in the real menuRef
208 // NOTE: menuRef is generally used here for submenus, as
209 // GetMenuItemRefCon could give an incorrect wxMenuItem if we pass
210 // just the top level wxTaskBar menu
212 MenuItemIndex menuItemIndex
;
215 err
= GetIndMenuItemWithCommandID(taskbarMenuRef
,
217 1, &menuRef
, &menuItemIndex
);
220 MenuCommand id
= command
.commandID
;
221 wxMenuItem
* item
= NULL
;
223 if (id
!= 0) // get the wxMenuItem reference from the MenuRef
224 GetMenuItemRefCon(menuRef
, menuItemIndex
, (UInt32
*) &item
);
228 // Handle items that are checkable
229 // FIXME: Doesn't work (at least on 10.2)!
230 if (item
->IsCheckable())
231 item
->Check( !item
->IsChecked() ) ;
233 // send the wxEvent to the wxMenu
234 item
->GetMenu()->SendEvent(id
,
235 item
->IsCheckable() ?
236 item
->IsChecked() : -1
238 err
= noErr
; // successfully handled the event
241 } //end if noErr on getting HICommand from event
243 // return whether we handled the event or not
247 // We better have a kEventClassApplication/kEventAppGetDockTileMenu combo here,
248 // otherwise something is truly funky
249 wxASSERT(eventClass
== kEventClassApplication
&&
250 eventKind
== kEventAppGetDockTileMenu
);
252 // process the right click events
253 // NB: This may result in double or even triple-creation of the menus
254 // We need to do this for 2.4 compat, however
255 wxTaskBarIconEvent
downevt(wxEVT_TASKBAR_RIGHT_DOWN
,NULL
);
256 pTB
->m_parent
->ProcessEvent(downevt
);
258 wxTaskBarIconEvent
upevt(wxEVT_TASKBAR_RIGHT_UP
,NULL
);
259 pTB
->m_parent
->ProcessEvent(upevt
);
262 wxMenu
* menu
= pTB
->DoCreatePopupMenu();
264 OSStatus err
= eventNotHandledErr
;
268 // note to self - a MenuRef *is* a MenuHandle
269 MenuRef hMenu
= MAC_WXHMENU(menu
->GetHMenu());
271 // When SetEventParameter is called it will decrement
272 // the reference count of the menu - we need to make
273 // sure it stays around in the wxMenu class here
276 // set the actual dock menu
277 err
= SetEventParameter(inEvent
, kEventParamMenuRef
,
278 typeMenuRef
, sizeof(MenuRef
), &hMenu
);
279 wxASSERT(err
== noErr
);
285 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
288 // Performs a top-to-bottom copy of the input menu and all of its
291 // This is mostly needed for 2.4 compatability. However wxPython and others
292 // still use this way of setting the taskbarmenu.
293 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
294 wxMenu
* wxDeepCopyMenu(wxMenu
* menu
)
300 // NB: Here we have to perform a deep copy of the menu,
301 // copying each and every menu item from menu to m_pMenu.
302 // Other implementations use wxWindow::PopupMenu here,
303 // which idle execution until the user selects something,
304 // but since the mac handles this internally, we can't -
305 // and have no way at all to idle it while the dock menu
306 // is being shown before menu goes out of scope (it may
307 // not be on the heap, and may expire right after this function
308 // is done - we need it to last until the carbon event is triggered -
309 // that's when the user right clicks).
311 // Also, since there is no equal (assignment) operator
312 // on either wxMenu or wxMenuItem, we have to do all the
313 // dirty work ourselves.
316 // perform a deep copy of the menu
317 wxMenuItemList
& theList
= menu
->GetMenuItems();
318 wxMenuItemList::compatibility_iterator theNode
= theList
.GetFirst();
320 // create the main menu
321 wxMenu
* m_pMenu
= new wxMenu(menu
->GetTitle());
323 while (theNode
!= NULL
)
325 wxMenuItem
* theItem
= theNode
->GetData();
328 m_pMenu
, // parent menu
329 theItem
->GetId(), // id
330 theItem
->GetText(), // text label
331 theItem
->GetHelp(), // status bar help string
332 theItem
->GetKind(), // menu flags - checkable, separator, etc.
333 wxDeepCopyMenu(theItem
->GetSubMenu()) // submenu
335 theNode
= theNode
->GetNext();
341 //-----------------------------------------------------------------------------
342 // wxDockTaskBarIcon ctor
344 // Initializes the dock implementation of wxTaskBarIcon.
346 // Here we create some mac-specific event handlers and UPPs.
347 //-----------------------------------------------------------------------------
348 wxDockTaskBarIcon::wxDockTaskBarIcon(wxTaskBarIcon
* parent
)
349 : wxTaskBarIconImpl(parent
),
350 m_eventHandlerRef(NULL
), m_pMenu(NULL
),
351 m_theLastMenu(GetApplicationDockTileMenu()), m_iconAdded(false)
353 // register the events that will return the dock menu
354 EventTypeSpec tbEventList
[] =
356 { kEventClassCommand
, kEventProcessCommand
},
357 { kEventClassApplication
, kEventAppGetDockTileMenu
}
360 m_eventupp
= NewEventHandlerUPP(wxDockEventHandler
);
361 wxASSERT(m_eventupp
!= NULL
);
366 InstallApplicationEventHandler(
368 GetEventTypeCount(tbEventList
), tbEventList
,
369 this, &m_eventHandlerRef
);
370 wxASSERT( err
== noErr
);
373 //-----------------------------------------------------------------------------
374 // wxDockTaskBarIcon Destructor
376 // Cleans up mac events and restores the old icon to the dock
377 //-----------------------------------------------------------------------------
378 wxDockTaskBarIcon::~wxDockTaskBarIcon()
380 // clean up event handler and event UPP
381 RemoveEventHandler(m_eventHandlerRef
);
382 DisposeEventHandlerUPP(m_eventupp
);
384 // restore old icon and menu to the dock
388 //-----------------------------------------------------------------------------
389 // wxDockTaskBarIcon::DoCreatePopupMenu
391 // Helper function that handles a request from the dock event handler
392 // to get the menu for the dock
393 //-----------------------------------------------------------------------------
394 wxMenu
* wxDockTaskBarIcon::DoCreatePopupMenu()
396 // get the menu from the parent
397 wxMenu
* theNewMenu
= CreatePopupMenu();
403 m_pMenu
= theNewMenu
;
404 m_pMenu
->SetInvokingWindow(m_menuEventWindow
);
407 // the return here can be one of three things
408 // (in order of priority):
409 // 1) User passed a menu from CreatePopupMenu override
410 // 2) menu sent to and copied from PopupMenu
411 // 3) If neither (1) or (2), then NULL
416 //-----------------------------------------------------------------------------
417 // wxDockTaskBarIcon::IsIconInstalled
419 // Returns whether or not the dock is not using the default image
420 //-----------------------------------------------------------------------------
421 bool wxDockTaskBarIcon::IsIconInstalled() const
426 //-----------------------------------------------------------------------------
427 // wxDockTaskBarIcon::SetIcon
429 // Sets the icon for the dock CGImage functions and SetApplicationDockTileImage
430 //-----------------------------------------------------------------------------
431 bool wxDockTaskBarIcon::SetIcon(const wxIcon
& icon
, const wxString
& tooltip
)
433 // convert the wxIcon into a wxBitmap so we can perform some
434 // wxBitmap operations with it
435 wxBitmap
bmp( icon
) ;
436 wxASSERT( bmp
.Ok() );
438 // get the CGImageRef for the wxBitmap:
439 // OSX builds only, but then the dock only exists in OSX
440 CGImageRef pImage
= (CGImageRef
) bmp
.CGImageCreate();
441 wxASSERT( pImage
!= NULL
);
443 // actually set the dock image
444 OSStatus err
= SetApplicationDockTileImage( pImage
);
445 wxASSERT( err
== noErr
);
447 // free the CGImage, now that it's referenced by the dock
449 CGImageRelease( pImage
);
451 bool success
= (err
== noErr
);
452 m_iconAdded
= success
;
457 //-----------------------------------------------------------------------------
458 // wxDockTaskBarIcon::RemoveIcon
460 // Restores the old image for the dock via RestoreApplicationDockTileImage
461 //-----------------------------------------------------------------------------
462 bool wxDockTaskBarIcon::RemoveIcon()
470 // restore old icon to the dock
471 OSStatus err
= RestoreApplicationDockTileImage();
472 wxASSERT(err
== noErr
);
474 // restore the old menu to the dock
475 SetApplicationDockTileMenu(m_theLastMenu
);
477 bool success
= (err
== noErr
);
478 m_iconAdded
= !success
;
483 //-----------------------------------------------------------------------------
484 // wxDockTaskBarIcon::PopupMenu
486 // 2.4 and wxPython method that "pops of the menu in the taskbar".
488 // In reality because of the way the dock menu works in carbon
489 // we just save the menu, and if the user didn't override CreatePopupMenu
490 // return the menu passed here, thus sort of getting the same effect.
491 //-----------------------------------------------------------------------------
492 bool wxDockTaskBarIcon::PopupMenu(wxMenu
*menu
)
494 wxASSERT(menu
!= NULL
);
500 m_pMenu
= wxDeepCopyMenu(menu
);
503 m_pMenu
->SetInvokingWindow(m_menuEventWindow
);
508 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
512 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
514 IMPLEMENT_DYNAMIC_CLASS(wxTaskBarIcon
, wxEvtHandler
)
516 //-----------------------------------------------------------------------------
517 // wxTaskBarIcon Constructor
519 // Creates the backend
521 // Note that we only support DOCK currently as others require cocoa and
522 // also some require hacks and other such things. (MenuExtras are
523 // actually seperate programs that also require a special undocumented id
524 // hack and other such fun stuff).
525 //-----------------------------------------------------------------------------
526 wxTaskBarIcon::wxTaskBarIcon(wxTaskBarIconType nType
)
528 wxASSERT_MSG(nType
== DOCK
,
529 wxT("Only the DOCK implementation of wxTaskBarIcon")
530 wxT("on mac carbon is currently supported!"));
531 m_impl
= new wxDockTaskBarIcon(this);
534 //-----------------------------------------------------------------------------
535 // wxTaskBarIcon Destructor
537 // Destroys the backend
538 //-----------------------------------------------------------------------------
539 wxTaskBarIcon::~wxTaskBarIcon()
544 //-----------------------------------------------------------------------------
545 // wxTaskBarIcon::SetIcon
546 // wxTaskBarIcon::RemoveIcon
547 // wxTaskBarIcon::PopupMenu
549 // Just calls the backend version of the said function.
550 //-----------------------------------------------------------------------------
551 bool wxTaskBarIcon::IsIconInstalled() const
552 { return m_impl
->IsIconInstalled(); }
553 bool wxTaskBarIcon::SetIcon(const wxIcon
& icon
, const wxString
& tooltip
)
554 { return m_impl
->SetIcon(icon
, tooltip
); }
555 bool wxTaskBarIcon::RemoveIcon()
556 { return m_impl
->RemoveIcon(); }
557 bool wxTaskBarIcon::PopupMenu(wxMenu
*menu
)
558 { return m_impl
->PopupMenu(menu
); }
560 #endif //wxHAS_TASK_BAR_ICON