///////////////////////////////////////////////////////////////////////////////
// Name:        src/generic/infobar.cpp
// Purpose:     generic wxInfoBar implementation
// Author:      Vadim Zeitlin
// Created:     2009-07-28
// Copyright:   (c) 2009 Vadim Zeitlin <vadim@wxwidgets.org>
// Licence:     wxWindows licence
///////////////////////////////////////////////////////////////////////////////
 
// ============================================================================
// declarations
// ============================================================================
 
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
 
// for compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
 
#ifdef __BORLANDC__
    #pragma hdrstop
#endif
 
#if wxUSE_INFOBAR
 
#include "wx/infobar.h"
 
#ifndef WX_PRECOMP
    #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/artprov.h"
#include "wx/scopeguard.h"
 
wxBEGIN_EVENT_TABLE(wxInfoBarGeneric, wxInfoBarBase)
    EVT_BUTTON(wxID_ANY, wxInfoBarGeneric::OnButton)
wxEND_EVENT_TABLE()
 
// ============================================================================
// implementation
// ============================================================================
 
void wxInfoBarGeneric::Init()
{
    m_icon = NULL;
    m_text = NULL;
    m_button = NULL;
 
    m_showEffect =
    m_hideEffect = wxSHOW_EFFECT_MAX;
 
    // use default effect duration
    m_effectDuration = 0;
}
 
bool wxInfoBarGeneric::Create(wxWindow *parent, wxWindowID winid)
{
    // calling Hide() before Create() ensures that we're created initially
    // hidden
    Hide();
    if ( !wxWindow::Create(parent, winid) )
        return false;
 
    // use special, easy to notice, colours
    SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_INFOBK));
 
    // create the controls: icon, text and the button to dismiss the
    // message.
 
    // the icon is not shown unless it's assigned a valid bitmap
    m_icon = new wxStaticBitmap(this, wxID_ANY, wxNullBitmap);
 
    m_text = new wxStaticText(this, wxID_ANY, wxString());
    m_text->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_INFOTEXT));
 
    m_button = wxBitmapButton::NewCloseButton(this, wxID_ANY);
    m_button->SetToolTip(_("Hide this notification message."));
 
    // 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_button, wxSizerFlags().Centre().Border());
    SetSizer(sizer);
 
    return true;
}
 
bool wxInfoBarGeneric::SetFont(const wxFont& font)
{
    if ( !wxInfoBarBase::SetFont(font) )
        return false;
 
    // check that we're not called before Create()
    if ( m_text )
        m_text->SetFont(font);
 
    return true;
}
 
bool wxInfoBarGeneric::SetForegroundColour(const wxColor& colour)
{
    if ( !wxInfoBarBase::SetForegroundColour(colour) )
        return false;
 
    if ( m_text )
        m_text->SetForegroundColour(colour);
 
    return true;
}
 
wxInfoBarGeneric::BarPlacement wxInfoBarGeneric::GetBarPlacement() const
{
    wxSizer * const sizer = GetContainingSizer();
    if ( !sizer )
        return BarPlacement_Unknown;
 
    const wxSizerItemList& siblings = sizer->GetChildren();
    if ( siblings.GetFirst()->GetData()->GetWindow() == this )
        return BarPlacement_Top;
    else if ( siblings.GetLast()->GetData()->GetWindow() == this )
        return BarPlacement_Bottom;
    else
        return BarPlacement_Unknown;
}
 
wxShowEffect wxInfoBarGeneric::GetShowEffect() const
{
    if ( m_showEffect != wxSHOW_EFFECT_MAX )
        return m_showEffect;
 
    switch ( GetBarPlacement() )
    {
        case BarPlacement_Top:
            return wxSHOW_EFFECT_SLIDE_TO_BOTTOM;
 
        case BarPlacement_Bottom:
            return wxSHOW_EFFECT_SLIDE_TO_TOP;
 
        default:
            wxFAIL_MSG( "unknown info bar placement" );
            wxFALLTHROUGH;
 
        case BarPlacement_Unknown:
            return wxSHOW_EFFECT_NONE;
    }
}
 
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" );
            wxFALLTHROUGH;
 
        case BarPlacement_Unknown:
            return wxSHOW_EFFECT_NONE;
    }
}
 
