/***************************************************************************
 *   Copyright (C) 2005 by Thierry CHARLES   *
 *   thierry@les-charles.net   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "tapplication.h"
#include "tapplwindow.h"
#include "tapplicationpanel.h"
#include "tpanelelement.h"
#include "components/stdgui/tpanel.h"
#include "components/stdgui/tgenbutton.h"
#include "components/stdgui/tsplitter.h"
#include "components/stdgui/tbitmap.h"

#include <wx/sizer.h>
#include <wx/menu.h>
#include <wx/settings.h>

#include "lib/lib_logging.h"

#include "left_arrow.xpm"
#include "right_arrow.xpm"
#include "up_arrow.xpm"
#include "down_arrow.xpm"
#include "cross.xpm"

bool operator>(const TApplicationPanel::TPanelElementMeta & a, const TApplicationPanel::TPanelElementMeta & b)
{
    return a.iDisplayOrder > b.iDisplayOrder;
}


TBitmap * TApplicationPanel::_leftBmp = NULL;
const TBitmap * TApplicationPanel::getLeftArrowBmp()
{
    if(!_leftBmp)
    {
        _leftBmp = new TBitmap((const char **)left_arrow_xpm);
    }
    return _leftBmp;
}

TBitmap * TApplicationPanel::_rightBmp = NULL;
const TBitmap * TApplicationPanel::getRightArrowBmp()
{
    if(!_rightBmp)
    {
        _rightBmp = new TBitmap((const char **)right_arrow_xpm);
    }
    return _rightBmp;
}

TBitmap * TApplicationPanel::_upBmp = NULL;
const TBitmap * TApplicationPanel::getUpArrowBmp()
{
    if(!_upBmp)
    {
        _upBmp = new TBitmap((const char **)up_arrow_xpm);
    }
    return _upBmp;
}

TBitmap * TApplicationPanel::_downBmp = NULL;
const TBitmap * TApplicationPanel::getDownArrowBmp()
{
    if(!_downBmp)
    {
        _downBmp = new TBitmap((const char **)down_arrow_xpm);
    }
    return _downBmp;
}

TBitmap * TApplicationPanel::_crossBmp = NULL;
const TBitmap * TApplicationPanel::getCrossBmp()
{
    if(!_crossBmp)
    {
        _crossBmp = new TBitmap((const char **)cross_xpm);
    }
    return _crossBmp;
}


/** tous les éléments restants sont détruits */
TApplicationPanel::~TApplicationPanel()
{
    TPanelElementMap::iterator it = this->elements.begin();
    while(it != this->elements.end())
    {
        (*it).second.elt->discardContentPanel();
        delete (*it).second.elt;
        (*it).second.elt = NULL;
        it++;
    }
    this->elements.erase(this->elements.begin(),this->elements.end());
}


TApplicationPanel::TPanelElementMap::iterator TApplicationPanel::getElementIterator(const int iId)
{
    return this->elements.find(iId);
}

TApplicationPanel::TPanelElementMap::const_iterator TApplicationPanel::getElementIterator(const int iId) const
{
    return this->elements.find(iId);
}

/**
 * Supprime un élément du panneau
 * @param iEltId ID de l'élément à supprimer
 * @param bAutoDestroy indique si le panneau doit se charger de la destruction de l'élément
 * @return un pointeur sur l'élément qui vient d'être supprimé ou NULL si celui-ci n'existait pas ou à été détruit
 */
TPanelElement * TApplicationPanel::removeElement(const int iEltId, bool bAutoDestroy)
{
    TPanelElementMap::iterator it = this->getElementIterator(iEltId);
    if(it == this->elements.end())
        return NULL;
    TPanelElementMeta meta = (*it).second;

    this->elements.erase(it);
    if(meta.bVisible)
        this->iVisibleElements--;

    this->updateDisplayMap();

    this->fireRemovingElement(meta.elt);

    if(bAutoDestroy)
    {
        delete meta.elt;
        meta.elt = NULL;
    }

    if(meta.container);
    {
        delete meta.container;
        meta.container = NULL;
        meta.title = NULL;
    }

    this->discardAll();

    if(this->displayMode == Exclusive && meta.bVisible
        && this->iVisibleElements < 1 && this->elements.size() > 0)
    {
        TPanelElement * panelToDisplay = this->getElementAt(meta.iDisplayOrder);
        if(!panelToDisplay)
            panelToDisplay = this->getElementAt(meta.iDisplayOrder - 1);

        if(panelToDisplay)
        {
            this->setElementVisible(panelToDisplay->getID(),true);
        }
    }

    return meta.elt;
}

/** renvoie un pointeur sur l'élément identifié par iEltId ou NULL si celui-ci n'existe pas */
TPanelElement * TApplicationPanel::getElement(const int iEltId) const
{
    TPanelElementMap::const_iterator it = this->getElementIterator(iEltId);
    if(it == this->elements.end())
        return NULL;
    return (*it).second.elt;
}

/**
 * ajoute un élément au panneau
 * @param elt element a rajouter
 * @param iDisplayOrder ordre d'affichage de l'element (-1 = en fin)
 */
void TApplicationPanel::addElement(TPanelElement * elt, int iDisplayOrder)
{
    if(this->getElement(elt->getID()))
        return;

    elt->setOwner( this );

    this->elements[elt->getID()] = TPanelElementMeta(elt);
    this->setElementDisplayOrder(elt,iDisplayOrder,false);

    this->fireAddingElement(elt);

    if(this->displayMode == Exclusive && !this->iVisibleElements)
        this->setElementVisible(elt->getID(),true);

    this->discardButtonsPanel(); // MAJ de la barre de boutons
    this->discardContentPanel(); // MAJ du panneau de contenu

    if(elt->isAlwaysVisible())
        this->setElementVisible(elt->getID(),true);
}

