/**
© Florian Joncour, 2013-2018 florian@zetta-sys.com

Ce logiciel est un programme informatique faisant interface à la bibliothèque
Perspective3D, un outil de modélisation 3D à partir de vues orthographiques 2D.

Ce logiciel est régi par la licence CeCILL-C soumise au droit français et
respectant les principes de diffusion des logiciels libres. Vous pouvez
utiliser, modifier et/ou redistribuer ce programme sous les conditions
de la licence CeCILL-C telle que diffusée par le CEA, le CNRS et l'INRIA
sur le site "http://www.cecill.info".

En contrepartie de l'accessibilité au code source et des droits de copie,
de modification et de redistribution accordés par cette licence, il n'est
offert aux utilisateurs qu'une garantie limitée.  Pour les mêmes raisons,
seule une responsabilité restreinte pèse sur l'auteur du programme,  le
titulaire des droits patrimoniaux et les concédants successifs.

A cet égard  l'attention de l'utilisateur est attirée sur les risques
associés au chargement,  à l'utilisation,  à la modification et/ou au
développement et à la reproduction du logiciel par l'utilisateur étant
donné sa spécificité de logiciel libre, qui peut le rendre complexe à
manipuler et qui le réserve donc à des développeurs et des professionnels
avertis possédant  des  connaissances  informatiques approfondies.  Les
utilisateurs sont donc invités à charger  et  tester  l'adéquation  du
logiciel à leurs besoins dans des conditions permettant d'assurer la
sécurité de leurs systèmes et ou de leurs données et, plus généralement,
à l'utiliser et l'exploiter dans les mêmes conditions de sécurité.

Le fait que vous puissiez accéder à cet en-tête signifie que vous avez
pris connaissance de la licence CeCILL-C, et que vous en avez accepté les
termes.
**/

#include <QThread>
#include <QString>
#include "interface3d.h"
#include "proprietes3d.h"

#include "style.h"

#ifdef DEBUG
#include <QDebug>
#endif

class Proprietes3DPriv
{
    public:
        inline Proprietes3DPriv(Proprietes3D *props=nullptr) : proprietes(props)
        {
        }

        inline ~Proprietes3DPriv()
        {
            ;
        }

