X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/a31a5f85341a2ef131d86a1dee12f3d6c8156118..d35ad26eee38a8d71efde5a6e7f7f28f4b7df1bd:/src/mac/carbon/taskbar.cpp diff --git a/src/mac/carbon/taskbar.cpp b/src/mac/carbon/taskbar.cpp index 7213103b04..5fea2b7552 100644 --- a/src/mac/carbon/taskbar.cpp +++ b/src/mac/carbon/taskbar.cpp @@ -1,70 +1,521 @@ -///////////////////////////////////////////////////////////////////////// -// File: taskbar.cpp -// Purpose: Implements wxTaskBarIcon class for manipulating icons on -// the task bar. Optional. -// Author: Stefan Csomor +/////////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/taskbar.cpp +// Purpose: wxTaskBarIcon +// Author: Ryan Norton // Modified by: -// Created: 1998-01-01 +// Created: 09/25/2004 // RCS-ID: $Id$ -// Copyright: (c) -// Licence: wxWindows licence -///////////////////////////////////////////////////////////////////////// +// Copyright: (c) 2004 Ryan Norton +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// -#ifdef __GNUG__ -#pragma implementation "taskbar.h" -#endif +#include "wx/wxprec.h" + +#ifdef wxHAS_TASK_BAR_ICON + +#include "wx/mac/private.h" #include "wx/taskbar.h" +#include "wx/menu.h" +#include "wx/icon.h" +#include "wx/dcmemory.h" + + +class wxTaskBarIconImpl +{ +public: + wxTaskBarIconImpl(wxTaskBarIcon* parent); + virtual ~wxTaskBarIconImpl(); + + virtual bool IsIconInstalled() const = 0; + virtual bool SetIcon(const wxIcon& icon, const wxString& tooltip) = 0; + virtual bool RemoveIcon() = 0; + virtual bool PopupMenu(wxMenu *menu) = 0; + + wxMenu * CreatePopupMenu() + { return m_parent->CreatePopupMenu(); } + + wxTaskBarIcon *m_parent; + class wxTaskBarIconWindow *m_menuEventWindow; + + DECLARE_NO_COPY_CLASS(wxTaskBarIconImpl) +}; + +//----------------------------------------------------------------------------- +// +// wxTaskBarIconWindow +// +// Event handler for menus +// NB: Since wxWindows in Mac HAVE to have parents we need this to be +// a top level window... +//----------------------------------------------------------------------------- + +class wxTaskBarIconWindow : public wxTopLevelWindow +{ +public: + wxTaskBarIconWindow(wxTaskBarIconImpl *impl) + : wxTopLevelWindow(NULL, -1, wxT("")), m_impl(impl) + { + Connect( + -1, wxEVT_COMMAND_MENU_SELECTED, + wxCommandEventHandler(wxTaskBarIconWindow::OnMenuEvent) ); + } + + void OnMenuEvent(wxCommandEvent& event) + { + m_impl->m_parent->ProcessEvent(event); + } + +private: + wxTaskBarIconImpl *m_impl; +}; + +class wxDockTaskBarIcon : public wxTaskBarIconImpl +{ +public: + wxDockTaskBarIcon(wxTaskBarIcon* parent); + virtual ~wxDockTaskBarIcon(); + + virtual bool IsIconInstalled() const; + virtual bool SetIcon(const wxIcon& icon, const wxString& tooltip); + virtual bool RemoveIcon(); + virtual bool PopupMenu(wxMenu *menu); + + wxMenu* DoCreatePopupMenu(); + + EventHandlerRef m_eventHandlerRef; + EventHandlerUPP m_eventupp; + wxWindow *m_eventWindow; + wxMenu *m_pMenu; + MenuRef m_theLastMenu; + bool m_iconAdded; +}; + +// Forward declarations for utility functions for dock implementation +pascal OSStatus wxDockEventHandler( + EventHandlerCallRef inHandlerCallRef, + EventRef inEvent, void* pData ); +wxMenu * wxDeepCopyMenu( wxMenu *menu ); + -wxTaskBarIcon::wxTaskBarIcon() +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// +// wxTaskBarIconImpl +// +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +wxTaskBarIconImpl::wxTaskBarIconImpl(wxTaskBarIcon* parent) + : m_parent(parent), m_menuEventWindow(new wxTaskBarIconWindow(this)) { - // TODO } -wxTaskBarIcon::~wxTaskBarIcon() +wxTaskBarIconImpl::~wxTaskBarIconImpl() { - // TODO + delete m_menuEventWindow; } -// Operations -bool wxTaskBarIcon::SetIcon(const wxIcon& icon, const wxString& tooltip) +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// +// wxDockTaskBarIcon +// +// OS X Dock implementation of wxTaskBarIcon using Carbon +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +//----------------------------------------------------------------------------- +// wxDockEventHandler +// +// This is the global Mac/Carbon event handler for the dock. +// We need this for two reasons: +// 1) To handle wxTaskBarIcon menu events (see below for why) +// 2) To handle events from the dock when it requests a menu +//----------------------------------------------------------------------------- +pascal OSStatus wxDockEventHandler( EventHandlerCallRef inHandlerCallRef, + EventRef inEvent, void *pData ) +{ + // Get the parameters we want from the event + wxDockTaskBarIcon* pTB = (wxDockTaskBarIcon*) pData; + const UInt32 eventClass = GetEventClass(inEvent); + const UInt32 eventKind = GetEventKind(inEvent); + + // Handle wxTaskBar menu events (note that this is a global event handler + // so it will actually get called by all commands/menus) + if ((eventClass == kEventClassCommand) && (eventKind == kEventCommandProcess)) + { + // if we have no taskbar menu quickly pass it back to wxApp + if (pTB->m_pMenu == NULL) + return eventNotHandledErr; + + // This is the real reason why we need this. Normally menus + // get handled in wxMacAppEventHandler + // + // pascal OSStatus wxMacAppEventHandler(EventHandlerCallRef handler, + // EventRef event, void *data) + // + // However, in the case of a taskbar menu call + // command.menu.menuRef IS NULL! + // Which causes the wxApp handler just to skip it. + MenuRef taskbarMenuRef = MAC_WXHMENU(pTB->m_pMenu->GetHMenu()); + OSStatus err; + + // get the HICommand from the event + HICommand command; + err = GetEventParameter( + inEvent, kEventParamDirectObject, + typeHICommand, NULL, + sizeof(HICommand), NULL, &command ); + if (err == noErr) + { + // Obtain the REAL menuRef and the menuItemIndex in the real menuRef + // + // NOTE: menuRef is generally used here for submenus, as + // GetMenuItemRefCon could give an incorrect wxMenuItem if we pass + // just the top level wxTaskBar menu + MenuItemIndex menuItemIndex; + MenuRef menuRef; + + err = GetIndMenuItemWithCommandID( + taskbarMenuRef, + command.commandID, + 1, &menuRef, &menuItemIndex ); + if (err == noErr) + { + MenuCommand id = command.commandID; + wxMenuItem *item = NULL; + + if (id != 0) // get the wxMenuItem reference from the MenuRef + GetMenuItemRefCon( menuRef, menuItemIndex, (UInt32*) &item ); + + if (item) + { + // Handle items that are checkable + // FIXME: Doesn't work (at least on 10.2)! + if (item->IsCheckable()) + item->Check( !item->IsChecked() ); + + // send the wxEvent to the wxMenu + item->GetMenu()->SendEvent( id, item->IsCheckable() ? item->IsChecked() : -1 ); + + // successfully handled the event + err = noErr; + } + } + } //end if noErr on getting HICommand from event + + // return whether we handled the event or not + return err; + } + + // We better have a kEventClassApplication/kEventAppGetDockTileMenu combo here, + // otherwise something is truly funky + wxASSERT(eventClass == kEventClassApplication && + eventKind == kEventAppGetDockTileMenu); + + // process the right click events + // NB: This may result in double or even triple-creation of the menus + // We need to do this for 2.4 compat, however + wxTaskBarIconEvent downevt(wxEVT_TASKBAR_RIGHT_DOWN, NULL); + pTB->m_parent->ProcessEvent(downevt); + + wxTaskBarIconEvent upevt(wxEVT_TASKBAR_RIGHT_UP, NULL); + pTB->m_parent->ProcessEvent(upevt); + + // create popup menu + wxMenu* menu = pTB->DoCreatePopupMenu(); + + OSStatus err = eventNotHandledErr; + + if (menu != NULL) + { + // note to self - a MenuRef *is* a MenuHandle + MenuRef hMenu = MAC_WXHMENU(menu->GetHMenu()); + + // When SetEventParameter is called it will decrement + // the reference count of the menu - we need to make + // sure it stays around in the wxMenu class here + RetainMenu(hMenu); + + // set the actual dock menu + err = SetEventParameter( + inEvent, kEventParamMenuRef, + typeMenuRef, sizeof(MenuRef), &hMenu ); + verify_noerr( err ); + } + + return err; +} + +//----------------------------------------------------------------------------- +// wxDeepCopyMenu +// +// Performs a top-to-bottom copy of the input menu and all of its +// submenus. +// +// This is mostly needed for 2.4 compatability. However wxPython and others +// still use this way of setting the taskbarmenu. +//----------------------------------------------------------------------------- +wxMenu * wxDeepCopyMenu( wxMenu *menu ) { - // TODO - return FALSE; + if (menu == NULL) + return NULL; + + // NB: Here we have to perform a deep copy of the menu, + // copying each and every menu item from menu to m_pMenu. + // Other implementations use wxWindow::PopupMenu here, + // which idle execution until the user selects something, + // but since the Mac handles this internally, we can't - + // and have no way at all to idle it while the dock menu + // is being shown before menu goes out of scope (it may + // not be on the heap, and may expire right after this function + // is done - we need it to last until the carbon event is triggered - + // that's when the user right clicks). + // + // Also, since there is no equal (assignment) operator + // on either wxMenu or wxMenuItem, we have to do all the + // dirty work ourselves. + + // perform a deep copy of the menu + wxMenuItemList& theList = menu->GetMenuItems(); + wxMenuItemList::compatibility_iterator theNode = theList.GetFirst(); + + // create the main menu + wxMenu *m_pMenu = new wxMenu(menu->GetTitle()); + + while (theNode != NULL) + { + wxMenuItem* theItem = theNode->GetData(); + m_pMenu->Append( + new wxMenuItem( + m_pMenu, // parent menu + theItem->GetId(), // id + theItem->GetText(), // text label + theItem->GetHelp(), // status bar help string + theItem->GetKind(), // menu flags - checkable, separator, etc. + wxDeepCopyMenu(theItem->GetSubMenu()) )); // submenu + + theNode = theNode->GetNext(); + } + + return m_pMenu; } -bool wxTaskBarIcon::RemoveIcon() +//----------------------------------------------------------------------------- +// wxDockTaskBarIcon ctor +// +// Initializes the dock implementation of wxTaskBarIcon. +// +// Here we create some Mac-specific event handlers and UPPs. +//----------------------------------------------------------------------------- +wxDockTaskBarIcon::wxDockTaskBarIcon(wxTaskBarIcon* parent) + : wxTaskBarIconImpl(parent), + m_eventHandlerRef(NULL), m_pMenu(NULL), + m_theLastMenu(GetApplicationDockTileMenu()), m_iconAdded(false) { - // TODO - return FALSE; + // register the events that will return the dock menu + EventTypeSpec tbEventList[] = + { + { kEventClassCommand, kEventProcessCommand }, + { kEventClassApplication, kEventAppGetDockTileMenu } + }; + + m_eventupp = NewEventHandlerUPP(wxDockEventHandler); + wxASSERT(m_eventupp != NULL); + + OSStatus err = InstallApplicationEventHandler( + m_eventupp, + GetEventTypeCount(tbEventList), tbEventList, + this, &m_eventHandlerRef); + verify_noerr( err ); +} + +//----------------------------------------------------------------------------- +// wxDockTaskBarIcon Destructor +// +// Cleans up mac events and restores the old icon to the dock +//----------------------------------------------------------------------------- +wxDockTaskBarIcon::~wxDockTaskBarIcon() +{ + // clean up event handler and event UPP + RemoveEventHandler(m_eventHandlerRef); + DisposeEventHandlerUPP(m_eventupp); + + // restore old icon and menu to the dock + RemoveIcon(); } -// Overridables -void wxTaskBarIcon::OnMouseMove() +//----------------------------------------------------------------------------- +// wxDockTaskBarIcon::DoCreatePopupMenu +// +// Helper function that handles a request from the dock event handler +// to get the menu for the dock +//----------------------------------------------------------------------------- +wxMenu * wxDockTaskBarIcon::DoCreatePopupMenu() { + // get the menu from the parent + wxMenu* theNewMenu = CreatePopupMenu(); + + if (theNewMenu) + { + if (m_pMenu) + delete m_pMenu; + m_pMenu = theNewMenu; + m_pMenu->SetInvokingWindow(m_menuEventWindow); + } + + // the return here can be one of three things + // (in order of priority): + // 1) User passed a menu from CreatePopupMenu override + // 2) menu sent to and copied from PopupMenu + // 3) If neither (1) or (2), then NULL + // + return m_pMenu; } -void wxTaskBarIcon::OnLButtonDown() +//----------------------------------------------------------------------------- +// wxDockTaskBarIcon::IsIconInstalled +// +// Returns whether or not the dock is not using the default image +//----------------------------------------------------------------------------- +bool wxDockTaskBarIcon::IsIconInstalled() const { + return m_iconAdded; } -void wxTaskBarIcon::OnLButtonUp() +//----------------------------------------------------------------------------- +// wxDockTaskBarIcon::SetIcon +// +// Sets the icon for the dock CGImage functions and SetApplicationDockTileImage +//----------------------------------------------------------------------------- +bool wxDockTaskBarIcon::SetIcon(const wxIcon& icon, const wxString& tooltip) { + // convert the wxIcon into a wxBitmap so we can perform some + // wxBitmap operations with it + wxBitmap bmp( icon ); + wxASSERT( bmp.Ok() ); + + // get the CGImageRef for the wxBitmap: + // OSX builds only, but then the dock only exists in OSX + CGImageRef pImage = (CGImageRef) bmp.CGImageCreate(); + wxASSERT( pImage != NULL ); + + // actually set the dock image + OSStatus err = SetApplicationDockTileImage( pImage ); + verify_noerr( err ); + + // free the CGImage, now that it's referenced by the dock + if (pImage != NULL) + CGImageRelease( pImage ); + + bool success = (err == noErr); + m_iconAdded = success; + + return success; } -void wxTaskBarIcon::OnRButtonDown() +//----------------------------------------------------------------------------- +// wxDockTaskBarIcon::RemoveIcon +// +// Restores the old image for the dock via RestoreApplicationDockTileImage +//----------------------------------------------------------------------------- +bool wxDockTaskBarIcon::RemoveIcon() { + if (m_pMenu) + { + delete m_pMenu; + m_pMenu = NULL; + } + + // restore old icon to the dock + OSStatus err = RestoreApplicationDockTileImage(); + verify_noerr( err ); + + // restore the old menu to the dock + SetApplicationDockTileMenu( m_theLastMenu ); + + bool success = (err == noErr); + m_iconAdded = !success; + + return success; } -void wxTaskBarIcon::OnRButtonUp() +//----------------------------------------------------------------------------- +// wxDockTaskBarIcon::PopupMenu +// +// 2.4 and wxPython method that "pops of the menu in the taskbar". +// +// In reality because of the way the dock menu works in carbon +// we just save the menu, and if the user didn't override CreatePopupMenu +// return the menu passed here, thus sort of getting the same effect. +//----------------------------------------------------------------------------- +bool wxDockTaskBarIcon::PopupMenu(wxMenu *menu) { + wxASSERT(menu != NULL); + + if (m_pMenu) + delete m_pMenu; + + // start copy of menu + m_pMenu = wxDeepCopyMenu(menu); + + // finish up + m_pMenu->SetInvokingWindow(m_menuEventWindow); + + return true; } -void wxTaskBarIcon::OnLButtonDClick() +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// +// wxTaskBarIcon +// +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +IMPLEMENT_DYNAMIC_CLASS(wxTaskBarIcon, wxEvtHandler) + +//----------------------------------------------------------------------------- +// wxTaskBarIcon Constructor +// +// Creates the backend +// +// Note that we only support DOCK currently as others require cocoa and +// also some require hacks and other such things. (MenuExtras are +// actually seperate programs that also require a special undocumented id +// hack and other such fun stuff). +//----------------------------------------------------------------------------- +wxTaskBarIcon::wxTaskBarIcon(wxTaskBarIconType nType) { + wxASSERT_MSG( + nType == DOCK, + wxT("Only the DOCK implementation of wxTaskBarIcon on Mac-Carbon is currently supported!") ); + + m_impl = new wxDockTaskBarIcon(this); } -void wxTaskBarIcon::OnRButtonDClick() +//----------------------------------------------------------------------------- +// wxTaskBarIcon Destructor +// +// Destroys the backend +//----------------------------------------------------------------------------- +wxTaskBarIcon::~wxTaskBarIcon() { + delete m_impl; } +//----------------------------------------------------------------------------- +// wxTaskBarIcon::SetIcon +// wxTaskBarIcon::RemoveIcon +// wxTaskBarIcon::PopupMenu +// +// Just calls the backend version of the said function. +//----------------------------------------------------------------------------- +bool wxTaskBarIcon::IsIconInstalled() const +{ return m_impl->IsIconInstalled(); } + +bool wxTaskBarIcon::SetIcon(const wxIcon& icon, const wxString& tooltip) +{ return m_impl->SetIcon(icon, tooltip); } + +bool wxTaskBarIcon::RemoveIcon() +{ return m_impl->RemoveIcon(); } + +bool wxTaskBarIcon::PopupMenu(wxMenu *menu) +{ return m_impl->PopupMenu(menu); } + +#endif // wxHAS_TASK_BAR_ICON