/** applique les propriétés de visibilité à un élément */
void TApplicationPanel::applyVisibility(TPanelElementMap::iterator it, bool bVisible)
{
    if(this->contentPanel && (*it).second.elt->getContentPanel())
        this->contentPanel->GetSizer()->Show((*it).second.container,bVisible);

    if((*it).second.bVisible == bVisible)
        return;

    (*it).second.bVisible = bVisible;
    if((*it).second.btn != NULL)
        (*it).second.btn->setToggled(bVisible);
    this->iVisibleElements += (bVisible) ? 1 : -1; // a faire en dernier autrement le dé-toggleage re-toggle le bouton

    this->fireElementVisibilityChanged((*it).second.elt,bVisible);
}

/** affiche ou cache un élément */
void TApplicationPanel::setElementVisible(const int iId, bool bVisible)
{
    if(this->bUpdatingVisibleElements)
        return;
    this->bUpdatingVisibleElements = true;

    TPanelElementMap::iterator it = this->getElementIterator(iId);
    if(it == this->elements.end())
        return;

    bool bFocus = bVisible && (!(*it).second.bVisible) && (*it).second.btn && (*it).second.btn->isToggled();

    bool bMightUpdateSizes = (*it).second.bVisible != bVisible;

    this->applyVisibility(it,bVisible);

    if(bVisible && this->displayMode == Exclusive)
    {
        // en mode exclusif, on cache les autres éléments
        TPanelElementMap::iterator itB = this->elements.begin();
        TPanelElementMap::iterator itE = this->elements.end();
        while(itB != itE)
        {
            if((*itB).second.bVisible && itB != it && (!(*itB).second.elt->isAlwaysVisible()))
            {
                this->applyVisibility(itB,false);
            }
            itB++;
        }
    }


    if(this->contentPanel && bMightUpdateSizes)
    {
        this->updateComposedPanelSize();
    }

    if(bFocus)
        (*it).second.elt->getContentPanel()->SetFocus();

    if(bVisible && (*it).second.btn && dynamic_cast<wxWindow *>(this->getOwner())->IsShown())
    {
        // on s'assure que le bouton correspondant est visible
        if(this->subButtonsPanel && this->subButtonsPanel->GetSizer())
        {
            if(this->subButtonsPanel->GetSizer()->GetItem((*it).second.btn)
                && this->subButtonsPanel->GetSizer()->GetItem((*it).second.btn)->IsShown())
            {
                wxRect rect = this->buttonsPanel->GetSizer()->GetItem(this->subButtonsPanel)->GetRect();
                wxSize cltSize = rect.GetSize();
                int iSpace = (this->orientation == Vertical) ? rect.GetHeight() + rect.GetY() : rect.GetWidth() + rect.GetX();
                iSpace -= (this->orientation == Vertical) ? this->buttonsForwardBtn->GetSize().GetHeight() : this->buttonsForwardBtn->GetSize().GetWidth();

                wxSize sz = (*it).second.btn->GetSize();
                int iRequiredSpace = iSpace - ((this->orientation == Vertical) ? sz.GetHeight() : sz.GetWidth());

                int iPos = (this->orientation == Vertical) ?
                                    (*it).second.btn->GetPosition().y : (*it).second.btn->GetPosition().x;
                while(iPos > iRequiredSpace)
                {
                    int iOldPos = iPos;
                    this->buttonActivated(this->buttonsBackwardBtn);
                    iPos = (this->orientation == Vertical) ?
                            (*it).second.btn->GetPosition().y : (*it).second.btn->GetPosition().x;
                    if(iOldPos == iPos)
                        break;
                }

            }
            else if(this->subButtonsPanel->GetSizer()->GetItem((*it).second.iDisplayOrder))
            {
                while(!this->subButtonsPanel->GetSizer()->GetItem((*it).second.iDisplayOrder)->IsShown())
                    this->buttonActivated(this->buttonsForwardBtn);
            }
        }

        this->updateScrollButtonsState();
    }

    this->bUpdatingVisibleElements = false;
}

/** indique si un élément est visible ou non */
bool TApplicationPanel::isElementVisible(const int iId) const
{
    TPanelElementMap::const_iterator it = this->getElementIterator(iId);
    if(it == this->elements.end())
        return false;
    return (*it).second.bVisible;
}

/** affiche/cache la barre de boutons */
void TApplicationPanel::setButtonsBarVisible(const bool bVisible)
{
    this->bButtonsBarVisible = bVisible;
    if(this->composedPanel)
    {
        this->composedPanel->GetSizer()->Show( this->buttonsPanel , bVisible );
        if(this->buttonsPanel)
            this->buttonsPanel->Show(bVisible);
    }
}

/** affiche/cache le panneau principal */
void TApplicationPanel::setContentPanelVisible(const bool bVisible)
{
    this->bContentPanelVisible = bVisible;
    if(this->composedPanel)
    {
        this->composedPanel->GetSizer()->Show( this->contentPanel , bVisible );
        if(this->contentPanel)
            this->contentPanel->Show(bVisible);
    }
}

/** renvoie le panneau composé apres l'avoir construit si nécessaire */
TPanel * TApplicationPanel::getComposedPanel()
{
    if(!this->composedPanel)
        this->buildComposedPanel();
    return this->composedPanel;
}