        inline void Init()
        {
            if (!proprietes)
            {
                return;
            }
            if (!proprietes->perspective && ! proprietes->scene_p3d)
            {
                return;
            }

            const Perspective3D::PScene3D &scene = (proprietes->scene_p3d) ? (*proprietes->scene_p3d) : proprietes->perspective->SceneConst();
            const Perspective3D::Pmaillage3D &mesh = scene.Maillage();
            proprietes->Proprietes->clear();
            proprietes->Ajout_propriete(proprietes->tr("Temps de génération"), QString::number(scene.TempsGeneration()*0.001, 'f', 2), proprietes->tr("s"));
            proprietes->Ajout_propriete(proprietes->tr("X:Y:Z"), QString::number(scene.DimX()) + " x " + QString::number(scene.DimY()) + " x " + QString::number(scene.DimZ()));
            proprietes->Ajout_propriete(proprietes->tr("Points"), QString::number(mesh.Sommets().size()));
            proprietes->Ajout_propriete(proprietes->tr("Arrêtes"), QString::number(mesh.Filaire().size()));
            proprietes->Ajout_propriete(proprietes->tr("Faces"), QString::number(scene.NbSurfaces()));

            const Perspective3D::PStdVectSurfaces3D &surfaces = mesh.Surfaces();
            const unsigned int n_surfaces = surfaces.size();
#ifdef DEBUG
            int cpt_triangles = 0;
            for(unsigned int i=0; i<n_surfaces; ++i) { cpt_triangles += surfaces[i].NTriangles(); }
            proprietes->Ajout_propriete(proprietes->tr("Triangles"), QString::number(cpt_triangles));
#endif // DEBUG

            proprietes->Ajout_propriete(proprietes->tr("Echelle Solide"), QString::number(scene.Echelle()));
            if (proprietes->perspective)
            {
                proprietes->Ajout_propriete(proprietes->tr("params3D"), QString::number(PENUM_CAST_INT(proprietes->perspective->Parametres().Parametres3d)));
            }

        //    proprietes->Ajout_propriete(tr("Echelle vue"), QString::number(proprietes->VueModele->Echelle()));
#ifdef DEBUG
            const int ident_euler = mesh.Sommets().size() - mesh.Filaire().size() + mesh.Surfaces().size();
            proprietes->Ajout_propriete(proprietes->tr("Relation d'Euler"), QString::number(ident_euler));
#endif // DEBUG
            proprietes->Ajout_propriete(proprietes->tr("Origine X"), QString::number(scene.Centre().X()));
            proprietes->Ajout_propriete(proprietes->tr("Origine Y"), QString::number(scene.Centre().Y()));
            proprietes->Ajout_propriete(proprietes->tr("Origine Z"), QString::number(scene.Centre().Z()));

            if (proprietes->perspective)
            {
                pfloat tolerances[5];
                proprietes->perspective->AssigneTolerances(tolerances[0], tolerances[1], tolerances[2], tolerances[3], tolerances[4]);

                proprietes->Ajout_propriete(proprietes->tr("Tolérance (1)"), QString::number(tolerances[0], 'f'));
                proprietes->Ajout_propriete(proprietes->tr("Tolérance (2)"), QString::number(tolerances[1], 'f'));
                proprietes->Ajout_propriete(proprietes->tr("Tolérance (3)"), QString::number(tolerances[2], 'f'));
                proprietes->Ajout_propriete(proprietes->tr("Tolérance (V)"), QString::number(tolerances[3], 'f'));
                proprietes->Ajout_propriete(proprietes->tr("Tolérance (A)"), QString::number(tolerances[4], 'f'));
            }

            proprietes->listepoints->clear();
            proprietes->listepoints->addItem("-");
            const Perspective3D::PStdVectSommets3D &sommets = mesh.Sommets();
            const unsigned int n_sommets = sommets.size();
            for (unsigned int i = 0; i < n_sommets; ++i)
            {
                const Perspective3D::PSommet3D &p = sommets[i];
                if (p.Actif())
                {
                    proprietes->listepoints->addItem(QString::number(p.Id()));
                }
                else
                {
                    proprietes->listepoints->addItem(QString("~") + QString::number(p.Id()));
                }
            }

            proprietes->listelignes->clear();
            proprietes->listelignes->addItem("-");
            const Perspective3D::PStdVectSegments3D &segments = mesh.Filaire();
            const unsigned int n_segments = segments.size();
            for (unsigned int i = 0; i < n_segments; ++i)
            {
                const Perspective3D::PSegment3D &l = segments[i];
                if (l.Actif())
                {
                    proprietes->listelignes->addItem(QString::number(l.Id()));
                }
                else
                {
                    proprietes->listelignes->addItem(QString("~") + QString::number(l.Id()));
                }
            }

            proprietes->listesurfaces->clear();
            proprietes->listesurfaces->addItem("-");
            // Liste des surfaces
            for(unsigned int i=0; i<n_surfaces; ++i)
            {
                const Perspective3D::PSurface3D &s = surfaces[i];

                if (s.Actif())
                {
                    proprietes->listesurfaces->addItem(QString::number(s.Id()));
                }
                else
                {
                    proprietes->listesurfaces->addItem(QString("~") + QString::number(s.Id()));
                }
            }

            // Liste des solides
            const Perspective3D::PStdVectSolide3D &solides = scene.Solides();
            const unsigned int n_solides = solides.size();

            proprietes->listesolides->clear();
            proprietes->listesolides->addItem("-");
            for(unsigned int i=0; i<n_solides; ++i)
            {
                const Perspective3D::PSolide3D &s = solides[i];
                if (s.Actif())
                {
                    proprietes->listesolides->addItem(QString::number(s.Id()));
                }
                else
                {
                    proprietes->listesolides->addItem(QString("~") + QString::number(s.Id()));
                }
            }

            proprietes->update();
        }

