]> git.saurik.com Git - wxWidgets.git/blobdiff - src/generic/infobar.cpp
Swap client data pointers in wxRearrangeList too.
[wxWidgets.git] / src / generic / infobar.cpp
index db8708a56bc7c1430974028c0193bf3ee55dee91..76001a4a17e9df3035c16c1a588e55ff348f2f00 100644 (file)
@@ -3,7 +3,7 @@
 // Purpose:     generic wxInfoBar implementation
 // Author:      Vadim Zeitlin
 // Created:     2009-07-28
-// RCS-ID:      $Id: wxhead.cpp,v 1.10 2009-06-29 10:23:04 zeitlin Exp $
+// RCS-ID:      $Id$
 // Copyright:   (c) 2009 Vadim Zeitlin <vadim@wxwidgets.org>
 // Licence:     wxWindows licence
 ///////////////////////////////////////////////////////////////////////////////
 
 #if wxUSE_INFOBAR
 
+#include "wx/infobar.h"
+
 #ifndef WX_PRECOMP
-    #include "wx/artprov.h"
     #include "wx/bmpbuttn.h"
+    #include "wx/button.h"
+    #include "wx/dcmemory.h"
     #include "wx/settings.h"
     #include "wx/statbmp.h"
     #include "wx/stattext.h"
+    #include "wx/sizer.h"
 #endif // WX_PRECOMP
 
-#include "wx/infobar.h"
-
+#include "wx/artprov.h"
+#include "wx/renderer.h"
 #include "wx/scopeguard.h"
-#include "wx/sizer.h"
+
+BEGIN_EVENT_TABLE(wxInfoBarGeneric, wxInfoBarBase)
+    EVT_BUTTON(wxID_ANY, wxInfoBarGeneric::OnButton)
+END_EVENT_TABLE()
+
+// ----------------------------------------------------------------------------
+// local helpers
+// ----------------------------------------------------------------------------
+
+namespace
+{
+
+#ifdef wxHAS_DRAW_TITLE_BAR_BITMAP
+
+wxBitmap
+GetCloseButtonBitmap(wxWindow *win,
+                     const wxSize& size,
+                     const wxColour& colBg,
+                     int flags = 0)
+{
+    wxBitmap bmp(size);
+    wxMemoryDC dc(bmp);
+    dc.SetBackground(colBg);
+    dc.Clear();
+    wxRendererNative::Get().
+        DrawTitleBarBitmap(win, dc, size, wxTITLEBAR_BUTTON_CLOSE, flags);
+    return bmp;
+}
+
+#endif // wxHAS_DRAW_TITLE_BAR_BITMAP
+
+} // anonymous namespace
 
 // ============================================================================
 // implementation
 // ============================================================================
 
-void wxInfoBar::Init()
+void wxInfoBarGeneric::Init()
 {
     m_icon = NULL;
     m_text = NULL;
     m_button = NULL;
 
-    m_showEffect = wxSHOW_EFFECT_SLIDE_TO_BOTTOM;
-    m_hideEffect = wxSHOW_EFFECT_SLIDE_TO_TOP;
+    m_showEffect =
+    m_hideEffect = wxSHOW_EFFECT_MAX;
 
     // use default effect duration
     m_effectDuration = 0;
 }
 
-bool wxInfoBar::Create(wxWindow *parent, wxWindowID winid)
+bool wxInfoBarGeneric::Create(wxWindow *parent, wxWindowID winid)
 {
     // calling Hide() before Create() ensures that we're created initially
     // hidden
@@ -64,7 +99,8 @@ bool wxInfoBar::Create(wxWindow *parent, wxWindowID winid)
         return false;
 
     // use special, easy to notice, colours
-    SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_INFOBK));
+    const wxColour colBg = wxSystemSettings::GetColour(wxSYS_COLOUR_INFOBK);
+    SetBackgroundColour(colBg);
     SetOwnForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_INFOTEXT));
 
     // create the controls: icon, text and the button to dismiss the