/** construit le panneau composé des icones et du contenu actif */
void TApplicationPanel::buildComposedPanel()
{
    this->composedPanel = new TPanel(dynamic_cast<wxWindow *>(this->owner));
    wxBoxSizer * sizer = new wxBoxSizer( (this->orientation == Vertical) ? wxHORIZONTAL : wxVERTICAL);
    this->composedPanel->SetSizer(sizer);
    sizer->SetSizeHints(this->composedPanel);

    TPanel * buttons = this->getButtonsPanel();
    buttons->Reparent(this->composedPanel);
    TPanel * content = this->getContentPanel();
    content->Reparent(this->composedPanel);

    if(this->position == Left || this->position == Top || this->position == Center )
    {
        sizer->Add(buttons,0,wxEXPAND);
        sizer->Add(content,1,wxEXPAND);
    }
    else
    {
        sizer->Add(content,1,wxEXPAND);
        sizer->Add(buttons,0,wxEXPAND);
    }
    this->setButtonsBarVisible(this->bButtonsBarVisible);
    this->setContentPanelVisible(this->bContentPanelVisible);
    this->updateComposedPanelSize();
}

/** renvoie le panneau des icones */
TPanel * TApplicationPanel::getButtonsPanel()
{
    if(!this->buttonsPanel)
        this->buildButtonsPanel();
    return this->buttonsPanel;
}

/** construit le panneau des icones */
void TApplicationPanel::buildButtonsPanel()
{
    // on prépare le panel de base
    this->buttonsPanel = new TPanel(this->composedPanel != NULL ?
                                        static_cast<wxWindow *>(this->composedPanel)
                                        : dynamic_cast<wxWindow *>(this->owner));
    wxBoxSizer * sizer = new wxBoxSizer( (this->orientation == Vertical) ? wxVERTICAL : wxHORIZONTAL);
    this->buttonsPanel->SetSizer(sizer);

    this->buttonsForwardBtn = new TGenButton(this->buttonsPanel);
    this->buttonsForwardBtn->setFlat(true);
    this->buttonsForwardBtn->setBitmap((this->orientation == Vertical) ? getUpArrowBmp() : getLeftArrowBmp());
    this->buttonsForwardBtn->addButtonListener(this);
    sizer->Add(this->buttonsForwardBtn, 0, wxEXPAND);

    this->subButtonsPanel = new TPanel(this->buttonsPanel);
    wxBoxSizer * subSizer = new wxBoxSizer( (this->orientation == Vertical) ? wxVERTICAL : wxHORIZONTAL);
    this->subButtonsPanel->SetSizer(subSizer);


    // on ordonne les éléments avant de faire les boutons
    TPanelElementMap::iterator itB = this->elements.begin();
    TPanelElementMap::iterator itE = this->elements.end();
    TPanelElementOrderedList orderedList;
    while(itB != itE)
    {
        if(!(*itB).second.elt->isAlwaysVisible())
            orderedList.push((*itB).second);
        itB++;
    }

    // on prépare les paramètres des boutons
    TGenButton::BitmapSide bmpSide;
    TGenButton::TextOrientation textOrientation;
    switch(this->position)
    {
        case Left :
            bmpSide = TGenButton::Bottom;
            textOrientation = TGenButton::BottomToTop;
            break;
        case Right :
            bmpSide = TGenButton::Top;
            textOrientation = TGenButton::TopToBottom;
            break;
        default :
            bmpSide = TGenButton::Left;
            textOrientation = TGenButton::LeftToRight;
            break;
    }

    // on crée les boutons
    while(!orderedList.empty())
    {
        if(!orderedList.top().elt->isAlwaysVisible())
        {
            TPanelElement * elt = orderedList.top().elt;
            TGenButton * btn = new TGenButton(this->subButtonsPanel);
            btn->setBitmapSide(bmpSide);
            btn->setTextOrientation(textOrientation);
            btn->setFlat(true);
            btn->setToggleable(true);
            btn->setToggled(orderedList.top().bVisible);
            btn->setText(elt->getName());
            if(elt->getTooltip().length())
                btn->SetToolTip(elt->getTooltip());
            btn->setBitmap(elt->getBitmap());
            btn->SetId(elt->getID());
            btn->addButtonListener(this);

            subSizer->Add(btn,0,wxEXPAND);

            TGenButton * closeBtn = NULL;
            if(elt->mightShowClosingCross())
            {
                closeBtn = new TGenButton(this->subButtonsPanel);
                closeBtn->setFlat(true);
                closeBtn->setToggleable(false);
                closeBtn->SetToolTip(wxTr("Close"));
                closeBtn->setBitmap(getCrossBmp());
                closeBtn->addButtonListener(this);
                closeBtn->SetId(elt->getID());

                subSizer->Add(closeBtn,0,wxEXPAND);
            }

            this->elements[orderedList.top().elt->getID()].btn = btn;
            this->elements[orderedList.top().elt->getID()].closeBtn = closeBtn;
        }
        orderedList.pop();
    }
//    subSizer->AddStretchSpacer();
    subSizer->SetSizeHints(this->subButtonsPanel);

    sizer->Add(this->subButtonsPanel,1,wxEXPAND);

    this->buttonsForwardBtn2 = new TGenButton(this->buttonsPanel);
    this->buttonsForwardBtn2->setFlat(true);
    this->buttonsForwardBtn2->setBitmap((this->orientation == Vertical) ? getUpArrowBmp() : getLeftArrowBmp());
    this->buttonsForwardBtn2->addButtonListener(this);
    sizer->Add(this->buttonsForwardBtn2, 0, wxEXPAND);

    this->buttonsBackwardBtn = new TGenButton(this->buttonsPanel);
    this->buttonsBackwardBtn->setFlat(true);
    this->buttonsBackwardBtn->setBitmap((this->orientation == Vertical) ? getDownArrowBmp() : getRightArrowBmp());
    this->buttonsBackwardBtn->addButtonListener(this);
    sizer->Add(this->buttonsBackwardBtn, 0, wxEXPAND);

    const TBitmap * showBmp = NULL;
    switch(this->getPosition())
    {
        case Left:
            showBmp = getRightArrowBmp();
            break;
        case Right:
            showBmp = getLeftArrowBmp();
            break;
        case Bottom:
            showBmp = getUpArrowBmp();
            break;
        default:
            showBmp = getDownArrowBmp();
    }

    this->buttonsShowBtn = new TGenButton(this->buttonsPanel);
    this->buttonsShowBtn->setFlat(true);
    this->buttonsShowBtn->setBitmap(showBmp);
    this->buttonsShowBtn->addButtonListener(this);
    sizer->Add(this->buttonsShowBtn, 0, wxEXPAND);
    this->buttonsShowBtn->Connect( wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TApplicationPanel::onMenuClick), NULL, this );
}