    private:
        Proprietes3D *proprietes;
};

class Proprietes3DPriv_th : public QThread
{
    public:
        inline Proprietes3DPriv_th(Proprietes3D *props) : priv(props)
        {
            ;
        }

        inline ~Proprietes3DPriv_th()
        {
            ;
        }

        inline void run()
        {
            priv.Init();
        }

    private:
        Proprietes3DPriv priv;
};

Proprietes3D::Proprietes3D(Perspective3D::Perspective *persp, Perspective3D::PScene3D *scene, int id_style, Interface3d *parent) : QWidget(parent),
    perspective(persp), scene_p3d(scene), Parent(parent),
    priv_th(nullptr)
{
//    qRegisterMetaType<Proprietes3DPriv>();

    horizontalLayout_infos = new QHBoxLayout(this);
    horizontalLayout_infos->setSpacing(3);

    layoutconsole = new QVBoxLayout();
    console = new QPlainTextEdit;
    QLabel *label_console = new QLabel(this);
    label_console->setMaximumHeight(20);
    label_console->setText(tr("Console"));
    layoutconsole->addWidget(label_console);
    console->setReadOnly(true);
    console->setContextMenuPolicy(Qt::NoContextMenu);
    layoutconsole->addWidget(console);
    horizontalLayout_infos->addLayout(layoutconsole);

    espacement1 = new QSpacerItem(10, 10, QSizePolicy::Fixed, QSizePolicy::Fixed);
    horizontalLayout_infos->addItem(espacement1);

    Separateur_console = new QFrame(this);
    Separateur_console->setFrameShape(QFrame::VLine);
    Separateur_console->setFrameShadow(QFrame::Sunken);
    Separateur_console->setObjectName("separateur");
    horizontalLayout_infos->addWidget(Separateur_console);

    espacement2 = new QSpacerItem(10, 10, QSizePolicy::Fixed, QSizePolicy::Fixed);
    horizontalLayout_infos->addItem(espacement2);

    layoutinfos_selection = new QVBoxLayout();
    QLabel *label_infos_selection = new QLabel(this);
    label_infos_selection->setMaximumHeight(20);
    label_infos_selection->setText(tr("Sélection"));
    layoutinfos_selection->addWidget(label_infos_selection);
    ted_infos_selection = new QTextEdit(this);
    ted_infos_selection->setReadOnly(true);
    ted_infos_selection->setMaximumWidth(300);
    layoutinfos_selection->addWidget(ted_infos_selection);
    horizontalLayout_infos->addLayout(layoutinfos_selection);

    layoutlistepoints = new QVBoxLayout();
    QLabel *labelpoints = new QLabel(this);
    labelpoints->setMaximumHeight(20);
    labelpoints->setText(tr("Sommets"));
    layoutlistepoints->addWidget(labelpoints);
    listepoints = new QListWidget(this);
    listepoints->setFocusPolicy(Qt::NoFocus);
    listepoints->setMaximumWidth(120);
    layoutlistepoints->addWidget(listepoints);
    horizontalLayout_infos->addLayout(layoutlistepoints);
    connect(listepoints, SIGNAL(currentRowChanged(int)), this, SLOT(SelectionPoint(int)));
    connect(listepoints, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(SelectionPoint(QListWidgetItem*)));
    connect(listepoints, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(SupprimePoint(QListWidgetItem*)));

    layoutlistelignes = new QVBoxLayout();
    QLabel *labellignes = new QLabel(this);
    labellignes->setMaximumHeight(20);
    labellignes->setText(tr("Segments"));
    layoutlistelignes->addWidget(labellignes);
    listelignes = new QListWidget(this);
    listelignes->setFocusPolicy(Qt::NoFocus);
    listelignes->setMaximumWidth(120);
    layoutlistelignes->addWidget(listelignes);
    horizontalLayout_infos->addLayout(layoutlistelignes);
    connect(listelignes, SIGNAL(currentRowChanged(int)), this, SLOT(SelectionSegment(int)));
    connect(listelignes, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(SelectionSegment(QListWidgetItem*)));
    connect(listelignes, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(SupprimeLigne(QListWidgetItem*)));

    layoutlistesurfaces = new QVBoxLayout();
    QLabel *label_listesurfaces = new QLabel(this);
    label_listesurfaces->setMaximumHeight(20);
    label_listesurfaces->setText(tr("Faces"));
    listesurfaces = new QListWidget(this);
    listesurfaces->setFocusPolicy(Qt::NoFocus);
    listesurfaces->setMaximumWidth(120);
    layoutlistesurfaces->addWidget(label_listesurfaces);
    layoutlistesurfaces->addWidget(listesurfaces);
    horizontalLayout_infos->addLayout(layoutlistesurfaces);
    connect(listesurfaces, SIGNAL(currentRowChanged(int)), this, SLOT(SelectionSurface(int)));
    connect(listesurfaces, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(SelectionSurface(QListWidgetItem*)));
    connect(listesurfaces, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(SupprimeSurface(QListWidgetItem*)));

    layoutlistesolides = new QVBoxLayout();
    QLabel *label_listesolides = new QLabel(this);
    label_listesolides->setMaximumHeight(20);
    label_listesolides->setText(tr("Ensembles"));
    listesolides = new QListWidget(this);
    listesolides->setFocusPolicy(Qt::NoFocus);
    listesolides->setMaximumWidth(120);
    layoutlistesolides->addWidget(label_listesolides);
    layoutlistesolides->addWidget(listesolides);
    horizontalLayout_infos->addLayout(layoutlistesolides);
    connect(listesolides, SIGNAL(currentRowChanged(int)), this, SLOT(SelectionSolide(int)));
    connect(listesolides, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(SelectionSolide(QListWidgetItem*)));
    connect(listesolides, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(SupprimeSolide(QListWidgetItem*)));

    /* *** */

    layout_props = new QVBoxLayout();
    QLabel *label_props= new QLabel(this);
    label_props->setMaximumHeight(20);
    label_props->setText(tr("Propriétés"));
    layout_props->addWidget(label_props);
    Proprietes = new QListWidget();
    Proprietes->setFocusPolicy(Qt::NoFocus);
    Proprietes->setMaximumWidth(300);
    layout_props->addWidget(Proprietes);
    horizontalLayout_infos->addLayout(layout_props);

    if (id_style)
    {
        ChargeStyleWidget(console, id_style);
        ChargeStyleWidget(ted_infos_selection, id_style);
        ChargeStyleWidget(listepoints, id_style);
        ChargeStyleWidget(listelignes, id_style);
        ChargeStyleWidget(listesurfaces, id_style);
        ChargeStyleWidget(listesolides, id_style);
        ChargeStyleWidget(Proprietes, id_style);
    }
}