void wxInfoBarGeneric::UpdateParent()
{
    wxWindow * const parent = GetParent();
    parent->Layout();
}
 
void wxInfoBarGeneric::DoHide()
{
    HideWithEffect(GetHideEffect(), GetEffectDuration());
 
    UpdateParent();
}
 
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
    // internal visibility flag to force Layout() to take us into account (an
    // alternative solution to this hack would be to temporarily set
    // wxRESERVE_SPACE_EVEN_IF_HIDDEN flag but it's not really batter)
 
    // just change the internal flag indicating that the window is visible,
    // without really showing it
    wxWindowBase::Show();
 
    // adjust the parent layout to account for us
    UpdateParent();
 
    // reset the flag back before really showing the window or it wouldn't be
    // shown at all because it would believe itself already visible
    wxWindowBase::Show(false);
 
 
    // finally do really show the window.
    ShowWithEffect(GetShowEffect(), GetEffectDuration());
}
 
void wxInfoBarGeneric::ShowMessage(const wxString& msg, int flags)
{
    // first update the controls
    const int icon = flags & wxICON_MASK;
    if ( !icon || (icon == wxICON_NONE) )
    {
        m_icon->Hide();
    }
    else // do show an icon
    {
        m_icon->SetBitmap(wxArtProvider::GetBitmap(
                            wxArtProvider::GetMessageBoxIconId(flags),
                          wxART_BUTTON));
        m_icon->Show();
    }
 
    // notice the use of EscapeMnemonics() to ensure that "&" come through
    // correctly
    m_text->SetLabel(wxControl::EscapeMnemonics(msg));
 
 
    // then show this entire window if not done yet
    if ( !IsShown() )
    {
        DoShow();
    }
    else // we're already shown
    {
        // just update the layout to correspond to the new message
        Layout();
    }
}
 
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());
}
 
size_t wxInfoBarGeneric::GetButtonCount() const
{
    size_t count = 0;
    wxSizer * const sizer = GetSizer();
    if ( !sizer )
        return 0;
 
    // iterate over the sizer items in reverse order
    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
        // break the for-loop.
        if ( item->IsSpacer() )
            break;
 
        // if the standard button is shown, there must be no other ones
        if ( item->GetWindow() == m_button )
            return 0;
 
        ++count;
    }
 
    return count;
}
 
wxWindowID wxInfoBarGeneric::GetButtonId(size_t idx) const
{
    wxCHECK_MSG( idx < GetButtonCount(), wxID_NONE,
                 "Invalid infobar button position" );
 
    wxSizer * const sizer = GetSizer();
    if ( !sizer )
        return wxID_NONE;
 
    bool foundSpacer = false;
 
    size_t count = 0;
    const wxSizerItemList& items = sizer->GetChildren();
    for ( wxSizerItemList::compatibility_iterator node = items.GetLast();
          node != items.GetFirst() || node != items.GetLast();
          )
    {
        const wxSizerItem * const item = node->GetData();
 
        if ( item->IsSpacer() )
            foundSpacer = true;
 
        if ( foundSpacer )
        {
            if ( !item->IsSpacer() )
            {
                if ( count == idx )
                {
                  if ( item->GetWindow() != m_button )
                    return item->GetWindow()->GetId();
                }
 
                ++count;
            }
 
            node = node->GetNext();
        }
        else
        {
            node = node->GetPrevious();
        }
    }
 
    return wxID_NONE;
}
 
bool wxInfoBarGeneric::HasButtonId(wxWindowID btnid) const
{
    wxSizer * const sizer = GetSizer();
    if ( !sizer )
        return false;
 
    // iterate over the sizer items in reverse order to find the last added
    // button with this id
    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
        // then the wanted ID is not inside.
        if ( item->IsSpacer() )
            return false;
 
        // check if we found our button
        if ( item->GetWindow()->GetId() == btnid )
            return true;
    }
 
    return false;
}
 
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();
}
 
#endif // wxUSE_INFOBAR

V522 There might be dereferencing of a potential null pointer 'item->GetWindow()'.

V522 There might be dereferencing of a potential null pointer 'item->GetWindow()'.

V522 There might be dereferencing of a potential null pointer 'item->GetWindow()'.