/** renvoie le panneau des contenus actifs */
TPanel * TApplicationPanel::getContentPanel()
{
    if(!this->contentPanel)
        this->buildContentPanel();
    return this->contentPanel;
}

/** construit le panneau des contenus actifs */
void TApplicationPanel::buildContentPanel()
{
    // on prépare le panel de base
    this->contentPanel = new TPanel(this->composedPanel != NULL ?
                                        static_cast<wxWindow *>(this->composedPanel)
                                        : dynamic_cast<wxWindow *>(this->owner));
    wxBoxSizer * sizer = new wxBoxSizer( (this->orientation == Vertical) ? wxVERTICAL : wxHORIZONTAL);
    this->contentPanel->SetSizer(sizer);
    sizer->SetSizeHints(this->contentPanel);

    // on ordonne les éléments avant de faire les panneaux
    TPanelElementMap::iterator itB = this->elements.begin();
    TPanelElementMap::iterator itE = this->elements.end();
    TPanelElementOrderedList orderedList;
    while(itB != itE)
    {
//        (*itB).second.elt->discardContentPanel();
        orderedList.push((*itB).second);
        itB++;
    }

    wxColour titleBgColour = wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW);
    wxColour titleFgColour = wxSystemSettings::GetColour(wxSYS_COLOUR_3DHIGHLIGHT);

    this->iVisibleElements = 0;
    // on crée les panneaux
    while(!orderedList.empty())
    {
        TPanel * container = new TPanel(this->contentPanel);
        wxBoxSizer * containerSizer = new wxBoxSizer( wxVERTICAL );
        container->SetSizer(containerSizer);
        this->elements[orderedList.top().elt->getID()].container = container;

        if(this->displayMode != Exclusive && orderedList.top().elt->mightShowTitleBar())
        {
            TPanel * titleBg = new TPanel(container);
            wxBoxSizer * titleSizer = new wxBoxSizer( wxVERTICAL );
            titleBg->SetSizer(titleSizer);
            titleBg->SetBackgroundColour(titleBgColour);

            wxStaticText * title = new wxStaticText(titleBg,-1, orderedList.top().elt->getName(),
                    wxDefaultPosition,wxDefaultSize,wxALIGN_CENTRE);
            title->SetForegroundColour(titleFgColour);
            titleSizer->Add(title,0,wxEXPAND | wxALIGN_CENTER_HORIZONTAL);

            this->elements[orderedList.top().elt->getID()].title = title;

            containerSizer->Add(titleBg,0,wxEXPAND | wxALIGN_CENTER_HORIZONTAL | wxALL, 1);
        }
        TPanel * eltPanel = orderedList.top().elt->getContentPanel();
        eltPanel->Reparent(container);
        containerSizer->Add(eltPanel,1,wxEXPAND | wxALIGN_CENTER);

        sizer->Add(container,orderedList.top().elt->getExtensivity(),wxEXPAND | wxALL);
        sizer->Show(container,orderedList.top().bVisible);
        if(orderedList.top().bVisible)
            this->iVisibleElements++;

        orderedList.pop();
    }
    this->updateComposedPanelSize();
}

/** défini la position du panneau dans l'application. Ceci n'est qu'a titre indicatif pour la construction du panneau : ca ne le déplace pas */
void TApplicationPanel::setPosition(Position pos)
{
    Position prevPos = this->position;
    this->position = pos;
    Orientation prevOrient = this->orientation;
    if(pos == Left || pos == Right)
        this->orientation = Vertical;
    else
        this->orientation = Horizontal;
    // si on a un gros changement d'orientation on refait tout
    if(prevOrient != this->orientation)
        this->discardComposedPanel();
    else // si c'est juste un changement de face on refait la barre d'icones pour que les textes s'orientent bien
        if(prevPos != this->position)
            this->discardButtonsPanel();
}

/** détruit les panneaux d'icones et de contenu */
void TApplicationPanel::discardAll()
{
    if(!this->composedPanel)
        return;

    this->destroyButtonsPanel();
    TPanel * oldContentPanel = this->contentPanel;
    this->destroyContentPanel();

    wxSizer * sizer = this->composedPanel->GetSizer();
    if(this->position == Left || this->position == Top || this->position == Center )
    {
        sizer->Add(this->getButtonsPanel(),0,wxEXPAND|wxALL);
        sizer->Add(this->getContentPanel(),1,wxEXPAND|wxALL);
    }
    else
    {
        sizer->Add(this->getContentPanel(),1,wxEXPAND|wxALL);
        sizer->Add(this->getButtonsPanel(),0,wxEXPAND|wxALL);
    }

    sizer->Layout();

     if(oldContentPanel)
         delete oldContentPanel;
}