Proprietes3D::~Proprietes3D()
{
    delete Separateur_console;
    delete console;
    delete listepoints;
    delete listelignes;
    delete listesurfaces;
    delete ted_infos_selection;
    delete listesolides;
    delete Proprietes;
    delete layoutconsole;
    delete layout_props;
    delete layoutinfos_selection;
    delete layoutlistepoints;
    delete layoutlistesurfaces;
    delete layoutlistelignes;
    delete layoutlistesolides;

    if (priv_th)
    {
        delete priv_th;
    }
}

void Proprietes3D::Nettoyage()
{
    listepoints->clear();
    listelignes->clear();
    listesurfaces->clear();
    listesolides->clear();
    console->clear();
}

void Proprietes3D::AjoutConsole(const QString &s)
{
    console->appendPlainText(s);
}

void Proprietes3D::defTexteConsole(const QString &s)
{
    console->setPlainText(s);
    DefileTexteConsole();
}

void Proprietes3D::DefileTexteConsole() const
{
    console->moveCursor(QTextCursor::End);
    console->ensureCursorVisible();
}

void Proprietes3D::AfficheInfosPoint(const Perspective3D::Ppoint3D &p)
/* Affiche dans l'interface les informations sur un point. */
{
    if ((listepoints->currentRow() != p.Id()+1) && (p.Id() >= 0))
    {
        listepoints->setCurrentRow(p.Id()+1);
        QListWidgetItem *it = listepoints->item(p.Id()+1);
        if (it)
        {
            listepoints->scrollToItem(it, QAbstractItemView::PositionAtCenter);
        }
    }

    const Perspective3D::PCouleur &couleur = p.CouleurConst();
    QString txt_affichage(tr("Point") + "\n[\n");
    txt_affichage += "  " + tr("id") + ": " + QString::number(p.Id()) + "\n";
    txt_affichage += "  " + tr("coordonnés") + ": " + QString::number(p.X()) + ", " + QString::number(p.Y()) + ", " + QString::number(p.Z()) + "\n";
    txt_affichage += "  " + tr("courbe") + ": " + QString::number(p.Courbe()) + "\n";
    txt_affichage += "  " + tr("couleur") + ": " + QString::number(couleur.R()) + + ", " + QString::number(couleur.V()) + ", " + QString::number(couleur.B()) + "\n";
    txt_affichage += "]";
    ted_infos_selection->setText(txt_affichage);
}

