/////////////////////////////////////////////////////////////////////////////
// Name:        taskbar.cpp
// Purpose:     wxTaskBarIcon OSX Implementation
// Author:      Ryan Norton
// Modified by:
// Created:     09/25/2004
// RCS-ID:      $Id: taskbar.cpp,v 1.24.2.1 2005/12/19 11:53:42 JS Exp $
// Copyright:   (c) 2004 Ryan Norton
// Licence:     wxWindows licence
/////////////////////////////////////////////////////////////////////////////

#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"

IMPLEMENT_DYNAMIC_CLASS(wxTaskBarIcon, wxEvtHandler)

pascal OSStatus wxDockEventHandler( EventHandlerCallRef inHandlerCallRef,
                                    EventRef inEvent, void* pData)
{
    wxTaskBarIcon*& pTB = (wxTaskBarIcon*&) pData;

    const UInt32 eventClass = GetEventClass(inEvent);
    const UInt32 eventKind = GetEventKind(inEvent);
            
    if (eventClass == kEventClassCommand && eventKind == kEventCommandProcess) 
    {
        //TODO:	This is a complete copy of 
        //static pascal OSStatus wxMacAppCommandEventHandler( EventHandlerCallRef handler , EventRef event , void *data )

        if (! pTB->GetCurrentMenu() )
        {
            return eventNotHandledErr;
        }
                
        MenuRef hMenu = MAC_WXHMENU(pTB->GetCurrentMenu()->GetHMenu());
        OSStatus result = eventNotHandledErr ;

        HICommand command ;
        OSErr err;
    
        err =    GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, 	
                            NULL, sizeof(HICommand), NULL, &command);
        wxASSERT(err == noErr);
                
        MenuItemIndex menuItemIndex;
        err = GetIndMenuItemWithCommandID(hMenu, command.commandID, 1, NULL, &menuItemIndex);
        wxASSERT(err == noErr);
                
                    
        MenuCommand id = command.commandID ;
        wxMenuItem* item = NULL;
        
        // for items we don't really control
        if ( id == kHICommandPreferences )
        {
            id = wxApp::s_macPreferencesMenuItemId ;
        
            wxMenuBar* mbar = wxMenuBar::MacGetInstalledMenuBar() ;
            
            if ( mbar )
            {
                wxMenu* menu = NULL ;
                item = mbar->FindItem( id , &menu ) ;
            }
        }
        else if (id != 0)
            GetMenuItemRefCon( hMenu , menuItemIndex , (UInt32*) &item ) ;

        if ( item )
        {
            if (item->IsCheckable())
            {
                item->Check( !item->IsChecked() ) ;
            }

            item->GetMenu()->SendEvent( id , item->IsCheckable() ? item->IsChecked() : -1 ) ;
            result = noErr ;
        }
    
        return result ;
    }
    
    wxASSERT(eventClass == kEventClassApplication && eventKind == kEventAppGetDockTileMenu);
		    	
	//process the right click events
	wxTaskBarIconEvent downevt(wxEVT_TASKBAR_RIGHT_DOWN,NULL);
	pTB->ProcessEvent(downevt);
		
	wxTaskBarIconEvent upevt(wxEVT_TASKBAR_RIGHT_UP,NULL);
	pTB->ProcessEvent(upevt);

    //create popup menu
    wxMenu* menu = pTB->DoCreatePopupMenu();
    
    OSStatus err = noErr;

    if(menu)
    {
        //note to self - a MenuRef IS A MenuHandle
        MenuRef hMenu = MAC_WXHMENU(menu->GetHMenu());

        //When we call SetEventParameter 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((EventRef) inEvent, kEventParamMenuRef, 
                    typeMenuRef, sizeof(MenuRef), 
                     &hMenu);
        wxASSERT(err == 0);

        return err;
    }
    else
        return eventNotHandledErr;
}

DEFINE_ONE_SHOT_HANDLER_GETTER( wxDockEventHandler );

wxTaskBarIcon::wxTaskBarIcon(const wxTaskBarIconType& nType)
    : m_nType(nType), m_pEventHandlerRef(NULL), m_pMenu(NULL), 
        m_theLastMenu((WXHMENU)GetApplicationDockTileMenu()), m_iconAdded(false) 
{
    //Register the events that will return the dock menu
    EventTypeSpec tbEventList[] = { { kEventClassCommand, kEventProcessCommand },
                                    { kEventClassApplication, kEventAppGetDockTileMenu } };
    
#ifdef __WXDEBUG__
    OSStatus err =
#endif
    InstallApplicationEventHandler(
            GetwxDockEventHandlerUPP(),
            GetEventTypeCount(tbEventList), tbEventList, 
            this, (&(EventHandlerRef&)m_pEventHandlerRef));
            
    wxASSERT(err == noErr);
    
    Connect(wxEVT_TASKBAR_RIGHT_DOWN, wxTaskBarIconEventHandler(wxTaskBarIcon::OnRightDown));
}

wxTaskBarIcon::~wxTaskBarIcon()
{
    //clean up event handler
    RemoveEventHandler((EventHandlerRef&)m_pEventHandlerRef);

    //restore old icon and menu to the dock
    RemoveIcon();    
}

wxMenu* wxTaskBarIcon::GetCurrentMenu()
{
    return m_pMenu;
}

wxMenu* wxTaskBarIcon::DoCreatePopupMenu()
{    
    wxMenu* theNewMenu = CreatePopupMenu();
    
    if (theNewMenu)
    {
        delete m_pMenu;
        m_pMenu = theNewMenu;
        m_pMenu->SetEventHandler(this);
    }
    
    return m_pMenu;
}

// Operations:
bool wxTaskBarIcon::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 ) ;
    OSStatus err = noErr ;

    //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    
    err = SetApplicationDockTileImage(pImage);
    
    wxASSERT(err == 0);
    
    // Free the CGImage
    if (pImage != NULL)
        CGImageRelease(pImage);
    
    return m_iconAdded = err == noErr;
}
    
bool wxTaskBarIcon::RemoveIcon()
{
    if(m_pMenu)
    {
        delete m_pMenu;
        m_pMenu = NULL;
    }
    
    //restore old icon to the dock
	OSStatus err = RestoreApplicationDockTileImage();
	wxASSERT(err == 0);
    
    //restore the old menu to the dock
    SetApplicationDockTileMenu(MAC_WXHMENU(m_theLastMenu));

    return !(m_iconAdded = !(err == noErr));
}
    
bool wxTaskBarIcon::PopupMenu(wxMenu *menu)
{
    wxASSERT(menu != NULL);

    if (m_pMenu)
    {
        delete m_pMenu;
        m_pMenu = 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
    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.
                                        theItem->GetSubMenu() //submenu
                                        ));
        theNode = theNode->GetNext();
    }
    
    m_pMenu->SetEventHandler(this);
    return true;
}

//Skip the event so that popupmenu isn't called in parent, avoiding double-creation of the menus
void wxTaskBarIcon::OnRightDown(wxTaskBarIconEvent& evt)
{
    evt.Skip();
}

#endif //wxHAS_TASK_BAR_ICON