/** détruit le panneau d'icones */
void TApplicationPanel::destroyButtonsPanel()
{
    if(!this->buttonsPanel)
        return;

    if(this->composedPanel)
        this->composedPanel->GetSizer()->Detach(this->buttonsPanel);
    this->clearButtonsRefs();
    this->buttonsPanel->GetSizer()->Detach(this->subButtonsPanel);
    delete this->subButtonsPanel;
    this->subButtonsPanel = NULL;
    delete this->buttonsPanel;
    this->buttonsPanel = NULL;
}

/** mets le panneau de contenu de coté (penser a mémoriser sa référence) */
void TApplicationPanel::destroyContentPanel()
{
    if(!this->contentPanel)
        return;
    if(this->composedPanel)
        this->composedPanel->GetSizer()->Detach(this->contentPanel);
    this->contentPanel = NULL;
}

/** reconstruit le panneau des icones */
void TApplicationPanel::discardButtonsPanel()
{
    if(!this->buttonsPanel)
        return;

    this->destroyButtonsPanel();

    if(this->composedPanel)
    {
        this->composedPanel->GetSizer()->Insert((this->position == Left || this->position == Top || this->position == Center) ? 0 : 1,this->getButtonsPanel(), 0, wxEXPAND | wxALL);
        this->composedPanel->GetSizer()->Layout();
    }
}

/** reconstruit le panneau des contenus si nécessaire */
void TApplicationPanel::discardContentPanel()
{
    if(!this->contentPanel)
        return;

    TPanel * oldContentPanel = this->contentPanel;
    this->destroyContentPanel();

    if(this->composedPanel)
    {
        this->composedPanel->GetSizer()->Insert((this->position == Left || this->position == Top || this->position == Center) ? 1 : 0,this->getContentPanel(), 1, wxEXPAND | wxALL);
        this->composedPanel->GetSizer()->Layout();
    }
    delete oldContentPanel;
}

/** reconstruit le panneau complet */
void TApplicationPanel::discardComposedPanel()
{
    if(!this->composedPanel)
        return;

    TPanel * oldComposedPanel = this->composedPanel;
    this->composedPanel = NULL;
    this->buildComposedPanel();
    delete oldComposedPanel;
}

/** un bouton a été basculé */
void TApplicationPanel::buttonToggled(TGenButton * btn)
{
    // si on a des éléments visibles, on mémorise la taille du Panel
    if(this->iVisibleElements)
        this->panelSize = this->getComposedPanel()->GetSize();

    // on empeche de cacher le dernier element visible
    if(this->bMightHaveNoVisibleElement || btn->isToggled() || (this->iVisibleElements > 1))
    {
        this->setElementVisible(btn->GetId(),btn->isToggled());
    }
    else
    {
        btn->setToggled(true);
    }
}

/** mets a jour la taille du panneau */
void TApplicationPanel::updateComposedPanelSize()
{
    if(!this->composedPanel)
        return;

    this->setButtonsBarVisible(this->subButtonsPanel && this->subButtonsPanel->GetSizer()->GetItem(size_t(0)) != NULL);
    this->setContentPanelVisible(this->iVisibleElements > 0);

    wxSize sizeMin(1,1);
    wxSize size(1,1);
    wxSize sizeMax(1,1);
    if(this->elements.size())
    {
        if(! this->iVisibleElements)
        {
            size = this->getButtonsPanel()->GetBestSize();
            sizeMin = size;
            sizeMax = size;
        }
        else
        {
            wxSize buttonsSize = this->getButtonsPanel()->GetMinSize();
            wxSize contentMinSize = this->getContentPanel()->GetMinSize();
            wxSize contentBestSize = this->getContentPanel()->GetBestSize();
            wxSize contentMaxSize = this->getContentPanel()->GetMaxSize();

            if(this->orientation == Vertical)
            {
                sizeMin.SetWidth(buttonsSize.GetWidth() + contentMinSize.GetWidth());
                if(this->panelSize.GetWidth() < (contentBestSize.GetWidth() + buttonsSize.GetWidth()))
                    this->panelSize.SetWidth(buttonsSize.GetWidth() + contentBestSize.GetWidth());
                if(contentMaxSize.GetWidth() >= 0)
                    sizeMax.SetWidth(buttonsSize.GetWidth() + contentMaxSize.GetWidth());
                else
                    sizeMax.SetWidth(contentMaxSize.GetWidth());
            }
            else
            {
                sizeMin.SetHeight(buttonsSize.GetHeight() + contentMinSize.GetHeight());
                if(this->panelSize.GetHeight() < (contentBestSize.GetHeight() + buttonsSize.GetHeight()))
                    this->panelSize.SetHeight(buttonsSize.GetHeight() + contentBestSize.GetHeight());
                if(contentMaxSize.GetHeight() >= 0)
                    sizeMax.SetHeight(buttonsSize.GetHeight() + contentMaxSize.GetHeight());
                else
                    sizeMax.SetHeight(contentMaxSize.GetHeight());
            }

            size = this->panelSize;
        }
    }
    if(this->orientation == Vertical)
    {
        sizeMin.SetHeight(-1);
        this->panelSize.SetHeight(-1);
        sizeMax.SetHeight(-1);
    }
    else
    {
        sizeMin.SetWidth(-1);
        this->panelSize.SetWidth(-1);
        sizeMax.SetWidth(-1);
    }

    this->composedPanel->SetMinSize(sizeMin);
    this->composedPanel->SetMaxSize(sizeMax);

    static const TSplitter splitter;
    if(this->composedPanel->GetParent()->IsKindOf(splitter.GetClassInfo()))
    {
        TSplitter * splitter = static_cast<TSplitter *>(this->composedPanel->GetParent());
        long iSashPosition = -1;
        switch(this->position)
        {
            case Top:
                iSashPosition = size.GetHeight();
                break;
            case Left:
                iSashPosition = size.GetWidth();
                break;
            case Right:
                iSashPosition = splitter->GetSize().GetWidth() - size.GetWidth();
                break;
            case Bottom:
                iSashPosition = splitter->GetSize().GetHeight() - size.GetHeight();
                break;
            case Center:
                // on prend toute la place disponnible
                size.SetWidth(splitter->GetSashPosition());
                size.SetHeight(splitter->GetSize().GetHeight());
                break;
        }
        if(iSashPosition >= 0)
        {
            splitter->SetSashPosition(iSashPosition);
            splitter->setLocked(!this->iVisibleElements);
        }
    }

    // pour éviter les erreurs X11
    if((this->orientation == Vertical && size.GetWidth() != 0)
        || (this->orientation == Horizontal && size.GetHeight() != 0))
    {
        this->composedPanel->SetSize(size);
    }

    if(this->buttonsPanel)
        this->buttonsPanel->GetSizer()->Layout();
    if(this->contentPanel)
        this->contentPanel->GetSizer()->Layout();
    this->composedPanel->GetSizer()->Layout();

}