void Proprietes3D::AfficheInfosPoint(const Perspective3D::PSommet3D &p)
/* Affiche dans l'interface les informations sur un point. */
{
    if ((listepoints->currentRow() != p.Id()+1) && (p.Id() >= 0))
    {
        listepoints->setCurrentRow(p.Id()+1);
        QListWidgetItem *it = listepoints->item(p.Id()+1);
        if (it)
        {
            listepoints->scrollToItem(it, QAbstractItemView::PositionAtCenter);
        }
    }

    const Perspective3D::Pvec3 &px = p.V1();
    const Perspective3D::PCouleur &couleur = p.CouleurConst();
    QString txt_affichage(tr("Point") + "\n[\n");
    txt_affichage += "  " + tr("id") + ": " + QString::number(p.Id()) + "\n";
    txt_affichage += "  " + tr("coordonnés") + ": " + QString::number(px.X()) + ", " + QString::number(px.Y()) + ", " + QString::number(px.Z()) + "\n";
    txt_affichage += "  " + tr("couleur") + ": " + QString::number(couleur.R()) + + ", " + QString::number(couleur.V()) + ", " + QString::number(couleur.B()) + "\n";
    txt_affichage += "]";
    ted_infos_selection->setText(txt_affichage);
}

void Proprietes3D::AfficheInfosLigne(const Perspective3D::Pligne3D &l)
/* Affiche dans l'interface les informations sur une ligne */
{
    if ((listelignes->currentRow() != l.Id()+1) && (l.Id() >= 0))
    {
        listelignes->setCurrentRow(l.Id()+1);
        QListWidgetItem *it = listelignes->item(l.Id()+1);
        if (it)
        {
            listelignes->scrollToItem(it, QAbstractItemView::PositionAtCenter);
        }
    }

    QString txt_affichage(tr("Ligne") + "\n[\n");
    txt_affichage += "    " + tr("id") + ": " + QString::number(l.Id()) + "\n";
    txt_affichage += "    " + tr("norme") + ": " + QString::number(l.NormL().X()) + ", " + QString::number(l.NormL().Y()) + ", " + QString::number(l.NormL().Z()) + "\n";
    txt_affichage += "    " + tr("courbe") + ": " + QString::number(l.Courbe()) + "\n";

    txt_affichage += "  " + tr("Point") + " 1:\n  [\n";
    txt_affichage += "    " + tr("id") + ": " + QString::number(l.P1Const().Id()) + "\n";
    txt_affichage += "    " + tr("coordonnés") + ": " + QString::number(l.P1Const().X()) + ", " + QString::number(l.P1Const().Y()) + ", " + QString::number(l.P1Const().Z()) + "\n";
//    txt_affichage += "    " + tr("connexions lignes") + ": " + QString::number(l.P1Const().ConnexionLignes()) + "\n"; // Ajoute le comptage de connexions (à garder pour la version finale ?)
    txt_affichage += "    " + tr("courbe") + ": " + QString::number(l.P1Const().Courbe()) + "\n";
    txt_affichage += "    " + tr("trou") + ": " + QString::number(l.P1Const().Trou()) + "\n";

    txt_affichage += "  ]\n";
    txt_affichage += "  " + tr("Point") + " 2:\n  [\n";
    txt_affichage += "    " + tr("id") + ": " + QString::number(l.P2Const().Id()) + "\n";
    txt_affichage += "    " + tr("coordonnés") + ": " + QString::number(l.P2Const().X()) + ", " + QString::number(l.P2Const().Y()) + ", " + QString::number(l.P2Const().Z()) + "\n";
//    txt_affichage += "    " + tr("connexions lignes") + ": " + QString::number(l.P2Const().ConnexionLignes()) + "\n"; // Ajoute le comptage de connexions (à garder pour la version finale ?)
    txt_affichage += "    " + tr("courbe") + ": " + QString::number(l.P2Const().Courbe()) + "\n";
    txt_affichage += "    " + tr("trou") + ": " + QString::number(l.P2Const().Trou()) + "\n";
    txt_affichage += "  ]\n";
    txt_affichage += "]";

    ted_infos_selection->setText(txt_affichage);
}

