]> git.saurik.com Git - wxWidgets.git/blobdiff - src/osx/cocoa/taskbar.mm
wiring OnInit on osx to a later point in event processing
[wxWidgets.git] / src / osx / cocoa / taskbar.mm
index b6d327b863f991d1e14d5e71fc240b1bcbd7b354..20d09701ac3b12b29e58fad8e1003e9fe17c5d0a 100644 (file)
@@ -1,18 +1,19 @@
 /////////////////////////////////////////////////////////////////////////
 // File:        src/osx/cocoa/taskbar.mm
 // Purpose:     Implements wxTaskBarIcon class
-// Author:      David Elliott
+// Author:      David Elliott, Stefan Csomor
 // Modified by:
 // Created:     2004/01/24
-// RCS-ID:      $Id: taskbar.mm 35650 2005-09-23 12:56:45Z MR $
-// Copyright:   (c) 2004 David Elliott
+// RCS-ID:      $Id$
+// Copyright:   (c) 2004 David Elliott, Stefan Csomor
 // Licence:     wxWindows licence
 /////////////////////////////////////////////////////////////////////////
 
 #include "wx/wxprec.h"
-#ifdef wxUSE_TASKBARICON
+#if wxUSE_TASKBARICON
 
 #ifndef WX_PRECOMP
+    #include "wx/toplevel.h"
     #include "wx/menu.h"
     #include "wx/icon.h"
     #include "wx/log.h"
 
 #include "wx/osx/private.h"
 
-// A category for methods that are only present in Panther's SDK
-@interface NSStatusItem(wxNSStatusItemPrePantherCompatibility)
-- (void)popUpStatusItemMenu:(NSMenu *)menu;
-@end
-
 class wxTaskBarIconWindow;
 
+//-----------------------------------------------------------------------------
+//
+//  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);
+
+    void OnMenuEvent(wxCommandEvent& event);
+    void OnUpdateUIEvent(wxUpdateUIEvent& event);
+    
+private:
+    wxTaskBarIconImpl *m_impl;
+    DECLARE_EVENT_TABLE()
+};
+
 // ============================================================================
 // wxTaskBarIconImpl
 //     Base class for the various Cocoa implementations.
@@ -37,23 +55,27 @@ class wxTaskBarIconWindow;
 class wxTaskBarIconImpl
 {
 public:
-    wxTaskBarIconImpl(wxTaskBarIcon *taskBarIcon)
-    :   m_taskBarIcon(taskBarIcon)
-    ,   m_iconWindow(NULL)
-    {}
+    wxTaskBarIconImpl(wxTaskBarIcon *taskBarIcon);
+    
+    virtual bool IsStatusItem() const { return false; }
+
     virtual bool SetIcon(const wxIcon& icon, const wxString& tooltip = wxEmptyString) = 0;
     virtual bool RemoveIcon() = 0;
+    
+    bool IsIconInstalled() const { return m_icon.IsOk(); }
+        
     virtual bool PopupMenu(wxMenu *menu) = 0;
     virtual ~wxTaskBarIconImpl();
     inline wxTaskBarIcon* GetTaskBarIcon() { return m_taskBarIcon; }
     wxMenu * CreatePopupMenu()
     { return m_taskBarIcon->CreatePopupMenu(); }
-    
-    DECLARE_NO_COPY_CLASS(wxTaskBarIconImpl)
-    
+
+    wxDECLARE_NO_COPY_CLASS(wxTaskBarIconImpl);
+
 protected:
     wxTaskBarIcon *m_taskBarIcon;
-    wxTaskBarIconWindow *m_iconWindow;
+    wxBitmap m_icon;
+    wxTaskBarIconWindow *m_eventWindow;
 private:
     wxTaskBarIconImpl();
 };
@@ -71,16 +93,24 @@ public:
     virtual bool RemoveIcon();
     virtual bool PopupMenu(wxMenu *menu);
 
-    static WX_NSMenu CocoaGetDockNSMenu();
+    static WX_NSMenu OSXGetDockHMenu();
 protected:
-    WX_NSMenu CocoaDoGetDockNSMenu();
-    WX_NSImage m_originalDockIcon;
+    WX_NSMenu OSXDoGetDockHMenu();
     // There can be only one Dock icon, so make sure we keep it that way
     static wxTaskBarIconDockImpl *sm_dockIcon;
 private:
     wxTaskBarIconDockImpl();
+    wxMenu             *m_pMenu;
 };
 