/** détruit les références aux boutons de la barre */
void TApplicationPanel::clearButtonsRefs()
{
    TPanelElementMap::iterator itB = this->elements.begin();
    TPanelElementMap::iterator itE = this->elements.end();
    while(itB != itE)
    {
        (*itB).second.btn = NULL;
        itB++;
    }
}

/** mets a jour le bouton dans le panneau */
void TApplicationPanel::panelElementNameUpdated(const TPanelElement * elt)
{
    if(!this->buttonsPanel)
        return;

    TPanelElementMap::iterator it = this->getElementIterator(elt->getID());
    if(it == this->elements.end())
        return;
    if((*it).second.btn)
    {
        (*it).second.btn->setText(elt->getName());
    }

    if((*it).second.title)
    {
        (*it).second.title->SetLabel(elt->getName());
    }

    this->buttonsPanel->GetSizer()->Layout();

    this->fireElementDataChanged(elt);
}

/** mets a jour le bouton dans le panneau */
void TApplicationPanel::panelElementTooltipUpdated(const TPanelElement * elt)
{
    if(!this->buttonsPanel)
        return;

    TPanelElementMap::iterator it = this->getElementIterator(elt->getID());
    if(it == this->elements.end())
        return;
    if((*it).second.btn)
    {
        if(elt->getTooltip().length())
            (*it).second.btn->SetToolTip(elt->getTooltip());
        else
            (*it).second.btn->SetToolTip(NULL);
    }

    this->buttonsPanel->GetSizer()->Layout();

    this->fireElementDataChanged(elt);
}

/** mets a jour de l'icone du composant dans le panneau */
void TApplicationPanel::panelElementBitmapUpdated(const TPanelElement * elt)
{
    if(!this->buttonsPanel)
        return;

    TPanelElementMap::iterator it = this->getElementIterator(elt->getID());
    if(it == this->elements.end())
        return;
    if((*it).second.btn)
    {
        (*it).second.btn->setBitmap(elt->getBitmap());
    }

    this->buttonsPanel->GetSizer()->Layout();

    this->fireElementDataChanged(elt);
}

/** mets a jour la taille du composant dans le panneau */
void TApplicationPanel::panelElementSizeUpdated(const TPanelElement * elt)
{
    if(!this->contentPanel)
        return;

    this->contentPanel->GetSizer()->Layout();
}

/**
 * renvoie le premier element marque comme visible
 * @param bMightBeAlwaysVisible indique si un element marque comme toujours visible peut etre retourne
 * @return NULL si aucun element correspondant aux criteres n'a ete trouve
 */
TPanelElement * TApplicationPanel::getFirstVisibleElement(bool bMightBeAlwaysVisible)
{
    TPanelElementMap::iterator itB = this->elements.begin();
    TPanelElementMap::iterator itE = this->elements.end();
    while(itB != itE)
    {
        if((*itB).second.bVisible && (*itB).second.elt->isAlwaysVisible() == bMightBeAlwaysVisible)
            return (*itB).second.elt;
        itB++;
    }
    return NULL;
}

/** mets a jour l'ordre d'affichage d'un élément */
void TApplicationPanel::setElementDisplayOrder(TPanelElement * elt, int iDisplayOrder, bool bUpdatePanel)
{
    if(iDisplayOrder < 0)
        iDisplayOrder = 999999999;

    this->orderedElements.erase(this->orderedElements.begin(),this->orderedElements.end());

    this->elements[elt->getID()].iDisplayOrder = iDisplayOrder;

    TPanelElementMap::iterator itB = this->elements.begin();
    TPanelElementMap::iterator itE = this->elements.end();
    while(itB != itE)
    {
        if((*itB).second.iDisplayOrder >= iDisplayOrder && (*itB).second.elt != elt)
            (*itB).second.iDisplayOrder++;
        itB++;
    }

    this->updateDisplayMap();

    if(bUpdatePanel)
    {
        this->discardButtonsPanel(); // MAJ de la barre de boutons
        this->discardContentPanel(); // MAJ du panneau de contenu
    }
}