@@ -73,100 +109,138 @@ bool wxInfoBar::Create(wxWindow *parent, wxWindowID winid)
     // the icon is not shown unless it's assigned a valid bitmap
     m_icon = new wxStaticBitmap(this, wxID_ANY, wxNullBitmap);
 
-    // by default, the text uses a larger, more noticeable, font
     m_text = new wxStaticText(this, wxID_ANY, "");
-    m_text->SetFont(m_text->GetFont().Bold().Larger());
 
+#ifdef wxHAS_DRAW_TITLE_BAR_BITMAP
+    const wxSize sizeBmp = wxArtProvider::GetSizeHint(wxART_BUTTON);
+    wxBitmap bmp = GetCloseButtonBitmap(this, sizeBmp, colBg);
+#else // !wxHAS_DRAW_TITLE_BAR_BITMAP
+    wxBitmap bmp = wxArtProvider::GetBitmap(wxART_CLOSE, wxART_BUTTON);
+#endif // wxHAS_DRAW_TITLE_BAR_BITMAP
     m_button = new wxBitmapButton
                    (
                     this,
                     wxID_ANY,
-                    wxArtProvider::GetBitmap(wxART_CROSS_MARK),
+                    bmp,
                     wxDefaultPosition,
                     wxDefaultSize,
                     wxBORDER_NONE
                    );
-    m_button->SetToolTip(_("Hide this notification message."));
 
-    Connect
-    (
-        wxEVT_COMMAND_BUTTON_CLICKED,
-        wxCommandEventHandler(wxInfoBar::OnButton),
-        NULL,
-        this
-    );
+#ifdef wxHAS_DRAW_TITLE_BAR_BITMAP
+    m_button->SetBitmapPressed(
+        GetCloseButtonBitmap(this, sizeBmp, colBg, wxCONTROL_PRESSED));
+
+    m_button->SetBitmapCurrent(
+        GetCloseButtonBitmap(this, sizeBmp, colBg, wxCONTROL_CURRENT));
+#endif // wxHAS_DRAW_TITLE_BAR_BITMAP
+
+    m_button->SetBackgroundColour(colBg);
+    m_button->SetToolTip(_("Hide this notification message."));
 
-    // Center the text inside the sizer.
+    // center the text inside the sizer with an icon to the left of it and a
+    // button at the very right
+    //
+    // NB: AddButton() relies on the button being the last control in the sizer
+    //     and being preceded by a spacer
     wxSizer * const sizer = new wxBoxSizer(wxHORIZONTAL);
+    sizer->Add(m_icon, wxSizerFlags().Centre().Border());
+    sizer->Add(m_text, wxSizerFlags().Centre());
     sizer->AddStretchSpacer();
-    sizer->Add(m_icon, wxSizerFlags().Centre().DoubleBorder());
-    sizer->Add(m_text, wxSizerFlags().Centre().DoubleBorder());
-    sizer->AddStretchSpacer();
-    sizer->Add(m_button, wxSizerFlags().Centre().DoubleBorder());
+    sizer->Add(m_button, wxSizerFlags().Centre().Border());
     SetSizer(sizer);
 
     return true;
 }
 
-void wxInfoBar::UpdateParent()
+bool wxInfoBarGeneric::SetFont(const wxFont& font)
 {
-    wxWindow * const parent = wxGetTopLevelParent(GetParent());
-    parent->Layout();
+    if ( !wxInfoBarBase::SetFont(font) )
+        return false;
+
+    // check that we're not called before Create()
+    if ( m_text )
+        m_text->SetFont(font);
+
+    return true;
 }
 