void Proprietes3D::AfficheInfosLigne(const Perspective3D::PSegment3D &l)
/* Affiche dans l'interface les informations sur une ligne */
{
    if ((listelignes->currentRow() != l.Id()+1) && (l.Id() >= 0))
    {
        listelignes->setCurrentRow(l.Id()+1);
        QListWidgetItem *it = listelignes->item(l.Id()+1);
        if (it)
        {
            listelignes->scrollToItem(it, QAbstractItemView::PositionAtCenter);
        }
    }

    QString txt_affichage(tr("Ligne") + "\n[\n");
    txt_affichage += "    " + tr("id") + ": " + QString::number(l.Id()) + "\n";
    txt_affichage += "    " + tr("courbe") + ": " + QString::number(l.Courbe()) + "\n";

    txt_affichage += "  " + tr("Point") + " 1:\n  [\n";
    txt_affichage += "    " + tr("id") + ": " + QString::number(l.V1().Id()) + "\n";
    txt_affichage += "    " + tr("coordonnés") + ": " + QString::number(l.V1().X()) + ", " + QString::number(l.V1().Y()) + ", " + QString::number(l.V1().Z()) + "\n";

    txt_affichage += "  ]\n";
    txt_affichage += "  " + tr("Point") + " 2:\n  [\n";
    txt_affichage += "    " + tr("id") + ": " + QString::number(l.V2().Id()) + "\n";
    txt_affichage += "    " + tr("coordonnés") + ": " + QString::number(l.V2().X()) + ", " + QString::number(l.V2().Y()) + ", " + QString::number(l.V2().Z()) + "\n";
    txt_affichage += "  ]\n";
    txt_affichage += "]";

    ted_infos_selection->setText(txt_affichage);
}