/** actualise la map indexée par ordre d'affichage */
void TApplicationPanel::updateDisplayMap()
{
    TPanelElementMap::iterator itB = this->elements.begin();
    TPanelElementMap::iterator itE = this->elements.end();
    TPanelElementOrderedList orderedList;
    while(itB != itE)
    {
        orderedList.push((*itB).second);
        itB++;
    }
    TPanelElement * elt;
    int iDisplayOrder = 0;
    this->orderedElements.erase(this->orderedElements.begin(),this->orderedElements.end());
    // on crée les boutons
    while(!orderedList.empty())
    {
        elt = orderedList.top().elt;
        orderedList.pop();
        this->elements[elt->getID()].iDisplayOrder = iDisplayOrder;
        this->orderedElements[iDisplayOrder] = elt;
        iDisplayOrder++;
    }
}

/** renvoie l'élément situé à un index */
TPanelElement * TApplicationPanel::getElementAt(long iIdx) const
{
    TElementsDisplayMap::const_iterator elt = this->orderedElements.find(iIdx);
    if(elt == this->orderedElements.end())
        return NULL;
    return (*elt).second;
}

/** charge les parametres du composant a partir des informations contenues dans le noeud passï¿½ en paramï¿½tre */
bool TApplicationPanel::loadParameters(TiXmlElement * parametersNode)
{
    TiXmlElement * elt = parametersNode->FirstChildElement(XMLCC_NODE_PANEL_SIZE);
    if(elt)
    {
        int iSize = atoi(elt->GetText());
        if(this->orientation == Vertical)
            this->panelSize.SetWidth(iSize);
        else
            this->panelSize.SetHeight(iSize);
    }
    return true;
}
/** renvoie les parametres du composant sous la forme d'un noeud xml */
TiXmlElement * TApplicationPanel::getParameters()
{
    TiXmlElement * root = XmlConfiguredComponent::getParameters();
    if(!root)
        root = new TiXmlElement(XMLCC_NODE_PANEL_PARAMS);
    if(this->position == Center)
        return root;

    TiXmlElement * elt = new TiXmlElement(XMLCC_NODE_PANEL_SIZE);
    elt->LinkEndChild(new TiXmlText(
            convertIntToString( this->orientation == Vertical ?
                                  this->panelSize.GetWidth()
                                : this->panelSize.GetHeight())));
    root->LinkEndChild(elt);

    return root;
}


/** charge les éléments d'un panneau a partir des informations stockés sous forme XML */
bool TApplicationPanel::loadElements(TiXmlElement * eltsNode)
{
    TiXmlElement * elt = eltsNode->FirstChildElement(XMLCC_NODE_ELEMENT);
    while(elt)
    {
        int iId = 0;
        if(elt->QueryIntAttribute( XMLCC_ATTR_ELEMENT_ID , & iId ) != TIXML_SUCCESS )
            return false;

        const char * szAttr = elt->Attribute( XMLCC_ATTR_ELEMENT_VIS );
        if((!szAttr) || (!szAttr[0]))
            return false;
        bool bVisible = (strcmp(szAttr,"true") == 0);

//         int iDisplayOrder = 0;
//         if(elt->QueryIntAttribute( XMLCC_ATTR_ELEMENT_ORD , & iId ) != TIXML_SUCCESS )
//             return false;

        const char * szType = elt->Attribute( XMLCC_ATTR_ELEMENT_TYPE );
        if((!szType) || (!szType[0]))
            return false;

        const char * szName = elt->Attribute( XMLCC_ATTR_ELEMENT_NAME );

        TPanelElement * panelElt = this->getElement(iId);
        if(!panelElt )
            panelElt = CurrentApplication()->builElement(szType,this,iId);
        if(panelElt)
        {
            if(szName)
                panelElt->setName(ISO2WX(szName));

            TiXmlElement * params = elt->FirstChildElement(XMLCC_NODE_ELEMENT_PARAMS);
            if(params)
            {
                if(!panelElt->loadParameters(params))
                    return false;
            }
// on a sauvegardé dans l'ordre d'affichage this->setElementDisplayOrder(panelElt,iDisplayOrder,true);
            this->setElementVisible(iId,bVisible);
        }

        elt = elt->NextSiblingElement(XMLCC_NODE_ELEMENT);
    }
    this->updateComposedPanelSize();
    return true;
}

/** sauvegarde les elements d'un panneau en XML */
TiXmlElement * TApplicationPanel::getElementsNode()
{
    if(!this->elements.size())
        return NULL;

    TiXmlElement * root = new TiXmlElement(XMLCC_NODE_PANEL_ELTS);
    TiXmlElement * elt = NULL;

    TElementsDisplayMap::iterator itB = this->orderedElements.begin();
    TElementsDisplayMap::iterator itE = this->orderedElements.end();
    while(itB != itE)
    {
        if((*itB).second->mightBeSaved())
        {
            elt = new TiXmlElement(XMLCC_NODE_ELEMENT);

            elt->SetAttribute( XMLCC_ATTR_ELEMENT_ID , (*itB).second->getID() );

            elt->SetAttribute( XMLCC_ATTR_ELEMENT_VIS,
                            this->elements[(*itB).second->getID()].bVisible ? "true" : "false" );

            elt->SetAttribute( XMLCC_ATTR_ELEMENT_ORD , (*itB).first );

            elt->SetAttribute( XMLCC_ATTR_ELEMENT_TYPE, (*itB).second->getElementType() );

            elt->SetAttribute( XMLCC_ATTR_ELEMENT_NAME, (const char *)(*itB).second->getName().fn_str() );

            TiXmlElement * params = (*itB).second->getParameters();
            if(params)
                elt->LinkEndChild( params );

            root->LinkEndChild( elt );
            elt = NULL;
        }

        itB++;
    }

    return root;
}