-void wxInfoBar::ChangeParentBackground()
+wxInfoBarGeneric::BarPlacement wxInfoBarGeneric::GetBarPlacement() const
 {
-    wxWindow * const parent = GetParent();
-    m_origParentBgCol = parent->GetBackgroundColour();
-
     wxSizer * const sizer = GetContainingSizer();
     if ( !sizer )
-        return;
+        return BarPlacement_Unknown;
+
+    // FIXME-VC6: can't compare "const wxInfoBarGeneric *" and "wxWindow *",
+    //            so need this workaround
+    wxWindow * const self = const_cast<wxInfoBarGeneric *>(this);
+    const wxSizerItemList& siblings = sizer->GetChildren();
+    if ( siblings.GetFirst()->GetData()->GetWindow() == self )
+        return BarPlacement_Top;
+    else if ( siblings.GetLast()->GetData()->GetWindow() == self )
+        return BarPlacement_Bottom;
+    else
+        return BarPlacement_Unknown;
+}
 
-    wxWindow *sibling = NULL;
-    for ( wxSizerItemList::compatibility_iterator
-            node = sizer->GetChildren().GetFirst();
-            node;
-            node = node->GetNext() )
+wxShowEffect wxInfoBarGeneric::GetShowEffect() const
+{
+    if ( m_showEffect != wxSHOW_EFFECT_MAX )
+        return m_showEffect;
+
+    switch ( GetBarPlacement() )
     {
-        if ( node->GetData()->GetWindow() == this )
-        {
-            // find the next window following us
-            for ( node = node->GetNext();
-                  node;
-                  node = node->GetNext() )
-            {
-                wxSizerItem * const item = node->GetData();
-                if ( item->IsWindow() )
-                {
-                    sibling = item->GetWindow();
-                    break;
-                }
-            }
+        case BarPlacement_Top:
+            return wxSHOW_EFFECT_SLIDE_TO_BOTTOM;
 
-            break;
-        }
+        case BarPlacement_Bottom:
+            return wxSHOW_EFFECT_SLIDE_TO_TOP;
+
+        default:
+            wxFAIL_MSG( "unknown info bar placement" );
+            // fall through
+
+        case BarPlacement_Unknown:
+            return wxSHOW_EFFECT_NONE;
     }
+}
 
-    if ( sibling )
-        parent->SetOwnBackgroundColour(sibling->GetBackgroundColour());
+wxShowEffect wxInfoBarGeneric::GetHideEffect() const
+{
+    if ( m_hideEffect != wxSHOW_EFFECT_MAX )
+        return m_hideEffect;
+
+    switch ( GetBarPlacement() )
+    {
+        case BarPlacement_Top:
+            return wxSHOW_EFFECT_SLIDE_TO_TOP;
+
+        case BarPlacement_Bottom:
+            return wxSHOW_EFFECT_SLIDE_TO_BOTTOM;
+
+        default:
+            wxFAIL_MSG( "unknown info bar placement" );
+            // fall through
+
+        case BarPlacement_Unknown:
+            return wxSHOW_EFFECT_NONE;
+    }
 }
 
-void wxInfoBar::RestoreParentBackground()
+void wxInfoBarGeneric::UpdateParent()
 {
-    GetParent()->SetOwnBackgroundColour(m_origParentBgCol);
+    wxWindow * const parent = GetParent();
+    parent->Layout();
 }
 
-void wxInfoBar::DoHide()
+void wxInfoBarGeneric::DoHide()
 {
-    ChangeParentBackground();
-    wxON_BLOCK_EXIT_THIS0( wxInfoBar::RestoreParentBackground );
+    HideWithEffect(GetHideEffect(), GetEffectDuration());
 
-    HideWithEffect(m_hideEffect, m_effectDuration);
     UpdateParent();
 }
 