void Proprietes3D::AfficheInfosSurface(const Perspective3D::PSurface3D &s)
/* Affiche les infos d'une surface */
{
    if ((listesurfaces->currentRow() != s.Id()+1) && (s.Id() >= 0))
    {
        listesurfaces->setCurrentRow(s.Id()+1);
        QListWidgetItem *it = listesurfaces->item(s.Id()+1);
        if (it)
        {
            listesurfaces->scrollToItem(it, QAbstractItemView::PositionAtCenter);
        }
    }

    QString txt_affichage(tr("Surface") + "\n[\n");
    txt_affichage += "  " + tr("id") + ": " + QString::number(s.Id()) + "\n";
    txt_affichage += "  " + tr("triangles") + ": " + QString::number(s.TrianglesConst().size()) + "\n";
    txt_affichage += "  " + tr("norme") + ": " + QString::number(s.Norm().X()) + ", " + QString::number(s.Norm().Y()) + ", " + QString::number(s.Norm().Z()) + "\n";
    txt_affichage += "  " + tr("centre") + ": " + QString::number(s.Centre().X()) + ", " + QString::number(s.Centre().Y()) + ", " + QString::number(s.Centre().Z()) + "\n";

    // Affichage des ids des lignes 3D:
    txt_affichage += "  " + tr("Lignes contour") + ": (" + QString::number(s.ContourConst().size()) + ")\n  [\n";
    for(unsigned int i=0; i<s.ContourConst().size(); ++i)
    {
        const Perspective3D::PSegment3D &l = s.ContourConst()[i];
        txt_affichage += "    " + QString::number(i) + ": " + QString::number(l.Id()) + " (" + QString::number(l.V1().Id()) + ":" + QString::number(l.V2().Id()) + ")" + "\n";
    }
    txt_affichage += "  ]\n";

    // Affichage des ids des points 3D:
    txt_affichage += "  " + tr("Sommets contour") + ": (" + QString::number(s.ContourConst().size()) + ")\n  [\n";
    for(unsigned int i=0; i<s.ContourConst().size(); ++i)
    {
        txt_affichage += "    " + QString::number(i) + ": " + QString::number(s.ContourConst()[i].V1().Id()) + "\n";
    }
    txt_affichage += "  ]\n";
    txt_affichage += "]";
    ted_infos_selection->setText(txt_affichage);
}

void Proprietes3D::AfficheInfosSolide(const Perspective3D::PSolide3D &s, int id)
{
    if ((listesolides->currentRow() != id+1) && (id >= 0))
    {
        listesolides->setCurrentRow(id+1);
        QListWidgetItem *it = listesolides->item(id+1);
        if (it)
        {
            listesolides->scrollToItem(it, QAbstractItemView::PositionAtCenter);
        }
    }

    QString txt_affichage(tr("Solide") + "\n[\n");
    const pint n = s.NombreSurfaces();
    txt_affichage += "  " + tr("Faces") + ": (" + QString::number(n) + ")\n  [\n";
    for(int i=0; i<n; ++i)
    {
        txt_affichage += "    " + QString::number(s.Surfaces()[i]) + "\n";
    }
    txt_affichage += "  ]\n]";
    ted_infos_selection->setText(txt_affichage);
}

void Proprietes3D::AfficheProprietesScene_th()
/* Affiche les propritées de la scène (exécution d'un thread). */
{
    if (priv_th)
    {
        if (!priv_th->isRunning())
        {
            priv_th->start();
        }
    }
    else
    {
        priv_th = new Proprietes3DPriv_th(this);
        priv_th->start();
    }
}

void Proprietes3D::AfficheProprietesScene()
/* Affiche les propritées de la scène. */
{
#if 1
    Proprietes3DPriv priv(this);
    priv.Init();
#else
    AfficheProprietesScene_th();
#endif // 0
}

void Proprietes3D::Ajout_propriete(const QString &prop, const QString &val)
{
    Proprietes->addItem(new QListWidgetItem(prop + " : " + val));
}