/** un bouton a été activé */
void TApplicationPanel::buttonActivated(TGenButton * btn)
{
    if(btn == this->buttonsBackwardBtn)
    {
        wxSizer * sizer = this->subButtonsPanel->GetSizer();
        int i = 0;
        wxSizerItem * item = sizer->GetItem(i);
        while( item )
        {
            if(item->IsShown())
            {
                if( sizer->GetItem(i + 1) )
                    sizer->Show(i,false);
                sizer->Layout();
                break;
            }
            i++;
            item = sizer->GetItem(i);
        }
    }
    else if((btn == this->buttonsForwardBtn) || (btn == this->buttonsForwardBtn2))
    {
        wxSizer * sizer = this->subButtonsPanel->GetSizer();
        int i = 0;
        wxSizerItem * item = sizer->GetItem(i);
        while( item )
        {
            if(item->IsShown())
            {
                if(i)
                    sizer->Show(i-1,true);
                sizer->Layout();
                break;
            }
            i++;
            item = sizer->GetItem(i);
        }
    }
    else if(btn == this->buttonsShowBtn)
    {
        if(!this->lastButtonsMenu)
        {
            typedef std::map<wxString, TPanelElement *> TEltsByNameMap;
            TEltsByNameMap elts;
            TPanelElementMap::iterator itB = this->elements.begin();
            TPanelElementMap::iterator itE = this->elements.end();
            while(itB != itE)
            {
                elts[(*itB).second.elt->getName()] = (*itB).second.elt;
                itB++;
            }

            this->lastButtonsMenu = new wxMenu();

            TEltsByNameMap::iterator it = elts.begin();
            while(it != elts.end())
            {
                this->lastButtonsMenu->Append((*it).second->getID(),(*it).second->getName(),(*it).second->getTooltip());
                it++;
            }
        }

        this->buttonsShowBtn->PopupMenu(this->lastButtonsMenu);
    }
    else
    {
        TPanelElementMap::iterator it = this->elements.find(btn->GetId());
        if(it != this->elements.end() && (*it).second.closeBtn == btn)
        {
            (*it).second.elt->doCloseElement();
        }
    }
    this->updateScrollButtonsState();
}

void TApplicationPanel::onMenuClick(wxCommandEvent& evt)
{
    this->setElementVisible(evt.GetId(), true);
}


bool TApplicationPanel::addPanelListener(TApplicationPanelListener * l)
{
    return this->listeners.insert(l).second;
}

bool TApplicationPanel::removePanelListener(TApplicationPanelListener * l)
{
    return this->listeners.erase(l) != 0;
}

/** envoie un evenement indiquant qu'un element est ajoute */
void TApplicationPanel::fireAddingElement(TPanelElement * elt)
{
    TApplicationPanelListenerList::iterator itB = this->listeners.begin();
    TApplicationPanelListenerList::iterator itE = this->listeners.end();

    while(itB != itE)
    {
        (*itB)->addingElement(this, elt);
        itB++;
    }
}

/** envoie un evenement indiquant qu'un element devient visible / cache */
void TApplicationPanel::fireElementVisibilityChanged(TPanelElement * elt, bool bVisible)
{
    TApplicationPanelListenerList::iterator itB = this->listeners.begin();
    TApplicationPanelListenerList::iterator itE = this->listeners.end();

    while(itB != itE)
    {
        (*itB)->elementVisibilityChanged(this, elt, bVisible);
        itB++;
    }
}

/** envoie un evenement indiquant qu'un element va etre supprime */
void TApplicationPanel::fireRemovingElement(TPanelElement * elt)
{
    TApplicationPanelListenerList::iterator itB = this->listeners.begin();
    TApplicationPanelListenerList::iterator itE = this->listeners.end();

    while(itB != itE)
    {
        (*itB)->removingElement(this, elt);
        itB++;
    }
}

/** envoie un evenement que les données d'un element ont changé */
void TApplicationPanel::fireElementDataChanged(const TPanelElement * elt)
{
    TApplicationPanelListenerList::iterator itB = this->listeners.begin();
    TApplicationPanelListenerList::iterator itE = this->listeners.end();

    while(itB != itE)
    {
        (*itB)->elementDataChanged(this, elt);
        itB++;
    }
}

/** affiche / cache les boutons de scroll des tabs */
void TApplicationPanel::updateScrollButtonsState()
{
    wxSizer * sizer = this->subButtonsPanel->GetSizer();
    wxSizerItem * item = sizer->GetItem((size_t)0);
    sizer = this->buttonsPanel->GetSizer();
    sizer->Show((size_t)0,!item->IsShown());
    sizer->Show((size_t)2,!item->IsShown());
}

/** un clic droit sur un bouton */
void TApplicationPanel::buttonRightClicked(TGenButton * btn)
{
    TPanelElementMap::iterator it = this->elements.find(btn->GetId());
    if(it != this->elements.end() && (*it).second.btn == btn)
    {
        wxMenu * menu = (*it).second.elt->getPopupMenu();
        btn->PopupMenu(menu);
    }
}