+class wxTaskBarIconCustomStatusItemImpl;
+
+@interface wxOSXStatusItemTarget : NSObject
+{
+    wxTaskBarIconCustomStatusItemImpl* impl;
+}
+@end
+
 // ============================================================================
 // wxTaskBarIconCustomStatusItemImpl
 //     An implementation using an NSStatusItem with a custom NSView
@@ -90,52 +120,19 @@ class wxTaskBarIconCustomStatusItemImpl: public wxTaskBarIconImpl
 public:
     wxTaskBarIconCustomStatusItemImpl(wxTaskBarIcon *taskBarIcon);
     virtual ~wxTaskBarIconCustomStatusItemImpl();
+    
+    virtual bool IsStatusItem() const { return true; }
+
     virtual bool SetIcon(const wxIcon& icon, const wxString& tooltip = wxEmptyString);
     virtual bool RemoveIcon();
     virtual bool PopupMenu(wxMenu *menu);
 protected:
-    NSStatusItem *m_cocoaNSStatusItem;
+    NSStatusItem *m_statusItem;
+    wxOSXStatusItemTarget *m_target;
 private:
     wxTaskBarIconCustomStatusItemImpl();
 };
 
-// ============================================================================
-// wxTaskBarIconWindow
-//     Used by all implementations to forward events from the wxMenu
-// ============================================================================
-class wxTaskBarIconWindow: public wxWindow
-{
-    DECLARE_EVENT_TABLE()
-public:
-    wxTaskBarIconWindow(wxTaskBarIconImpl *taskBarIconImpl)
-    :   wxWindow(NULL,-1)
-    ,   m_taskBarIconImpl(taskBarIconImpl)
-    {   wxASSERT(m_taskBarIconImpl); }
-
-    void OnMenuEvent(wxCommandEvent& event);
-protected:
-    wxTaskBarIconImpl *m_taskBarIconImpl;
-};
-
-// ============================================================================
-// wxTaskBarIconWindowCustom
-//     Used by the CustomStatusIcon implementation for the custom NSView.
-// ============================================================================
-class wxTaskBarIconWindowCustom: public wxTaskBarIconWindow
-{
-    DECLARE_EVENT_TABLE()
-public:
-    wxTaskBarIconWindowCustom(wxTaskBarIconImpl *taskBarIconImpl)
-    :   wxTaskBarIconWindow(taskBarIconImpl)
-    {}
-    void SetIcon(const wxIcon& icon)
-    {   m_icon = icon; }
-    void OnMouseEvent(wxMouseEvent &event);
-    void OnPaint(wxPaintEvent &event);
-protected:
-    wxIcon m_icon;
-};
-
 // ============================================================================
 // wxTaskBarIcon implementation
 //     The facade class.