void Proprietes3D::Ajout_propriete(const QString &prop, const QString &val, const QString &suffix)
{
    Proprietes->addItem(new QListWidgetItem(prop + " : " + val + " " + suffix));
}

void Proprietes3D::SelectionPoint(int id)
{
    Parent->SelectionPoint(id, true, false);
    listepoints->setFocus();
}

void Proprietes3D::SelectionSegment(int id)
{
    Parent->SelectionSegment(id, true, false);
    listelignes->setFocus();
}

void Proprietes3D::SelectionSurface(int id)
{
#ifdef DEBUG
    Parent->SelectionSurface(id, true, true, true);
#else
    Parent->SelectionSurface(id, true, false, true);
#endif // DEBUG
    listesurfaces->setFocus();
}

void Proprietes3D::SelectionSolide(int id)
{
    Parent->SelectionSolide(id);
    listesolides->setFocus();
}

void Proprietes3D::SupprimePoint(QListWidgetItem *it)
{
    Parent->SupprimeRestaurePoint(listepoints->row(it));
}

void Proprietes3D::SupprimeLigne(QListWidgetItem *it)
{
    Parent->SupprimeRestaureLigne(listelignes->row(it));
}

void Proprietes3D::SupprimeSurface(QListWidgetItem *it)
{
    Parent->SupprimeRestaureSurface(listesurfaces->row(it));
}

void Proprietes3D::SupprimeSolide(QListWidgetItem *it)
{
    Parent->SupprimeRestaureSolide(listesolides->row(it));
}

void Proprietes3D::SelectionPoint(QListWidgetItem *it)
{
    Parent->SelectionPoint(listepoints->row(it), true, true);
    listepoints->setFocus();
}

void Proprietes3D::SelectionSegment(QListWidgetItem *it)
{
    Parent->SelectionSegment(listelignes->row(it), true, true);
    listelignes->setFocus();
}

void Proprietes3D::SelectionSurface(QListWidgetItem *it)
{
    SelectionSurface(listesurfaces->row(it));
}

void Proprietes3D::SelectionSolide(QListWidgetItem *it)
{
    SelectionSolide(listesolides->row(it));
}

Proprietes3D_widget::Proprietes3D_widget(Perspective3D::Perspective *persp, Perspective3D::PScene3D *scene, int id_style, Interface3d *parent) :
    QWidget(nullptr), Parent(parent), props_w(new Proprietes3D(persp, scene, id_style, parent))
{
    setMaximumHeight(180);
    setLayout(props_w->layout());
}

void Proprietes3D_widget::showEvent(QShowEvent *ev)
{
    props_w->DefileTexteConsole();
    ev->accept();
}

Proprietes3D_widget::~Proprietes3D_widget()
{
    delete props_w;
}

Proprietes3D_dock::Proprietes3D_dock(Perspective3D::Perspective *persp, Perspective3D::PScene3D *scene, int id_style, Interface3d *parent) :
    CadreDialog(parent), Parent(parent), props_w(new Proprietes3D(persp, scene, id_style, parent))
{
    setWindowIcon(QIcon(":/icones/proprietes3d.png"));
    setWindowTitle(tr("Propriétés"));
    setLayout(props_w->layout());
}

void Proprietes3D_dock::showEvent(QShowEvent *ev)
{
    QPoint pos_interface3d = Parent->window()->pos();
    QSize cadre_interface3d = Parent->window()->size();
    const unsigned int hauteur = 180;
    const unsigned int marge_x = 60;
    const unsigned int marge_y = 10;

    setGeometry(pos_interface3d.x()+marge_x, cadre_interface3d.height()-(hauteur+marge_y), cadre_interface3d.width()-(marge_x*2), hauteur);
    props_w->DefileTexteConsole();
    ev->accept();
}


Proprietes3D_dock::~Proprietes3D_dock()
{
    delete props_w;
}