-void wxInfoBar::DoShow()
+void wxInfoBarGeneric::DoShow()
 {
     // re-layout the parent first so that the window expands into an already
     // unoccupied by the other controls area: for this we need to change our
@@ -178,12 +252,6 @@ void wxInfoBar::DoShow()
     // without really showing it
     wxWindowBase::Show();
 
-    // an extra hack: we want the temporarily uncovered area in which we're
-    // going to expand to look like part of this sibling for a better effect so
-    // temporarily change the background of our parent to the same colour
-    ChangeParentBackground();
-    wxON_BLOCK_EXIT_THIS0( wxInfoBar::RestoreParentBackground );
-
     // adjust the parent layout to account for us
     UpdateParent();
 
@@ -193,10 +261,10 @@ void wxInfoBar::DoShow()
 
 
     // finally do really show the window.
-    ShowWithEffect(m_showEffect, m_effectDuration);
+    ShowWithEffect(GetShowEffect(), GetEffectDuration());
 }
 
-void wxInfoBar::ShowMessage(const wxString& msg, int flags)
+void wxInfoBarGeneric::ShowMessage(const wxString& msg, int flags)
 {
     // first update the controls
     const int icon = flags & wxICON_MASK;
@@ -206,7 +274,9 @@ void wxInfoBar::ShowMessage(const wxString& msg, int flags)
     }
     else // do show an icon
     {
-        m_icon->SetBitmap(wxArtProvider::GetMessageBoxIcon(icon));
+        m_icon->SetBitmap(wxArtProvider::GetBitmap(
+                            wxArtProvider::GetMessageBoxIconId(flags),
+                          wxART_BUTTON));
         m_icon->Show();
     }
 
@@ -227,7 +297,76 @@ void wxInfoBar::ShowMessage(const wxString& msg, int flags)
     }
 }
 
-void wxInfoBar::OnButton(wxCommandEvent& WXUNUSED(event))
+void wxInfoBarGeneric::Dismiss()
+{
+    DoHide();
+}
+
+void wxInfoBarGeneric::AddButton(wxWindowID btnid, const wxString& label)
+{
+    wxSizer * const sizer = GetSizer();
+    wxCHECK_RET( sizer, "must be created first" );
+
+    // user-added buttons replace the standard close button so remove it if we
+    // hadn't done it yet
+    if ( sizer->Detach(m_button) )
+    {
+        m_button->Hide();
+    }
+
+    wxButton * const button = new wxButton(this, btnid, label);
+
+#ifdef __WXMAC__
+    // smaller buttons look better in the (narrow) info bar under OS X
+    button->SetWindowVariant(wxWINDOW_VARIANT_SMALL);
+#endif // __WXMAC__
+
+    sizer->Add(button, wxSizerFlags().Centre().DoubleBorder());
+}
+
+void wxInfoBarGeneric::RemoveButton(wxWindowID btnid)
+{
+    wxSizer * const sizer = GetSizer();
+    wxCHECK_RET( sizer, "must be created first" );
+
+    // iterate over the sizer items in reverse order to find the last added
+    // button with this id (ids of all buttons should be unique anyhow but if
+    // they are repeated removing the last added one probably makes more sense)
+    const wxSizerItemList& items = sizer->GetChildren();
+    for ( wxSizerItemList::compatibility_iterator node = items.GetLast();
+          node != items.GetFirst();
+          node = node->GetPrevious() )
+    {
+        const wxSizerItem * const item = node->GetData();
+
+        // if we reached the spacer separating the buttons from the text
+        // preceding them without finding our button, it must mean it's not
+        // there at all
+        if ( item->IsSpacer() )
+        {
+            wxFAIL_MSG( wxString::Format("button with id %d not found", btnid) );
+            return;
+        }
+
+        // check if we found our button
+        if ( item->GetWindow()->GetId() == btnid )
+        {
+            delete item->GetWindow();
+            break;
+        }
+    }
+
+    // check if there are any custom buttons left
+    if ( sizer->GetChildren().GetLast()->GetData()->IsSpacer() )
+    {
+        // if the last item is the spacer, none are left so restore the
+        // standard close button
+        sizer->Add(m_button, wxSizerFlags().Centre().DoubleBorder());
+        m_button->Show();
+    }
+}
+
+void wxInfoBarGeneric::OnButton(wxCommandEvent& WXUNUSED(event))
 {
     DoHide();
 }