@@ -144,9 +141,9 @@ IMPLEMENT_DYNAMIC_CLASS(wxTaskBarIcon, wxEvtHandler)
 
 wxTaskBarIcon::wxTaskBarIcon(wxTaskBarIconType iconType)
 {
-    if(iconType == DOCK)
+    if(iconType == wxTBI_DOCK)
         m_impl = new wxTaskBarIconDockImpl(this);
-    else if(iconType == CUSTOM_STATUSITEM)
+    else if(iconType == wxTBI_CUSTOM_STATUSITEM)
         m_impl = new wxTaskBarIconCustomStatusItemImpl(this);
     else
     {   m_impl = NULL;
@@ -156,39 +153,69 @@ wxTaskBarIcon::wxTaskBarIcon(wxTaskBarIconType iconType)
 
 wxTaskBarIcon::~wxTaskBarIcon()
 {
-    delete m_impl;
+    if ( m_impl )
+    {
+        if ( m_impl->IsIconInstalled() )
+            m_impl->RemoveIcon();
+        delete m_impl;
+        m_impl = NULL;
+    }
+}
+
+bool wxTaskBarIcon::OSXIsStatusItem()
+{
+    if ( m_impl )
+        return m_impl->IsStatusItem();
+
+    return false;
 }
 
 // Operations
 
 bool wxTaskBarIcon::IsIconInstalled() const
-{ 
+{
+    if ( m_impl )
+        return m_impl->IsIconInstalled();
+    
     return false;
 }
 
 bool wxTaskBarIcon::SetIcon(const wxIcon& icon, const wxString& tooltip)
 {
-    return m_impl->SetIcon(icon,tooltip);
+    if ( m_impl )
+        return m_impl->SetIcon(icon,tooltip);
+
+    return false;
 }
 
 bool wxTaskBarIcon::RemoveIcon()
 {
-    return m_impl->RemoveIcon();
+    if ( m_impl )
+        return m_impl->RemoveIcon();
+
+    return false;
 }
 
 bool wxTaskBarIcon::PopupMenu(wxMenu *menu)
 {
-    return m_impl->PopupMenu(menu);
+    if ( m_impl )
+        return m_impl->PopupMenu(menu);
+
+    return false;
 }
 
 // ============================================================================
 // wxTaskBarIconImpl
 // ============================================================================
 
+wxTaskBarIconImpl::wxTaskBarIconImpl(wxTaskBarIcon* taskBarIcon)
+ : m_taskBarIcon(taskBarIcon), m_eventWindow(new wxTaskBarIconWindow(this))
+{
+}
+
 wxTaskBarIconImpl::~wxTaskBarIconImpl()
 {
-//    wxAutoNSAutoreleasePool pool;
-    delete m_iconWindow;
+    delete m_eventWindow;
 }
 
 // ============================================================================
@@ -199,67 +226,112 @@ wxTaskBarIconDockImpl *wxTaskBarIconDockImpl::sm_dockIcon = NULL;
 wxTaskBarIconDockImpl::wxTaskBarIconDockImpl(wxTaskBarIcon *taskBarIcon)
 :   wxTaskBarIconImpl(taskBarIcon)
 {
-    m_originalDockIcon = nil;
     wxASSERT_MSG(!sm_dockIcon, wxT("You should never have more than one dock icon!"));
     sm_dockIcon = this;
+    m_pMenu = NULL;
 }
 
 wxTaskBarIconDockImpl::~wxTaskBarIconDockImpl()
 {
-//    wxAutoNSAutoreleasePool pool;
     if(sm_dockIcon == this)
         sm_dockIcon = NULL;
 }
 
-WX_NSMenu wxTaskBarIconDockImpl::CocoaGetDockNSMenu()
+WX_NSMenu wxTaskBarIconDockImpl::OSXGetDockHMenu()
 {
     if(sm_dockIcon)
-        return sm_dockIcon->CocoaDoGetDockNSMenu();
+        return sm_dockIcon->OSXDoGetDockHMenu();
+    
     return nil;
 }
 
-WX_NSMenu wxTaskBarIconDockImpl::CocoaDoGetDockNSMenu()
+WX_NSMenu wxTaskBarIconDockImpl::OSXDoGetDockHMenu()
 {
     wxMenu *dockMenu = CreatePopupMenu();
+
     if(!dockMenu)
         return nil;
-    if(!m_iconWindow)
-        m_iconWindow = new wxTaskBarIconWindow(this);
-    dockMenu->SetInvokingWindow(m_iconWindow);
-    dockMenu->UpdateUI();
-    //dockMenu->SetCocoaDeletes(true);
+    
+    wxDELETE(m_pMenu);
+
+    m_pMenu = dockMenu;
+    
+    m_pMenu->SetInvokingWindow(m_eventWindow);
+    
+    m_pMenu->UpdateUI();
+    
     return (WX_NSMenu)dockMenu->GetHMenu();
 }
 
-bool wxTaskBarIconDockImpl::SetIcon(const wxIcon& icon, const wxString& tooltip)
+bool wxTaskBarIconDockImpl::SetIcon(const wxIcon& icon, const wxString& WXUNUSED(tooltip))
 {
-    wxMacAutoreleasePool pool;
-    m_originalDockIcon = [[[NSApplication sharedApplication] applicationIconImage] retain];
-    //[[NSApplication sharedApplication] setApplicationIconImage:icon.GetNSImage()];
+    m_icon.CopyFromIcon(icon);
+    [[NSApplication sharedApplication] setApplicationIconImage:m_icon.GetNSImage()];
     return true;
 }
 
 bool wxTaskBarIconDockImpl::RemoveIcon()
 {
-    [[NSApplication sharedApplication] setApplicationIconImage:m_originalDockIcon];
-    [m_originalDockIcon release];
+    wxDELETE(m_pMenu);
+    m_icon = wxBitmap();
+    [[NSApplication sharedApplication] setApplicationIconImage:nil];
     return true;
 }
 
-bool wxTaskBarIconDockImpl::PopupMenu(wxMenu *menu)
+bool wxTaskBarIconDockImpl::PopupMenu(wxMenu *WXUNUSED(menu))
 {
     wxFAIL_MSG(wxT("You cannot force the Dock icon menu to popup"));
     return false;
 }
 
+@interface wxNSAppController(wxTaskBarIconNSApplicationDelegateCategory)
+- (NSMenu*)applicationDockMenu:(NSApplication *)sender;
+@end
+
+@implementation wxNSAppController(wxTaskBarIconNSApplicationDelegateCategory)
+- (NSMenu*)applicationDockMenu:(NSApplication *)sender
+{
+    wxUnusedVar(sender);
+    
+    return wxTaskBarIconDockImpl::OSXGetDockHMenu();
+}
+@end
 
 // ============================================================================
 // wxTaskBarIconCustomStatusItemImpl
 // ============================================================================
+
+@implementation wxOSXStatusItemTarget
+
+- (void) clickedAction: (id) sender
+{
+    wxUnusedVar(sender);
+    wxMenu *menu = impl->CreatePopupMenu();
+    if (menu)
+    {
+        impl->PopupMenu(menu);
+        delete menu;
+    }    
+}
+
+- (void)setImplementation: (wxTaskBarIconCustomStatusItemImpl *) theImplementation
+{
+    impl = theImplementation;
+}
+
+- (wxTaskBarIconCustomStatusItemImpl*) implementation
+{
+    return impl;
+}
+
+@end
+
+
 wxTaskBarIconCustomStatusItemImpl::wxTaskBarIconCustomStatusItemImpl(wxTaskBarIcon *taskBarIcon)
 :   wxTaskBarIconImpl(taskBarIcon)
 {
-    m_cocoaNSStatusItem = nil;
+    m_statusItem = nil;
+    m_target = nil;
 }
 
 wxTaskBarIconCustomStatusItemImpl::~wxTaskBarIconCustomStatusItemImpl()
@@ -268,50 +340,60 @@ wxTaskBarIconCustomStatusItemImpl::~wxTaskBarIconCustomStatusItemImpl()
 
 bool wxTaskBarIconCustomStatusItemImpl::SetIcon(const wxIcon& icon, const wxString& tooltip)
 {
-    wxMacAutoreleasePool pool;
-    if(!m_cocoaNSStatusItem)
+    if(!m_statusItem)
     {
-        m_cocoaNSStatusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
-        [m_cocoaNSStatusItem retain];
+        m_statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
+        [m_statusItem retain];
+
+        m_target = [[wxOSXStatusItemTarget alloc] init];
+        [m_target setImplementation:this];
+        [m_statusItem setHighlightMode:YES];
+        [m_statusItem setTarget:m_target];
+        [m_statusItem setAction:@selector(clickedAction:)];
+        [m_statusItem sendActionOn:NSLeftMouseDownMask];
     }
-    if(!m_iconWindow)
-        m_iconWindow= new wxTaskBarIconWindowCustom(this);
-    static_cast<wxTaskBarIconWindowCustom*>(m_iconWindow)->SetIcon(icon);
-    // FIXME: no less than 10 because most icon types don't work yet
-    // and this allows us to see how task bar icons would work
-    [(NSView*)m_iconWindow->GetHandle() setFrame:NSMakeRect(0.0,0.0,wxMax(10,icon.GetWidth()),[[NSStatusBar systemStatusBar] thickness])];
-    [m_cocoaNSStatusItem setView:(NSView*)m_iconWindow->GetHandle()];
+
+    m_icon.CopyFromIcon(icon);
+    
+    // status item doesn't scale automatically
+    
+    int dimension = m_icon.GetHeight();
+    if ( m_icon.GetWidth() > dimension )
+        dimension = m_icon.GetWidth();
+    if ( dimension > 16 )
+    {
+        wxImage img = m_icon.ConvertToImage();
+        int factor = (dimension+15)/16;
+        m_icon = img.ShrinkBy(factor, factor);
+    }
+
+    [m_statusItem setImage:m_icon.GetNSImage()];
+    wxCFStringRef cfTooltip(tooltip);
+    [m_statusItem setToolTip:cfTooltip.AsNSString()];
     return true;
 }
 
 bool wxTaskBarIconCustomStatusItemImpl::RemoveIcon()
 {
-    [m_cocoaNSStatusItem release];
-    m_cocoaNSStatusItem = nil;
-    delete m_iconWindow;
-    m_iconWindow = NULL;
+    [m_statusItem release];
+    m_statusItem = nil;
+    [m_target release];
+    m_target = nil;
+    
+    m_icon = wxBitmap();
+    
     return true;
 }
 
 bool wxTaskBarIconCustomStatusItemImpl::PopupMenu(wxMenu *menu)
 {
     wxASSERT(menu);
-    menu->SetInvokingWindow(m_iconWindow);
+
+    menu->SetInvokingWindow(m_eventWindow);
     menu->UpdateUI();
 
-    if([m_cocoaNSStatusItem respondsToSelector:@selector(popUpStatusItemMenu:)])
-    {   // OS X >= 10.3
-        [m_cocoaNSStatusItem popUpStatusItemMenu:(NSMenu*)menu->GetHMenu()];
-    }
-    else
-    {   // pretty good fake for OS X < 10.3
-        NSEvent *nsevent = [NSEvent mouseEventWithType:NSLeftMouseDown
-            location:NSMakePoint(-1.0,-4.0) modifierFlags:0 timestamp:0
-            windowNumber:[[(NSView*)m_iconWindow->GetHandle() window] windowNumber]
-            context:[NSGraphicsContext currentContext]
-            eventNumber:0 clickCount:1 pressure:0.0];
-        [NSMenu popUpContextMenu:menu->GetHMenu() withEvent:nsevent forView:(NSView*)m_iconWindow->GetHandle()];
-    }
+    [m_statusItem popUpStatusItemMenu:(NSMenu*)menu->GetHMenu()];
+
     menu->SetInvokingWindow(NULL);
     return true;
 }
@@ -319,53 +401,25 @@ bool wxTaskBarIconCustomStatusItemImpl::PopupMenu(wxMenu *menu)
 // ============================================================================
 // wxTaskBarIconWindow
 // ============================================================================
+
 BEGIN_EVENT_TABLE(wxTaskBarIconWindow, wxWindow)
-    EVT_MENU(-1, wxTaskBarIconWindow::OnMenuEvent)
+EVT_MENU(-1, wxTaskBarIconWindow::OnMenuEvent)
+EVT_UPDATE_UI(-1, wxTaskBarIconWindow::OnUpdateUIEvent)
 END_EVENT_TABLE()
 
-void wxTaskBarIconWindow::OnMenuEvent(wxCommandEvent &event)
+wxTaskBarIconWindow::wxTaskBarIconWindow(wxTaskBarIconImpl *impl) 
+: m_impl(impl)
 {
-    m_taskBarIconImpl->GetTaskBarIcon()->ProcessEvent(event);
 }
 
-// ============================================================================
-// wxTaskBarIconWindowCustom
-// ============================================================================
-BEGIN_EVENT_TABLE(wxTaskBarIconWindowCustom, wxTaskBarIconWindow)
-    EVT_MOUSE_EVENTS(wxTaskBarIconWindowCustom::OnMouseEvent)
-    EVT_PAINT(wxTaskBarIconWindowCustom::OnPaint)
-END_EVENT_TABLE()
-
-void wxTaskBarIconWindowCustom::OnMouseEvent(wxMouseEvent &event)
-{
-    wxEventType tbEventType = 0;
-    if(event.GetEventType() == wxEVT_MOTION)
-        tbEventType = wxEVT_TASKBAR_MOVE;
-    else if(event.GetEventType() == wxEVT_LEFT_DOWN)
-        tbEventType = wxEVT_TASKBAR_LEFT_DOWN;
-    else if(event.GetEventType() == wxEVT_LEFT_UP)
-        tbEventType = wxEVT_TASKBAR_LEFT_UP;
-    else if(event.GetEventType() == wxEVT_RIGHT_DOWN)
-        tbEventType = wxEVT_TASKBAR_RIGHT_DOWN;
-    else if(event.GetEventType() == wxEVT_RIGHT_UP)
-        tbEventType = wxEVT_TASKBAR_RIGHT_UP;
-    else if(event.GetEventType() == wxEVT_LEFT_DCLICK)
-        tbEventType = wxEVT_TASKBAR_LEFT_DCLICK;
-    else if(event.GetEventType() == wxEVT_RIGHT_DCLICK)
-        tbEventType = wxEVT_TASKBAR_RIGHT_DCLICK;
-    else
-        return;
-    wxTaskBarIconEvent tbiEvent(tbEventType,m_taskBarIconImpl->GetTaskBarIcon());
-    m_taskBarIconImpl->GetTaskBarIcon()->ProcessEvent(tbiEvent);
+void wxTaskBarIconWindow::OnMenuEvent(wxCommandEvent& event)
+{
+    m_impl->GetTaskBarIcon()->ProcessEvent(event);
 }
 
-void wxTaskBarIconWindowCustom::OnPaint(wxPaintEvent &event)
+void wxTaskBarIconWindow::OnUpdateUIEvent(wxUpdateUIEvent& event)
 {
-    wxPaintDC dc(this);
-    // FIXME: This is a temporary hack until we can see real icons
-    dc.SetBackground(wxBrush(*wxBLUE));
-    dc.Clear();
-    dc.DrawIcon(m_icon,0,0);
+    m_impl->GetTaskBarIcon()->ProcessEvent(event);
 }
 
 #endif //def wxHAS_TASK_BAR_ICON