/**
© 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 <QCoreApplication>
#include <QMessageBox>
#include <QPrinter>
#include <QPrintDialog>
#include <QScrollBar>
#include <QGraphicsEllipseItem>
#include <QGraphicsRectItem>
#include <QGraphicsItemGroup>
#include <QBuffer>
#include <QDesktopWidget>

#include "fenetrageZ6.h"

#ifdef SUPPORT_GIF
#include "zGIF.h"
#endif

#include "mainwindow.h"
#include "zwidgets.h"
#include "vue2d.h"
#include "BarreOutils2D.h"
#include "th_perspective.h"
#include "interface3d.h"
#include "gestionnairescripts.h"
#include "documentation.h"
#include "parametres.h"
#include "style.h"
#include "API/Qt/Conversion.h"
#include "API/Qt/Utils.h"
#include "dialogbool.h"
#include "dialogint.h"
#include "dialogdouble.h"
#include "dialogqstring.h"
#include "cl_maj.h"
#include "ui_mainwindow.h"

#ifdef PSYS_WINDOWS
typedef QFileDialog Type_DialogFichier;
#else
typedef QFileDialogP Type_DialogFichier;
#endif

const unsigned int IntervalTempoInit = 250;
const unsigned int IntervalTempo = 50;
const int NombreVues2D = 1;

//#define MODE_VUEMULT_STRICT /* Si activé, on n'accepte dans le mode des vues automatiques que des vues validées par la séparatrice, sinon l'utilisateur sera amené à créer les vues les unes à la suite des autres. */

int MesgDialog(QWidget *parent, const QString &titre, const QString &texte, QMessageBox::Icon icone, bool bouton_stop=false, bool non_modal=false)
{
    QMessageBox::StandardButtons bouttons;

    if (icone == QMessageBox::Information)
    {
        bouttons = QMessageBox::Ok;
        if (bouton_stop) /* Spécialement pour les messages de l'automate. */
        {
            bouttons |= QMessageBox::Abort;
        }
    }
    else if (icone == QMessageBox::Warning)
    {
        bouttons = QMessageBox::Ok | QMessageBox::Cancel;
    }
    else if (icone == QMessageBox::Critical)
    {
        bouttons = QMessageBox::Ok | QMessageBox::Abort;
    }
    else if (icone == QMessageBox::Question)
    {
        bouttons = QMessageBox::Yes | QMessageBox::No;
        if (bouton_stop)
        {
            bouttons |= QMessageBox::Cancel;
        }
    }
    else
    {
        bouttons = QMessageBox::Ok;
        if (bouton_stop)
        {
            bouttons |= QMessageBox::Abort;
        }
    }

    QMessageBox mesg(icone, titre, texte, bouttons, parent);

    if (bouttons & QMessageBox::Ok)
    {
        mesg.setButtonText(QMessageBox::Ok, QObject::tr("Ok"));
    }
    if (bouttons & QMessageBox::Cancel)
    {
        mesg.setButtonText(QMessageBox::Cancel, QObject::tr("Annuler"));
    }
    if (bouttons & QMessageBox::Abort)
    {
        if (bouton_stop)
        {
            mesg.setButtonText(QMessageBox::Abort, QObject::tr("Arrêter le script"));
        }
        else
        {
            mesg.setButtonText(QMessageBox::Abort, QObject::tr("Arrêter"));
        }
    }
    if (bouttons & QMessageBox::Yes)
    {
        mesg.setButtonText(QMessageBox::Yes, QObject::tr("Oui"));
    }
    if (bouttons & QMessageBox::No)
    {
        mesg.setButtonText(QMessageBox::No, QObject::tr("Non"));
    }

    if (non_modal)
    {
        mesg.setModal(false);
        mesg.show();
        return 1;
    }

    int ret = mesg.exec();

    return ((ret == QMessageBox::Ok) || (ret == QMessageBox::Yes)) ? (1) :
                (bouton_stop ? (((ret == QMessageBox::Abort) || (ret == QMessageBox::Cancel)) ? -1 : 0) :
                (0));

//    bool r = ((ret == QMessageBox::Ok) || (ret == QMessageBox::Yes));
//    if (r)
//    {
//        return 1;
//    }
//    if (bouton_stop)
//    {
//        if ((ret == QMessageBox::Abort) || (ret == QMessageBox::Cancel))
//            return -1;
//    }
//    return 0;
}

inline QString TronqueChemin(const QString &s, int lg_max=75)
/* Tronque un chemin pour faire en sorte à ce qu'il ne dépasse pas (trop) lg_max caractères. */
{
    if (s.length() <= lg_max)
        return s;

    int pos_char = -1;
    const int lg = s.length();
    const int delta = lg - lg_max;

    if (delta < 2)
    {
        return s;
    }

    QString copie(s);

    for(int i=delta; i<lg; ++i) /* Trouve le dernier séparateur dans la zone à retirer */
    {
        if (s.at(i) == QDir::separator())
        {
            pos_char = i;
            break;
        }
#ifdef Q_OS_WIN
        else if (s.at(i) == QChar('/'))
        {
            pos_char = i;
            break;
        }
#endif // Q_OS_WIN
    }

    if (pos_char == -1)
    {
        pos_char = delta;
    }

    const QString prefix = QString(QDir::separator()) + "...";

    copie.remove(0, pos_char);
    copie.insert(0, prefix);
    return copie;
}

MainWindow::MainWindow(Perspective3D::i18n::lang_p3d_t lang_p3d, const QString &chemin_zetta6, bool gen_mod_auto, const QString &nfichier, int id_style, QWidget *parent) :
    QMainWindow(parent), ui(new Ui::MainWindow),
    Aide(nullptr),
    automate(chemin_zetta6), interpreteur(chemin_zetta6, ConvLangueP3DAutomate(lang_p3d)),
    widget_outils_barre_onglets(nullptr), widget_outils_2d_onglets(nullptr), widget_outils_3d_onglets(nullptr), barre_outils_widget_onglets(nullptr),
    barre_outils_2d_widget_onglets(nullptr), barre_outils_3d_widget_onglets(nullptr), barre_outils_quit_widget_onglets(nullptr),
    cmb_type_affichage_widget_onglets(nullptr),
    #ifdef AFFICHE_SELECTION_COULEUR_SOLIDE_PANORAMIQUE
        cmb_couleurs_scenes_widget_onglets(nullptr),
    #endif // AFFICHE_SELECTION_COULEUR_SOLIDE_PANORAMIQUE
    animation(), exec_import(false),
    persp_scene_2D(nullptr), vue2D(nullptr), scene2D(nullptr), compteur_entites_scene2d(0),
    fichier_demarrage(nfichier), chemin_installation(chemin_zetta6), id_feuille_style(id_style), gen_modele_demarrage(gen_mod_auto)
{
    Init = true;
    id_timer_init = -1;
    id_tempo_affiche_console = -1;
    id_timer_fichier_insert = -1;
    id_tempo_affiche_mesg_status = -1;

    ui->setupUi(this);
//    setWindowIcon(QIcon(GenLogoPerspective3D(false)));

    Perspective3D::i18n::defLangue(lang_p3d);

#ifdef DEBUG
    qDebug() << "Bibliothèque Perspective : " << libPerspective::VersionStr() << "(" << libPerspective::VersionInt() << ")";
#endif // DEBUG

    Aide = new Documentation(chemin_installation, PENUM_CAST_INT(lang_p3d), this);
    connect(Aide, SIGNAL(closeEvent(QCloseEvent *)), this, SLOT(FermetureAide(QCloseEvent *)));
    connect(Aide, SIGNAL(ExecFichier(const QString &)), this, SLOT(ExecFichier_Documentation(const QString &)));
    connect(Aide, SIGNAL(OuvreConfig(const QString &)), this, SLOT(OuvreConfig_Documentation(const QString &)));

#ifdef PARAMETRES_DOCK
    parametres = new Parametres(animation, Aide, lang_p3d, this);
#else
    parametres = new Parametres(animation, Aide, lang_p3d, this);
#endif

    connect(parametres, SIGNAL(FermetureParametres()), this, SLOT(FermetureParametres()));

    animation.ConfigurationTempsReference(parametres->R_TempsReferenceAnimation());
    animation.ConfigurationSouris(parametres->R_VitesseCurseurAnimation(), parametres->R_VitesseClicAnimation());

    parametres->R_PageDocumentation();

    if (parametres->R_AutoriseControleMAJ())
    {
        controle_mise_a_jour = new clMAJ;
        connect(controle_mise_a_jour, SIGNAL(VersionServeur(const QString &)), this, SLOT(ControleVersionServeur(const QString &)));
        controle_mise_a_jour->execControleVersion();
    }
    else
    {
        controle_mise_a_jour = nullptr;
    }

    GestionScripts = new GestionnaireScripts(animation, this);

    ui->statusBar->setMinimumHeight(32);
    ui->statusBar->setMaximumHeight(32);
    widget_barre_status = new QWidget(this);
    layout_barre_status = new QHBoxLayout(widget_barre_status);
    layout_barre_status->setContentsMargins(6, 0, 6, 0);

    w_exec_commandes = new WExecCommandes(widget_barre_status);
    w_exec_commandes->setFocusPolicy(Qt::ClickFocus);
    QStringList liste_commandes_interpreteur;
    if (interpreteur.ListeCommandes(liste_commandes_interpreteur))
    {
        w_exec_commandes->initCompletion(liste_commandes_interpreteur, false);
    }
    connect(w_exec_commandes, SIGNAL(NouvelleCommande(const QString &)), this, SLOT(ExecCommandeInterpreteur(const QString &)));

    label_barre_status_0 = new QLabel(widget_barre_status);
    label_barre_status_0->setAlignment(Qt::AlignCenter);
    label_barre_status_0->setMinimumWidth(150);
    label_barre_status_0->setText("");

    label_barre_status_1 = new QLabel(widget_barre_status);
    label_barre_status_1->setMinimumWidth(150);
    label_barre_status_1->setAlignment(Qt::AlignCenter);
    label_barre_status_1->setText("");

    label_barre_status_2 = new QLabel(widget_barre_status);
    label_barre_status_2->setAlignment(Qt::AlignCenter);
    label_barre_status_2->setMinimumWidth(18);
    label_barre_status_2->setText("");

    label_barre_status_centre = new QLabel(widget_barre_status);
    label_barre_status_centre->setAlignment(Qt::AlignCenter);

    label_barre_status_maj = new QLabel(widget_barre_status);
    label_barre_status_maj->setAlignment(Qt::AlignCenter);
    label_barre_status_maj->setMinimumWidth(18);
    label_barre_status_maj->setText("");

    layout_barre_status->addWidget(w_exec_commandes);
    layout_barre_status->addStretch(0);
    layout_barre_status->addWidget(label_barre_status_centre);
    layout_barre_status->addStretch(0);
    layout_barre_status->addWidget(label_barre_status_0);
    layout_barre_status->addSpacerItem(new QSpacerItem(10,10, QSizePolicy::Fixed, QSizePolicy::Fixed));
    layout_barre_status->addWidget(label_barre_status_1);
    layout_barre_status->addSpacerItem(new QSpacerItem(10,10, QSizePolicy::Fixed, QSizePolicy::Fixed));
    layout_barre_status->addWidget(label_barre_status_maj);
    layout_barre_status->addSpacerItem(new QSpacerItem(10,10, QSizePolicy::Fixed, QSizePolicy::Fixed));
    layout_barre_status->addWidget(label_barre_status_2);

    ui->statusBar->addWidget(widget_barre_status, 16);
//    ui->statusBar->setContentsMargins(6, 0, 6, 0);

//    ui->statusBar->addWidget(widget_barre_status);

    /* Parce qu'impossible à styler sans réimplémenter le dessin de PE_FrameTabBarBase, du coup plus moyen de se servir des feuilles de style, bref c'est le bordel. */
    QTabBar* tab_bar = ui->tabWidget->tabBar();
    tab_bar->setDrawBase(false);
    ui->tabWidget->setTabIcon(OngletFixe, QIcon(":/icones/plan_vide.png"));
    ui->tabWidget->setFocusPolicy(Qt::NoFocus);
    tab_bar->setFocusPolicy(Qt::NoFocus);
    tab_bar->setElideMode(Qt::TextElideMode::ElideMiddle);
    tab_bar->setMovable(false);
    tab_bar->setDocumentMode(true);

    scene2D = new Scene2D(animation, parametres->R_CouleurFond2D(), 0);
    int taille_scene2d = parametres->R_ZoneDessin();
    scene2D->setSceneRect(-(taille_scene2d/2), -(taille_scene2d/2), taille_scene2d, taille_scene2d);

#ifdef SUPPORT_VISION2D
    scene2D->defCouleurTrace(QColor(parametres->R_CouleurTraceVision2D()));
#endif // SUPPORT_VISION2D

    vue2D = new Vue2D(animation, scene2D, 0);

#ifdef SUPPORT_VISION2D
    scene2D->defTailleGrille(parametres->R_TailleGrilleTraceVision2D());
#endif // SUPPORT_VISION2D

    ui->verticalLayout_2D->addWidget(vue2D);

    MenuContextuel2D.setSeparatorsCollapsible(true);

    defIconeOnglet(0, Perspective3D::LogoPerspective3D(!parametres->R_InterfacePetitEcran()));

    menu_historique = new QMenu(this);
    menu_historique->setTitle(tr("Ouvrir un plan"));
    menu_historique->setIcon(QIcon(":/icones/parcourir.png"));
    connect(menu_historique, SIGNAL(triggered(QAction*)), this, SLOT(OuvreFichierHistorique(QAction*)));
    PeupleMenuOuvrir();

    barreoutils2d = nullptr;
    bouton_ouvrir = nullptr;
    GereGenerationBarreOutils();

#if QT_VERSION >= QT_VERSION_CHECK(4,6,0)
    ui->actionGenerer->setPriority(QAction::HighPriority);
#endif // QT_VERSION

#if QT_VERSION >= QT_VERSION_CHECK(5,4,0)
    tab_bar->setAutoHide(parametres->R_ReductionBarreOnglets2D());
#endif // QT_VERSION

    persp_scene_2D = nullptr;

    /* Initialise la console (dans l'interface). */
    tempo_affiche_console = 0;
    tempo_affiche_mesg_status = 0;

    console = new QPlainTextEdit;
    console->setContextMenuPolicy(Qt::NoContextMenu);
//    console->setMaximumHeight(TAILLE_Y_CONSOLE_2D);
    masque_console = true;

    if (id_feuille_style)
    {
        ChargeStyleWidget(console, id_feuille_style);
        ChargeStyleWidget(console->verticalScrollBar(), id_feuille_style);
    }

    widget_console = new QWidget;
    widget_console->setMaximumHeight(TAILLE_Y_CONSOLE_2D);
    QHBoxLayout *layout_widget_edition = new QHBoxLayout(widget_console);
    layout_widget_edition->addWidget(console);


#ifdef SUPPORT_VISION2D
#ifdef DEBUG
        vision_2d = nullptr;
#endif // DEBUG

    widget_edition_2d = new QWidget;
    widget_edition_2d->setMaximumWidth(300);

    QGridLayout*layout_edition_2d = new QGridLayout(widget_edition_2d);
//    layout_widget_edition->addLayout(layout_edition_2d);

    layout_edition_2d->setContentsMargins(0,0,0,0);

    QToolButton2 *bouton_annuler = new QToolButton2(widget_edition_2d);
    bouton_annuler->setText(tr("Annuler"));
    bouton_annuler->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
    bouton_annuler->setIcon(QIcon(":/icones/annuler.png"));
    bouton_annuler->setCursor(Qt::ArrowCursor);
    bouton_annuler->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
    connect(bouton_annuler, SIGNAL(mousePress()), this, SLOT(AnnulerScene2D_mousePress()));
    layout_edition_2d->addWidget(bouton_annuler, 0, 0);

    QToolButton2 *bouton_retablir = new QToolButton2(widget_edition_2d);
    bouton_retablir->setText(tr("Rétablir"));
    bouton_retablir->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
    bouton_retablir->setIcon(QIcon(":/icones/retablir.png"));
    bouton_retablir->setCursor(Qt::ArrowCursor);
    bouton_retablir->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
    connect(bouton_retablir, SIGNAL(mousePress()), this, SLOT(RetablirScene2D_mousePress()));
    layout_edition_2d->addWidget(bouton_retablir, 1, 0);

    QToolButton2 *bouton_enregistrer = new QToolButton2(widget_edition_2d);
    bouton_enregistrer->setText(tr("Enregistrer..."));
    bouton_enregistrer->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
    bouton_enregistrer->setIcon(QIcon(":/icones/plan_dxf.png"));
    bouton_enregistrer->setCursor(Qt::ArrowCursor);
    bouton_enregistrer->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
    connect(bouton_enregistrer, SIGNAL(mousePress()), this, SLOT(EnregistrerScene2D_mousePress()));
    layout_edition_2d->addWidget(bouton_enregistrer, 0, 1);

#ifdef DEBUG_API_P3D
    QToolButton2 *bouton_enregistrer_matrice_v2d = new QToolButton2(widget_edition_2d);
    bouton_enregistrer_matrice_v2d->setText(tr("Enregistrer matrice"));
    bouton_enregistrer_matrice_v2d->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
    bouton_enregistrer_matrice_v2d->setIcon(QIcon(":/icones/matrice3.png"));
    bouton_enregistrer_matrice_v2d->setCursor(Qt::ArrowCursor);
    bouton_enregistrer_matrice_v2d->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
    connect(bouton_enregistrer_matrice_v2d, SIGNAL(mousePress()), this, SLOT(EnregistrerMatriceVision2D_mousePress()));
    layout_edition_2d->addWidget(bouton_enregistrer_matrice_v2d, 1, 1);
#endif // DEBUG_API_P3D

    layout_widget_edition->addWidget(widget_edition_2d);
    widget_edition_2d->hide();
#endif // SUPPORT_VISION2D

    ui->verticalLayout_2D->addWidget(widget_console);

    ListeVues3D = new std::vector<Interface3d *>;
    OngletFixe = 0;

    ui->actionModele_filaire->setChecked(parametres->Chk_FilaireUniquement()->isChecked());

    connect(scene2D, SIGNAL(NouveauRect(Perspective3D::vues2D_t)), this, SLOT(NouveauRectSelection(Perspective3D::vues2D_t)));
    connect(scene2D, SIGNAL(GenModele(bool)), this, SLOT(ExecGenerationModele(bool)));
    connect(scene2D, SIGNAL(NouvelleOrigine(Perspective3D::vues2D_t)), this, SLOT(NouvelleOrigine(Perspective3D::vues2D_t)));
    connect(scene2D, SIGNAL(SupprimeOrigine(Perspective3D::vues2D_t)), this, SLOT(SupprimeOrigine(Perspective3D::vues2D_t)));
    connect(scene2D, SIGNAL(AfficheMenu(QPoint)), this, SLOT(GereMenuContextuel2D(QPoint)));
    connect(scene2D, SIGNAL(TexteCoords(const QString &)), this, SLOT(defTexteStatusCoords(const QString &)));
    connect(scene2D, SIGNAL(NouvelleScene()), this, SLOT(NouvelleScene2D()));
#ifdef SUPPORT_VISION2D
    connect(scene2D, SIGNAL(DoubleClicScene(bool)), this, SLOT(DoubleClicScene2D(bool)));
#endif // SUPPORT_VISION2D
    connect(parametres, SIGNAL(ValideParametres(bool)), this, SLOT(NouveauxParametres(bool)));
    connect(parametres, SIGNAL(NouvelleLicence()), this, SLOT(NouvelleLicence()));
    connect(vue2D, SIGNAL(InsertFichier(const QString &)), this, SLOT(InsertFichier(const QString &)));

#ifdef SUPPORT_VISION2D
    connect(scene2D, SIGNAL(NouveauTraceVision2D(const QList<QPoint>&, const QPointF &)), this, SLOT(NouveauTraceVision2D(const QList<QPoint>&, const QPointF &)));
#endif // SUPPORT_VISION2D

    connect(GestionScripts, SIGNAL(EvGestionScript(bool)), this, SLOT(GereIconeGestionnaireScripts(bool)));

    automate.AutomateNonConst().defConfig(parametres);

    const Automate *automate_ptr = automate.AutomatePtr();
    const Qt::ConnectionType type_connexion_automate(Qt::QueuedConnection);
    const Qt::ConnectionType type_connexion_automate_bloquant(Qt::BlockingQueuedConnection); /* Lent mais assure la synchro (bloque le thread principal), si par exemple on affiche un message à l'utilisateur */
//    const Qt::ConnectionType type_connexion_automate_bloquant(Qt::QueuedConnection);

    connect(automate_ptr, SIGNAL(ExecutionLigne(int, bool)), this, SLOT(ExecLigne_Automate(int, bool)), type_connexion_automate);
    connect(automate_ptr, SIGNAL(Message(const QString &, bool)), this, SLOT(Message_Automate(const QString &, bool)), type_connexion_automate_bloquant);
    connect(automate_ptr, SIGNAL(Dicte(const QString &, int)), this, SLOT(Diction_Automate(const QString &, int)), type_connexion_automate_bloquant);
    connect(automate_ptr, SIGNAL(CadrageVue(bool)), this, SLOT(CadrageVue_Automate(bool)), type_connexion_automate_bloquant);
    connect(automate_ptr, SIGNAL(Confirmation(const QString &)), this, SLOT(Confirmation_Automate(const QString &)), type_connexion_automate_bloquant);
    connect(automate_ptr, SIGNAL(ReinitInterface(bool)), this, SLOT(ReinitInterface_Automate(bool)), type_connexion_automate_bloquant);
    connect(automate_ptr, SIGNAL(OuvrirFichier(const QString &)), this, SLOT(OuvrirFichier_Automate(const QString &)), type_connexion_automate_bloquant);
    connect(automate_ptr, SIGNAL(FermeFichier()), this, SLOT(FermeFichier_Automate()), type_connexion_automate_bloquant);
    connect(automate_ptr, SIGNAL(Onglet2D()), this, SLOT(Onglet2D_Automate()), type_connexion_automate_bloquant);
    connect(automate_ptr, SIGNAL(Onglet3D(int)), this, SLOT(Onglet3D_Automate(int)), type_connexion_automate_bloquant);
    connect(automate_ptr, SIGNAL(Anime3D()), this, SLOT(Anime3D_Automate()), type_connexion_automate_bloquant);
    connect(automate_ptr, SIGNAL(DefVue(Perspective3D::vues2D_t, const QRectF &)), this, SLOT(DefVue_Automate(Perspective3D::vues2D_t, const QRectF &)), type_connexion_automate_bloquant);
    connect(automate_ptr, SIGNAL(DefOrigine(Perspective3D::vues2D_t, const QPointF &)), this, SLOT(DefOrigine_Automate(Perspective3D::vues2D_t, const QPointF &)), type_connexion_automate_bloquant);
    connect(automate_ptr, SIGNAL(Generation(const Perspective3D::ParametresPerspective &, bool, qreal)), this, SLOT(Generation_Automate(Perspective3D::ParametresPerspective, bool, qreal)), type_connexion_automate_bloquant); /* ! Copie locale ! */
    connect(automate_ptr, SIGNAL(ForceConfig(const Perspective3D::ParametresPerspective &)), this, SLOT(ForceConfig_Automate(Perspective3D::ParametresPerspective)), type_connexion_automate_bloquant);
    connect(automate_ptr, SIGNAL(FermeGeneration()), this, SLOT(FermeGeneration_Automate()), type_connexion_automate_bloquant);
    connect(automate_ptr, SIGNAL(FinScript()), this, SLOT(FinScript_Automate()), type_connexion_automate_bloquant);
    connect(automate_ptr, SIGNAL(Export3D(const QString &)), this, SLOT(Export3D_Automate(const QString &)), type_connexion_automate_bloquant);

    connect(automate_ptr, SIGNAL(Question_Bool(QString,bool,bool*,bool*)), this, SLOT(QuestionBool_Automate(QString,bool,bool*,bool*)), type_connexion_automate_bloquant);
    connect(automate_ptr, SIGNAL(Question_Int(QString,int,int*,bool*)), this, SLOT(QuestionInt_Automate(QString,int,int*,bool*)), type_connexion_automate_bloquant);
    connect(automate_ptr, SIGNAL(Question_Float(QString,double,double*,bool*)), this, SLOT(QuestionFloat_Automate(QString,double,double*,bool*)), type_connexion_automate_bloquant);

    connect(automate_ptr, SIGNAL(TraceGroupe()), this, SLOT(TraceGroupe_Automate()), type_connexion_automate);
    connect(automate_ptr, SIGNAL(TraceLigne(qreal,qreal,qreal,qreal,QRgb)), this, SLOT(TraceLigne_Automate(qreal,qreal,qreal,qreal,QRgb)), type_connexion_automate);
    connect(automate_ptr, SIGNAL(TraceCourbe(qreal,qreal,qreal,qreal,qreal,qreal,qreal,QRgb)), this, SLOT(TraceCourbe_Automate(qreal,qreal,qreal,qreal,qreal,qreal,qreal,QRgb)), type_connexion_automate);
    connect(automate_ptr, SIGNAL(TraceTexte(const QString &,qreal,qreal,qreal,qreal,QRgb)), this, SLOT(TraceTexte_Automate(const QString &,qreal,qreal,qreal,qreal,QRgb)), type_connexion_automate);
    connect(automate_ptr, SIGNAL(TracePoint(qreal,qreal,QRgb)), this, SLOT(TracePoint_Automate(qreal,qreal,QRgb)), type_connexion_automate);

    interpreteur.defConfig(parametres);
//    connect(&interpreteur, SIGNAL(ExecutionLigne(int, bool)), this, SLOT(ExecLigne_Automate(int, bool)));
    connect(&interpreteur, SIGNAL(Message(const QString &, bool)), this, SLOT(Message_Automate(const QString &, bool)));
    connect(&interpreteur, SIGNAL(Dicte(const QString &, int)), this, SLOT(Diction_Automate(const QString &, int)));
    connect(&interpreteur, SIGNAL(CadrageVue(bool)), this, SLOT(CadrageVue_Automate(bool)));
//    connect(&interpreteur, SIGNAL(Confirmation(const QString &)), this, SLOT(Confirmation_Automate(const QString &)));
    connect(&interpreteur, SIGNAL(ReinitInterface(bool)), this, SLOT(ReinitInterface_Automate(bool)));
    connect(&interpreteur, SIGNAL(OuvrirFichier(const QString &)), this, SLOT(OuvrirFichier_Automate(const QString &)));
    connect(&interpreteur, SIGNAL(FermeFichier()), this, SLOT(FermeFichier_Automate()));
    connect(&interpreteur, SIGNAL(Onglet2D()), this, SLOT(Onglet2D_Automate()));
    connect(&interpreteur, SIGNAL(Onglet3D(int)), this, SLOT(Onglet3D_Automate(int)));
//    connect(&interpreteur, SIGNAL(Anime3D()), this, SLOT(Anime3D_Automate()));
    connect(&interpreteur, SIGNAL(DefVue(Perspective3D::vues2D_t, const QRectF &)), this, SLOT(DefVue_Automate(Perspective3D::vues2D_t, const QRectF &)));
    connect(&interpreteur, SIGNAL(DefOrigine(Perspective3D::vues2D_t, const QPointF &)), this, SLOT(DefOrigine_Automate(Perspective3D::vues2D_t, const QPointF &)));
    connect(&interpreteur, SIGNAL(Generation(const Perspective3D::ParametresPerspective &, bool, qreal)), this, SLOT(Generation_Automate(Perspective3D::ParametresPerspective, bool, qreal))); /* ! Copie locale ! */
    connect(&interpreteur, SIGNAL(ForceConfig(const Perspective3D::ParametresPerspective &)), this, SLOT(ForceConfig_Automate(Perspective3D::ParametresPerspective)));
    connect(&interpreteur, SIGNAL(FermeGeneration()), this, SLOT(FermeGeneration_Automate()));
//    connect(&interpreteur, SIGNAL(FinScript()), this, SLOT(FinScript_Automate()));

    connect(&interpreteur, SIGNAL(TraceGroupe()), this, SLOT(TraceGroupe_Automate()));
    connect(&interpreteur, SIGNAL(TraceLigne(qreal,qreal,qreal,qreal,QRgb)), this, SLOT(TraceLigne_Automate(qreal,qreal,qreal,qreal,QRgb)));
    connect(&interpreteur, SIGNAL(TraceCourbe(qreal,qreal,qreal,qreal,qreal,qreal,qreal,QRgb)), this, SLOT(TraceCourbe_Automate(qreal,qreal,qreal,qreal,qreal,qreal,qreal,QRgb)));
    connect(&interpreteur, SIGNAL(TraceTexte(const QString &,qreal,qreal,qreal,qreal,QRgb)), this, SLOT(TraceTexte_Automate(const QString &,qreal,qreal,qreal,qreal,QRgb)));
    connect(&interpreteur, SIGNAL(TracePoint(qreal,qreal,QRgb)), this, SLOT(TracePoint_Automate(qreal,qreal,QRgb)));
    connect(&interpreteur, SIGNAL(NouvelleLangueEnv(int, bool)), this, SLOT(NouvelleLangueEnv_Automate(int, bool)));

//    setWindowState(Qt::WindowActive);

    GenOutilsBarreOnglets();

//    if (parametres->R_PremierLancement())
//    {
//        Aide->show();
//        addDockWidget(Qt::RightDockWidgetArea, Aide);
//    }
//    else
//    {
    Aide->hide();
//    }

    /* Initialisation des raccourcis: */
    ListeRaccourcis.push_back(new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q), this, SLOT(on_actionArreter_le_script_triggered()), 0, Qt::ApplicationShortcut));
    ListeRaccourcis.push_back(new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_S), this, SLOT(on_actionEnregistrer_le_mod_le_triggered())));
    ListeRaccourcis.push_back(new QShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_P), this, SLOT(on_actionImprimer_triggered())));
    ListeRaccourcis.push_back(new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_O), this, SLOT(OuvrirGenerique())));
    ListeRaccourcis.push_back(new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_R), this, SLOT(on_action_MAJ_plan_triggered())));
    ListeRaccourcis.push_back(new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_X), this, SLOT(on_actionExtrusion_triggered())));
    ListeRaccourcis.push_back(new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_V), this, SLOT(on_actionRevolution_triggered())));
    ListeRaccourcis.push_back(new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F), this, SLOT(on_actionVue_Face_triggered())));
    ListeRaccourcis.push_back(new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_C), this, SLOT(on_actionVue_cote_triggered())));
    ListeRaccourcis.push_back(new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_H), this, SLOT(on_actionVue_haute_triggered())));
    ListeRaccourcis.push_back(new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Delete), this, SLOT(on_actionSupprimer_les_vues_triggered())));
    ListeRaccourcis.push_back(new QShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_F), this, SLOT(on_actionOrigine_face_triggered())));
    ListeRaccourcis.push_back(new QShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_C), this, SLOT(on_actionOrigine_cote_triggered())));
    ListeRaccourcis.push_back(new QShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_H), this, SLOT(on_actionOrigine_haute_triggered())));
    ListeRaccourcis.push_back(new QShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Delete), this, SLOT(on_actionSupprimer_les_origines_triggered())));
    ListeRaccourcis.push_back(new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_E), this, SLOT(on_actionGenerer_triggered())));
    ListeRaccourcis.push_back(new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_T), this, SLOT(InverseModeFilaire())));
    ListeRaccourcis.push_back(new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_P), this, SLOT(on_actionParametres_triggered())));
    ListeRaccourcis.push_back(new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_W), this, SLOT(on_actionFermer_le_plan_triggered())));
    ListeRaccourcis.push_back(new QShortcut(QKeySequence(Qt::Key_F1), this, SLOT(on_actionAide_triggered()), 0, Qt::ApplicationShortcut));
    ListeRaccourcis.push_back(new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_A), this, SLOT(on_actionVues_automatiques_triggered())));
    ListeRaccourcis.push_back(new QShortcut(QKeySequence(Qt::Key_F2), this, SLOT(on_actionAfficheConsole_triggered()), 0, Qt::ApplicationShortcut));
    ListeRaccourcis.push_back(new QShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_S), this, SLOT(on_actionEnregistrer_le_script_triggered())));
    ListeRaccourcis.push_back(new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_G), this, SLOT(on_actionScripts_triggered()), 0, Qt::ApplicationShortcut));
    ListeRaccourcis.push_back(new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Print), this, SLOT(on_actionEnregistrer_image_triggered())));
    ListeRaccourcis.push_back(new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Less), this, SLOT(on_actionPermutte_ecran_triggered())));

#ifdef SUPPORT_VISION2D
    ListeRaccourcis.push_back(new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Z), this, SLOT(Annuler())));
    ListeRaccourcis.push_back(new QShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Z), this, SLOT(Restaurer())));
#endif //  SUPPORT_VISION2D

//#ifdef SUPPORT_VISION2D
//    ListeRaccourcis.push_back(new QShortcut(QKeySequence(Qt::SHIFT), this, SLOT(PermutteModeEdition2D()), 0, Qt::WindowShortcut));
//#endif //  SUPPORT_VISION2D

#ifdef SUPPORT_GIF
    ListeRaccourcis.push_back(new QShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Print), this, SLOT(on_actionEnregistrer_animation_triggered())));
#endif //  SUPPORT_GIF

    setAcceptDrops(true);
}

MainWindow::~MainWindow()
{
    FermeVues3D(true);
    delete ListeVues3D;

    for(int i=0; i<ListeRaccourcis.size(); ++i)
    {
        delete ListeRaccourcis[i];
    }

    if (persp_scene_2D)
    {
        delete persp_scene_2D;
    }

    delete Aide;
    delete scene2D;
    delete vue2D;
#ifdef SUPPORT_VISION2D
    #ifdef DEBUG
        if (vision_2d != nullptr)
        {
            delete vision_2d;
        }
    #endif // DEBUG
    delete widget_edition_2d;
#endif // SUPPORT_VISION2D
    delete console;
    delete widget_console;
    delete parametres;
    delete GestionScripts;
    if (barreoutils2d)
    {
        delete barreoutils2d;
    }
    delete ui;
}

void MainWindow::timerEvent(QTimerEvent *ev)
{
//    qDebug() << "timerEvent(" << ev->timerId() << ") : id_timer_init=" << id_timer_init << " (" << Init << "). id_tempo_affiche_console=" << id_tempo_affiche_console << " (" << masque_console << "). id_tempo_affiche_mesg_status=" << id_tempo_affiche_mesg_status;
//    killTimer(ev->timerId());

    if (Init && (ev->timerId() == id_timer_init)) /* Après l'init pour effectuer des actions sur le tard (en principe après l'affichage de la fenêtre principale). */
    {
        Init = false;
        addDockWidget(Qt::RightDockWidgetArea, Aide);
//        Aide->setFloating(parametres->R_InterfacePetitEcran());

#ifdef PARAMETRES_DOCK
        addDockWidget(Qt::LeftDockWidgetArea, parametres);
        parametres->setFloating(false);
#endif
        restoreState(parametres->R_EtatApplication());

        GereFenetre(true);

        if (ValideBarreOutils2D())
        {
            barreoutils2d->show();
//            if (parametres->R_InterfacePetitEcran())
//            {
//                ui->tabWidget->setStyleSheet(QString("QTabWidget::tab-bar { left : 70px; }"));
//                barreoutils2d->defForceCompacte(true);
//            }
//            else
//            {
//                ui->tabWidget->setStyleSheet(QString("QTabWidget::tab-bar { left : 5px; }"));
//                barreoutils2d->defForceCompacte(false);
//            }
        }

        GereIconesOutils2D();

        /* Configuration audio (pas à l'initialisation, il peut y avoir un certain temps de latence)... */
        animation.ConfigurationAudio(parametres->R_ActiveBruitagesAudio(), parametres->R_VolumeAudioD(), parametres->R_PeripheriqueAudio());
        animation.ConfigurationDiction(parametres->R_ActiveSyntheseVocale(), parametres->R_LangueAudio(), parametres->R_VolumeAudioD(), chemin_installation);

        if (parametres->R_PremierLancement()) /* Premier lancement du logiciel */
        {
            QScreen *ecran = EcranWidget(this);
            if (ecran)
            {
                /* Passe en mode petit écran si la résolution de l'écran est faible: */
                if (ecran->geometry().width() < 1850)
                {
                    parametres->defTypeInterface(2);
                    NouveauxParametres();

//                    Aide->setFloating(true);
//                    QScreen *ecran = EcranWidget(this);
//                    if (ecran)
//                    {
//                        Aide->move(ecran->geometry().width()-Aide->width()-30, 30);
//                        on_actionAide_triggered();
//                    }
                }
            }

            bool valide_chemin_exemple = false;
            QString chemin_exemple = chemin_installation + QDir::separator() + "scripts" + QDir::separator() + "demonstration_fr.exemples.dxf." ExtensionScriptsAutomate;

            if (QFile::exists(chemin_exemple))
            {
                valide_chemin_exemple = true;
            }
            else /* Tente le répertoire parent (suivant la manière dont a été lancé Zetta6, il peut être dans un sous-dossier par rapport au fichier d'exemple). */
            {
                QDir chemin_installation_p(chemin_installation);
                chemin_installation_p.cdUp();
                chemin_exemple = chemin_installation_p.absolutePath() + QDir::separator() + "scripts" + QDir::separator() + "demonstration_fr.exemples.dxf." ExtensionScriptsAutomate;

                valide_chemin_exemple = QFile::exists(chemin_exemple);
            }

            if (valide_chemin_exemple)
            {
//                if (MesgDialog(this, tr("Exemple"), tr("Ceci est votre premier lancement de Zetta6.") + "\n" + tr("Voulez-vous voir la démonstration ?"), QMessageBox::Question) > 0)
                {
                    Ouvrir(chemin_exemple);
                }
            }

            if (MesgDialog(this, tr("Licence"), tr("Ceci est votre premier lancement de Zetta6.") + "\n" + tr("Voulez-vous entrer une clé pour débloquer toutes les fonctionnalitées ?\nCeci ne vous sera plus demandé à l'avenir, mais vous pourrez modifier plus tard la clé de licence dans la fenêtre de configuration."), QMessageBox::Question) > 0)
            {
                on_actionDebloquer_le_logiciel_triggered();
            }
        }
        else /* On en est pas au premier lancement... */
        {
            bool ignore_action_demarrage = false;
            if (!parametres->ControleVersions()) /* On lance le programme avec une version différente par rapport à la configuration */
            {
                if (MesgDialog(this, tr("Information"), tr("Votre version actuelle de Zetta6 diffère de la version configurée. Voulez-vous afficher la configuration pour contrôler les paramètres ?"), QMessageBox::Question) > 0)
                {
                    parametres->show();
                    parametres->ForceOnglet(PONGLET_ERGONOMIE);
                    ignore_action_demarrage = true;
                }
            }

            if (!ignore_action_demarrage)
            {
                int action = parametres->R_ActionDemarrage();
                if (!fichier_demarrage.isEmpty())
                {
#ifdef DEBUG
                    qDebug() << "Ouverture manuelle d'un fichier au démarrage: " << fichier_demarrage;
#endif // DEBUG
                    Ouvrir(fichier_demarrage);
                    action = RIEN_FAIRE_DEMARRAGE;
                }

                if (action == OUVRIR_PRECEDENT_DEMARRAGE)
                {
                    const QString fichier_precedent = parametres->R_Fichier_Precedent();
                    if (!fichier_precedent.isEmpty())
                    {
                        if ((TypeFichier(fichier_precedent) == TypesFichiers::STL) && !gen_modele_demarrage)
                        {
                        }
                        else
                        {
#ifndef DEBUG
                            if (!fichier_precedent.endsWith("." ExtensionScriptsAutomate, Qt::CaseInsensitive))
                            {
//                                qDebug() << " Fichier précédent : " << fichier_precedent;
                                Ouvrir(fichier_precedent);
                            }
#else
//                            if (!fichier_precedent.endsWith("." ExtensionScriptsAutomate, Qt::CaseInsensitive))
                            {
                                Ouvrir(fichier_precedent);
//                                qDebug() << "Ouverture d'un fichier au démarrage: " << fichier_precedent;
                            }
#endif // DEBUG
                        }
                    }
                }
                else if (action == DIALOGUE_OUVRIR_DEMARRAGE)
                {
                    OuvrirGenerique();
#ifdef DEBUG
                    qDebug() << "Sélection d'un plan au démarrage";
#endif // DEBUG
                }
#ifdef DEBUG
                else
                {
                    qDebug() << "Rien à faire au démarrage...";
                }
#endif // DEBUG
            }
        }

        /* Réinitialisation du tempo à sa valeure normale (autre qu'à l'init quoi). */
//        qDebug() << "Arret du timer (0) " << ev->timerId();
        ArretTimer(&id_timer_init);
        //startTimer(IntervalTempo);
    }

    if (ev->timerId() == id_timer_fichier_insert)
    {
        if (!fichier_insert_tempo.isEmpty())
        {
            Ouvrir(fichier_insert_tempo);
            fichier_insert_tempo.clear();
            id_timer_fichier_insert = -1;
        }
    }

    if (tempo_affiche_mesg_status && (id_tempo_affiche_mesg_status>=0) && (ev->timerId() == id_tempo_affiche_mesg_status))
    {
        tempo_affiche_mesg_status -= IntervalTempo;
        if (tempo_affiche_mesg_status <= 0)
        {
            tempo_affiche_mesg_status = 0;
            AfficheMesgStatus("", 0);
            if (ev->timerId() != id_timer_init)
            {
                ArretTimer(&id_tempo_affiche_mesg_status);
            }
        }
    }

    if (masque_console)
    {
        if ((id_tempo_affiche_console>=0) && ev->timerId() == id_tempo_affiche_console)
        {
            if (tempo_affiche_console <= 0) // Ferme la console une fois arrivé à 0.
            {
                MasqueConsole();
                tempo_affiche_console = 0;
                if (ev->timerId() != id_timer_init)
                {
                    ArretTimer(&id_tempo_affiche_console);
                }
            }
            else if (tempo_affiche_console < 1500)
            {
                const int hauteur_console = (int) (((float) widget_console->height()) * .95);
                if (hauteur_console > 0)
                {
                    QTextCursor curseur = console->textCursor();
                    curseur.clearSelection();
                    console->setTextCursor(curseur);

                    int diff_ht_console = widget_console->height()-hauteur_console;
                    widget_console->setGeometry(widget_console->x(), widget_console->y()+(diff_ht_console), widget_console->width(), hauteur_console);
//                    console->moveCursor(QTextCursor::End);
//                    console->ensureCursorVisible();
                    vue2D->setGeometry(vue2D->x(), vue2D->y(), vue2D->width(), vue2D->height()+diff_ht_console);

//                    widget_console->repaint();
//                    vue2D->repaint();
                }
                tempo_affiche_console -= IntervalTempo;
            }
            else
            {
                tempo_affiche_console -= IntervalTempo;
            }

        }
    }
    ev->accept();
}

void MainWindow::showEvent(QShowEvent *ev)
{
    if (Init)
    {
        if (id_timer_init == -1)
        {
            id_timer_init = startTimer(IntervalTempoInit);
        }
    }
    ev->accept();
}

void MainWindow::closeEvent(QCloseEvent *ev)
{
    ArretAnimationsAutomate();
    FermeVues3D(true);

    parametres->defEtatApplication(saveState()); // Sauvegarde de la barre d'outils à la fermeture

    parametres->defPageDocumentation(); // Sauvegarde l'état de la navigation sur la documentation.
    parametres->defAnnule_PremierLancement();
    parametres->defVersion();

    if (parametres->R_SavGeometrieFenetre())
    {
        parametres->defGeometrieApplication(saveGeometry());
    }

    if (Aide->isVisible())
    {
        Aide->close();
    }

    if (parametres->isVisible())
    {
        parametres->close();
    }

#ifdef PARAMETRES_DOCK
    if (parametres->isVisible())
    {
        parametres->close();
    }
#endif

    for(int i=1; i<ui->tabWidget->count(); ++i)
    {
        ui->tabWidget->widget(i)->close();
    }

    ev->accept();
}

void MainWindow::keyPressEvent(QKeyEvent *ev)
{
//    qDebug() << "Frappe touche : " << ev->text();

#ifdef SUPPORT_VISION2D
    if (ev->key() == Qt::Key_Shift)
    {
        if (!w_exec_commandes->hasFocus())
        {
            ForceModeEdition(true);
        }
    }
    else
#endif // SUPPORT_VISION2D
    if ((ev->key() != Qt::Key_Control) &&
             (ev->key() != Qt::Key_Meta) &&
             (ev->key() != Qt::Key_Alt) &&
             (ev->key() != Qt::Key_unknown)) /* On s'assure qu'il ne s'agit pas d'un raccourcis clavier... */
    {
        if (!w_exec_commandes->hasFocus())
        {
            ui->statusBar->clearMessage();
            w_exec_commandes->setFocus();
            w_exec_commandes->defTexteCommande(ev->text());
        }
    }
    ev->accept();
}

void MainWindow::keyReleaseEvent(QKeyEvent *ev)
{
#ifdef SUPPORT_VISION2D
    if (ev->key() == Qt::Key_Shift)
    {
        ForceModeEdition(false);
    }
#endif // SUPPORT_VISION2D
    ev->accept();
}

void MainWindow::defIconeStatus(const char *icone)
{
    if (!icone)
    {
        label_barre_status_2->clear();
    }
    else
    {
        QPixmap ico(icone);
        ico = ico.scaledToHeight(18, Qt::SmoothTransformation);
        label_barre_status_2->setPixmap(ico);
    }
}

void MainWindow::NouvelleScene2D()
{
    if (Ptest_ptr(persp_scene_2D))
    {
        const pint n_ents = persp_scene_2D->CompteurEntites();
        const pint n_ents_dernier_groupe = Ptest_ptr(persp_scene_2D->DernierGroupe()) ? persp_scene_2D->DernierGroupe()->CompteurEntites() : 0;
        if ( (n_ents_dernier_groupe>0) && (n_ents != n_ents_dernier_groupe) )
        {
#ifdef DEBUG
            defTexteStatusInfo(QString::number(n_ents) + "/" + QString::number(scene2D->CompteurEntites()) + " " + tr("entités") + " (" + QString::number(n_ents_dernier_groupe) + " nouvelles)" );
#else
            defTexteStatusInfo(QString::number(n_ents) + " " + tr("entités") + " (" + QString::number(n_ents_dernier_groupe) + " nouvelles)" );
#endif // DEBUG
            return;
        }
        else if (n_ents)
        {
#ifdef DEBUG
            defTexteStatusInfo(QString::number(n_ents) + "/" + QString::number(scene2D->CompteurEntites()) + " " + tr("entités") );
#else
            defTexteStatusInfo(QString::number(n_ents) + " " + tr("entités"));
#endif // DEBUG
            return;
        }
    }
    defTexteStatusInfo("");
}

void MainWindow::defTexteStatusCoords(const QString &s)
/* Défini le texte dans la barre de status (coordonnées). */
{
    label_barre_status_1->setText(s);
}

void MainWindow::defTexteStatusInfo(const QString &s)
/* Défini le texte dans la barre de status (informations). */
{
    label_barre_status_0->setText(s);
}

void MainWindow::AfficheMesgStatus(const QString &s, unsigned int tempo_s)
/* Affiche un message temporaire dans la barre de status */
{
//    ui->statusBar->showMessage(s, tempo_s*1000);

    label_barre_status_centre->setText(s);

    if (s.length())
    {
        if (tempo_s)
        {
            if (id_tempo_affiche_mesg_status != -1 && (tempo_affiche_mesg_status >0))
            {
//                ArretTimer(&id_tempo_affiche_mesg_status);
//                id_tempo_affiche_mesg_status = -1;

                tempo_affiche_mesg_status = tempo_s*1000;
            }
            else
            {
                tempo_affiche_mesg_status = tempo_s*1000;
                id_tempo_affiche_mesg_status = startTimer(IntervalTempo);
            }
        }
    }
    else
    {
        if (id_tempo_affiche_mesg_status != -1)
        {
            ArretTimer(&id_tempo_affiche_mesg_status);
        }
    }
}

bool MainWindow::Contexte2D(int index) const
{
    return index < NombreVues2D;
}

bool MainWindow::Contexte2D() const
{
    return Contexte2D(ui->tabWidget->currentIndex());
}

void MainWindow::PasseContexte2D()
{
    if (!Contexte2D())
    {
        ui->tabWidget->setCurrentIndex(0);
    }
}

bool MainWindow::CliqueWidget(QWidget *w, int sommeil_post, bool ignore_etat_animation)
/* Exécute l'animation du clic sur un widget, avec (si spécifié dans la configuration la lecture du son du clic). */
{
    if (automate.AutomateConst().AnimationScript() && parametres->R_AutoriseAnimationScripts() && (w != 0))
    {
        return animation.ClicWidget(w, sommeil_post, ignore_etat_animation);
    }
    return false;
}

bool MainWindow::CliqueMenuContextuel(QAction *action, int sommeil_post, bool ignore_etat_animation)
/* Exécute l'animation du clic sur l'action dans le menu contextuel, avec (si spécifié dans la configuration la lecture du son du clic). */
{
    if (automate.AutomateConst().AnimationScript() && parametres->R_AutoriseAnimationScripts() && (action != 0))
    {
        QPoint pos = PositionCurseurWidget(this);
        if (pos.isNull())
        {
            pos = QPoint(vue2D->pos()+QPoint(vue2D->width()/4, vue2D->height()/4));
        }
        QCursor curseur = cursor();
        animation.ClicPoint(&curseur);
        AfficheMenuContextuel2D(pos);
        if (!MenuContextuel2D.isVisible())
        {
            return false;
        }
        const bool r = animation.ClicMenu(&MenuContextuel2D, action, 0, sommeil_post, ignore_etat_animation);
        MenuContextuel2D.close();
        return r;
    }
    return false;
}

bool MainWindow::CliqueSousMenuContextuel(QMenu *sous_menu, QAction *action, int sommeil_post, bool ignore_etat_animation)
/* Exécute l'animation du clic sur l'action dans un menu/sous_menu, avec (si spécifié dans la configuration la lecture du son du clic). */
{
    if (automate.AutomateConst().AnimationScript() && parametres->R_AutoriseAnimationScripts() && (action != 0) && (sous_menu != 0))
    {
        QPoint pos = PositionCurseurWidget(this);
        if (pos.isNull())
        {
            pos = QPoint(vue2D->pos()+QPoint(vue2D->width()/4, vue2D->height()/4));
        }
        QCursor curseur = cursor();
        animation.ClicPoint(&curseur);
        AfficheMenuContextuel2D(pos);
        if (!MenuContextuel2D.isVisible())
        {
            return false;
        }
        sous_menu->popup(MenuContextuel2D.pos()+QPoint(MenuContextuel2D.width(), 3));
        if (!sous_menu->isVisible())
        {
            return false;
        }

        const bool r = animation.ClicMenu(&MenuContextuel2D, action, sous_menu, sommeil_post, ignore_etat_animation);
        sous_menu->close();
        MenuContextuel2D.close();
        return r;
    }
    return false;
}

bool MainWindow::CliqueAction(QAction *action)
/* Clique sur une action (gère le contexte à savoir si il faut cliquer sur la barrer d'outils ou un menu). */
{
    if (ValideBarreOutils2D())
    {
        return CliqueWidget(barreoutils2d->widgetForAction(action));
    }

    return CliqueMenuContextuel(action);
}

void MainWindow::GenOutilsBarreOnglets()
/* Genère les outils de la barre d'onglets. */
{
    if (widget_outils_barre_onglets)
    {
        return;
    }

    widget_outils_barre_onglets = new QWidget(this);
    ChargeStyleWidget(widget_outils_barre_onglets, 4);

//    const int hauteur_barre_onglets = 32
//    widget_outils_barre_onglets->setMinimumHeight(32);

    QHBoxLayout *layout_widget_onglets = new QHBoxLayout(widget_outils_barre_onglets);
    layout_widget_onglets->setContentsMargins(6, 0, 6, 0);

    /* Outils 3D... */
    {
        widget_outils_3d_onglets = new QWidget(this);
        layout_widget_onglets->addWidget(widget_outils_3d_onglets);
        QHBoxLayout *layout_widgets_3d = new QHBoxLayout(widget_outils_3d_onglets);
        layout_widgets_3d->setContentsMargins(6, 0, 6, 0);

        barre_outils_3d_widget_onglets = new QToolBar("", widget_outils_2d_onglets);
        layout_widgets_3d->addWidget(barre_outils_3d_widget_onglets);
//        barre_outils_3d_widget_onglets->addAction(ui->actionVue_oblique_3d);
//        barre_outils_3d_widget_onglets->addAction(ui->actionVue_de_face_3d);
//        barre_outils_3d_widget_onglets->addAction(ui->actionVue_de_cote_3d);
//        barre_outils_3d_widget_onglets->addAction(ui->actionVue_de_dessus_3d);
        barre_outils_3d_widget_onglets->addAction(ui->actionEnregistrer_le_mod_le);
        barre_outils_3d_widget_onglets->addAction(ui->actionConversion_2D);
        barre_outils_3d_widget_onglets->addAction(ui->actionEnregistrer_le_script);

#ifdef DEBUG
        barre_outils_3d_widget_onglets->addAction(ui->actionAffichage_proprietes3D);
#endif // DEBUG

        barre_outils_3d_widget_onglets->addSeparator();

#ifdef AFFICHE_SELECTION_COULEUR_SOLIDE_PANORAMIQUE
        cmb_couleurs_scenes_widget_onglets = new QComboBox(widget_outils_barre_onglets);
        cmb_couleurs_scenes_widget_onglets->setMinimumWidth(80);
        cmb_couleurs_scenes_widget_onglets->setMaximumWidth(160);
        barre_outils_3d_widget_onglets->addWidget(cmb_couleurs_scenes_widget_onglets);
        PeupleCmb_RAL(*cmb_couleurs_scenes_widget_onglets);
        connect(cmb_couleurs_scenes_widget_onglets, SIGNAL(currentIndexChanged(int)), SLOT(SelectionCouleurPersp3D(int)));
#endif // AFFICHE_SELECTION_COULEUR_SOLIDE_PANORAMIQUE

        cmb_type_affichage_widget_onglets = new QComboBox(widget_outils_barre_onglets);
        cmb_type_affichage_widget_onglets->setMinimumSize(QSize(100, 0));
        barre_outils_3d_widget_onglets->addWidget(cmb_type_affichage_widget_onglets);
        cmb_type_affichage_widget_onglets->insertItems(0, QStringList() << tr("Scène") << tr("Solide") << tr("Arrêtes") << tr("Filaire"));
        cmb_type_affichage_widget_onglets->setItemIcon(0, QIcon(":/icones/projections1.png"));
        cmb_type_affichage_widget_onglets->setItemIcon(1, QIcon(":/icones/projections0.png"));
        cmb_type_affichage_widget_onglets->setItemIcon(2, QIcon(":/icones/arretes.png"));
        cmb_type_affichage_widget_onglets->setItemIcon(3, QIcon(":/icones/filaire.png"));
        cmb_type_affichage_widget_onglets->setToolTip(tr("Type d'affichage"));

        connect(cmb_type_affichage_widget_onglets, SIGNAL(currentIndexChanged(int)), SLOT(SelectionModeAffichage3D(int)));
    }

    /* Outils 2D... */
    {
        widget_outils_2d_onglets = new QWidget(this);

        layout_widget_onglets->addWidget(widget_outils_2d_onglets);
        QHBoxLayout *layout_widgets_2d = new QHBoxLayout(widget_outils_2d_onglets);
        layout_widgets_2d->setContentsMargins(6, 0, 6, 0);
        barre_outils_2d_widget_onglets = new QToolBar("", widget_outils_2d_onglets);

        if (true)
        {
            QToolButton* bouton_ouvrir_onglets = new QToolButton(widget_outils_2d_onglets);
            bouton_ouvrir_onglets->setIcon(QIcon(":/icones/parcourir.png"));
            bouton_ouvrir_onglets->setPopupMode(QToolButton::InstantPopup);
            bouton_ouvrir_onglets->setMenu(menu_historique); /* Pas de transmission enfant->parent, donc pas de soucis... */
            barre_outils_2d_widget_onglets->addWidget(bouton_ouvrir_onglets);
        }
        else
        {
            barre_outils_2d_widget_onglets->addAction(ui->actionOuvrir);
        }

        barre_outils_2d_widget_onglets->addAction(ui->action_MAJ_plan);
        barre_outils_2d_widget_onglets->addAction(ui->actionScripts);
        barre_outils_2d_widget_onglets->addAction(ui->actionVues_automatiques);
        barre_outils_2d_widget_onglets->addAction(ui->actionExtrusion);
        barre_outils_2d_widget_onglets->addAction(ui->actionRevolution);
        barre_outils_2d_widget_onglets->addAction(ui->actionModele_filaire);
        barre_outils_2d_widget_onglets->addAction(ui->actionGenerer);
        layout_widgets_2d->addWidget(barre_outils_2d_widget_onglets);
    }

    {
        barre_outils_widget_onglets = new QToolBar("", widget_outils_barre_onglets);
        layout_widget_onglets->addWidget(barre_outils_widget_onglets);
        barre_outils_widget_onglets->addAction(ui->actionParametres);
        barre_outils_widget_onglets->addAction(ui->actionAide);

        barre_outils_quit_widget_onglets = new QToolBar("", widget_outils_barre_onglets);
        layout_widget_onglets->addWidget(barre_outils_quit_widget_onglets);
        barre_outils_quit_widget_onglets->addAction(ui->action_Permutte_mode_plein_ecran);
        if (QApplication::desktop()->screenCount() > 1)
        {
            barre_outils_quit_widget_onglets->addAction(ui->actionPermutte_ecran);
        }
    }

    widget_outils_barre_onglets->setMinimumHeight(27);
    ui->tabWidget->tabBar()->setMinimumHeight(27);
    ui->tabWidget->setCornerWidget(widget_outils_barre_onglets, Qt::TopRightCorner);
}

void MainWindow::GereOutilsBarreOnglets()
/* Gère l'affichage (ou non) des outils de la barre d'onglets. */
{
    if (!widget_outils_barre_onglets)
    {
        return;
    }

    const bool panoramique = parametres->R_InterfacePanoramique();
    const bool plein_ecran = parametres->R_PleinEcran();

//    qDebug() << "GereOutilsBarreOnglets :: " << panoramique << " : " << plein_ecran;

    if (panoramique || plein_ecran)
    {
        ui->tabWidget->setCornerWidget(widget_outils_barre_onglets, Qt::TopRightCorner);
        widget_outils_barre_onglets->show();

        if (plein_ecran)
        {
            ui->action_Permutte_mode_plein_ecran->setIcon(QIcon(":/icones/plein_ecran1.png"));
            ui->action_Permutte_mode_plein_ecran->setToolTip(tr("Affichage fenêtré"));
            barre_outils_quit_widget_onglets->addAction(ui->actionQuitter);
        }
        else
        {
            ui->action_Permutte_mode_plein_ecran->setIcon(QIcon(":/icones/plein_ecran0.png"));
            ui->action_Permutte_mode_plein_ecran->setToolTip(tr("Affichage plein écran"));
            barre_outils_quit_widget_onglets->removeAction(ui->actionQuitter);
        }

        if (panoramique)
        {
            if (Contexte2D())
            {
                widget_outils_3d_onglets->hide();
                widget_outils_2d_onglets->show();
            }
            else
            {
                widget_outils_3d_onglets->show();
                widget_outils_2d_onglets->hide();

                Interface3d *inter_3d = Interface3DCourante();

                if (inter_3d)
                {
                    const bool etat_affichage_export3d = inter_3d->AffichageDefinifPret();
                    ui->actionEnregistrer_animation->setEnabled(etat_affichage_export3d);
                    ui->actionEnregistrer_le_mod_le->setEnabled(etat_affichage_export3d);
                    ui->actionEnregistrer_image->setEnabled(etat_affichage_export3d);

                    if (inter_3d->Perspective() != nullptr)
                    {
                        ui->actionEnregistrer_le_script->setEnabled(etat_affichage_export3d);
                    }
                    else
                    {
                        ui->actionEnregistrer_le_script->setEnabled(false);
                    }

                    int id_type_affichage = inter_3d->ProprietesPlan().type_affichage;
                    if (id_type_affichage == -1)
                    {
                        id_type_affichage = parametres->R_TypeAffichage3D();
                    }
                    if (id_type_affichage < cmb_type_affichage_widget_onglets->count())
                    {
                        cmb_type_affichage_widget_onglets->setCurrentIndex(id_type_affichage);
                    }

#ifdef AFFICHE_SELECTION_COULEUR_SOLIDE_PANORAMIQUE
                    int id_couleur_solide = Perspective3D::RAL::RechercheIndex(inter_3d->ProprietesPlan().id_ral);

                    if (id_couleur_solide == 0)
                    {
                        id_couleur_solide = Perspective3D::RAL::RechercheIndex(inter_3d->SceneP3DConst()->CouleurRAL());
                    }

                    if (id_couleur_solide != 0)
                    {
                        if (id_couleur_solide < cmb_couleurs_scenes_widget_onglets->count())
                        {
                            cmb_couleurs_scenes_widget_onglets->setCurrentIndex(id_couleur_solide);
                        }
                    }
#endif // AFFICHE_SELECTION_COULEUR_SOLIDE_PANORAMIQUE
                }
            }
            barre_outils_widget_onglets->show();
        }
        else
        {
            widget_outils_3d_onglets->hide();
            widget_outils_2d_onglets->hide();
            barre_outils_widget_onglets->hide();
        }
    }
    else
    {
        widget_outils_barre_onglets->hide();
        widget_outils_barre_onglets->setParent(this);
        ui->tabWidget->setCornerWidget(nullptr, Qt::TopRightCorner);
    }
}

void MainWindow::GereFenetre(bool restaure)
{
    if (restaure && parametres->R_SavGeometrieFenetre())
    {
        restoreGeometry(parametres->R_GeometrieApplication());
        if (parametres->R_PleinEcran())
        {
            ChangeEtatPleinEcranApp(this, true);
        }
    }
    else
    {
        ChangeEtatPleinEcranApp(this, parametres->R_PleinEcran());
    }
    GereOutilsBarreOnglets();
}

bool MainWindow::GereGenerationBarreOutils()
/* Contrôle si il faut générer une barre d'outils.
    Si c'est le cas, la fonction renvoi true, sinon elle renvoi false et un menu contextuel sera utilisé à la place dans l'interface 2D. */
{
    if (!scene2D)
    {
        return false;
    }
    if (parametres->R_MenuContextuel2D())
    {
        if (ValideBarreOutils2D())
        {
            disconnect(cmb_tolerance, SIGNAL(currentIndexChanged(int)), this, SLOT(cmb_tolerance_currentIndexChanged(int)));
            removeToolBar(barreoutils2d);
            delete barreoutils2d;
//            delete bouton_ouvrir;
            barreoutils2d = nullptr;
            bouton_ouvrir = nullptr;
            cmb_tolerance = nullptr;
        }
        scene2D->defMenuContextuel(true);
        return false;
    }
    else
    {
        if (ValideBarreOutils2D())
        {
            return false;
        }

        const int taille_icones_outils = parametres->R_TailleIconesOutils2D();
        barreoutils2d = new BarreOutils2D(taille_icones_outils, "Outils2D", this);
        barreoutils2d->setIconSize(QSize(taille_icones_outils, taille_icones_outils));

        bouton_ouvrir = new QToolButton(this);

        bouton_ouvrir->setIcon(QIcon(":/icones/parcourir.png"));
        bouton_ouvrir->setIconSize(QSize(taille_icones_outils, taille_icones_outils));
        bouton_ouvrir->setPopupMode(QToolButton::InstantPopup);
        bouton_ouvrir->setMenu(menu_historique);

        barreoutils2d->addWidget(bouton_ouvrir);
    //    barreoutils2d->addAction(ui->actionOuvrir);
        barreoutils2d->addAction(ui->action_MAJ_plan);
        barreoutils2d->addAction(ui->actionScripts);

    //    barreoutils2d->addAction(ui->actionFermer_le_plan);
        barreoutils2d->addSeparator();
        barreoutils2d->addAction(ui->actionVues_automatiques);
        barreoutils2d->addAction(ui->actionVue_Face);
        barreoutils2d->addAction(ui->actionVue_cote);
        barreoutils2d->addAction(ui->actionVue_haute);
        barreoutils2d->addAction(ui->actionSupprimer_les_vues);
        barreoutils2d->addSeparator();
        barreoutils2d->addAction(ui->actionExtrusion);
        barreoutils2d->addAction(ui->actionRevolution);
        barreoutils2d->addSeparator();
        barreoutils2d->addAction(ui->actionOrigine_face);
        barreoutils2d->addAction(ui->actionOrigine_cote);
        barreoutils2d->addAction(ui->actionOrigine_haute);
        barreoutils2d->addAction(ui->actionSupprimer_les_origines);
        barreoutils2d->addSeparator();
        barreoutils2d->addAction(ui->actionGenerer);
        barreoutils2d->addSeparator();

        cmb_tolerance = new QComboBox(barreoutils2d);
        cmb_tolerance->addItem("~");
    //    cmb_tolerance->setMaximumWidth(taille_icones_outils-2);
        const unsigned int n_niveaux_tolerance = 12;
        for(unsigned int i=1; i<=n_niveaux_tolerance; ++i)
            cmb_tolerance->addItem(QString::number(i));

        cmb_tolerance->setCurrentIndex(0);
        cmb_tolerance->setToolTip(tr("Défini un niveau de tolérance aux imprécisions\n(plus la valeur est grande, plus le logiciel accepte les imprécisions au risque de faire des erreurs).\n"));
        barreoutils2d->addWidget(cmb_tolerance);
        connect(cmb_tolerance, SIGNAL(currentIndexChanged(int)), this, SLOT(cmb_tolerance_currentIndexChanged(int)));

        barreoutils2d->addAction(ui->actionParametres);

        barreoutils2d->addAction(ui->actionModele_filaire);
        ui->actionModele_filaire->setEnabled(parametres->LicenceValideP3D());

        barreoutils2d->addSeparator();
        barreoutils2d->addAction(ui->actionAfficheConsole);
        barreoutils2d->addAction(ui->actionAide);

        addToolBar(Qt::LeftToolBarArea, barreoutils2d);
        scene2D->defMenuContextuel(false);
    }
    return true;
}

void MainWindow::ExecLigne_Automate(int id, bool autorise_animation)
{
    /* Le fichier de script doit en principe avoir été ouvert... */
    if (GestionScripts->isVisible())
    {
        if (autorise_animation)
        {
            GestionScripts->AfficheLigne(id);
        }
        else
        {
//            GestionScripts->close(); /* Evite de ralentir l'exécution dans le cas d'un script non animé... */
        }
    }
}

void MainWindow::Message_Automate(const QString &message, bool message_utilisateur)
{
    if (message_utilisateur)
    {
        AjoutConsole(message);
        if (!(MesgDialog(this, tr("Message automate"), message, QMessageBox::Information, true) > 0))
        {
            ArretAnimationsAutomate();
        }
    }
    else
    {
//        AjoutConsole(message, false);
        AfficheMesgStatus(message, message.length()/10);
    }
}

void MainWindow::Diction_Automate(const QString &message, int langue)
{
    if (animation.ExecDiction(message, langue, parametres->R_VolumeAudioD()))
    {
        ;
    }
    else
    {
#ifdef DEBUG
        qDebug() << "Erreur de diction : " << animation.ErreurDiction();
#endif // DEBUG
        Message_Automate(message, true);
    }
}

void MainWindow::CadrageVue_Automate(bool animation)
{
    if (animation)
    {
        vue2D->CadrageAnim(scene2D->itemsBoundingRect());

    }
    else
    {
        vue2D->fitInView(scene2D->itemsBoundingRect(), Qt::KeepAspectRatio);
    }
}

void MainWindow::Confirmation_Automate(const QString &message)
{
    if (!(MesgDialog(this, tr("Message automate"), message + "\n\n" + tr("Voulez-vous continuer ?"), QMessageBox::Question, true) > 0))
    {
        if (GestionScripts->isVisible())
        {
            GestionScripts->close();
        }
        ArretAnimationsAutomate();
    }
}

void MainWindow::ArretAnimationsAutomate()
/* Arrête l'exécution de l'automate et des animations. */
{
    animation.ArretDiction();
    SommeilTempoQt(50);

    if (automate.AutomateConst().ExecEnCours())
    {
        automate.AutomateNonConst().ArretUtilisateur();
    }
//    if (animation.Anime())
//    {
//        animation.AttendFinAnimation();
//    }

    animation.ArretAnimations();
    animation.AttendFinDiction();
    animation.AttendFinAnimation();
    animation.Sommeil(100, true); /* Force une mise en sommeil pour attendre la libération du mutex. */
}

void MainWindow::ReinitInterface_Automate(bool uniquement_2d)
{
//    PasseContexte2D(); /* On passe sur la vue 2D. */

//    if (automate.AnimationScript() && parametres->R_AutoriseAnimationScripts())
//    {
//        if (ui->tabWidget->count() > 1)
//        {
//            CliqueWidget(ui->tabWidget->tabBar()->tabButton(0, QTabBar::RightSide));
//        }
//    }

//    FermeVues3D(false);

//    if (automate.AnimationScript() && parametres->R_AutoriseAnimationScripts())
//    {
//        if (scene2D->NbVuesDefinies() > 0)
//        {
//            CliqueAction(ui->actionSupprimer_les_vues);
//        }
//    }
//    on_actionSupprimer_les_vues_triggered();
//    on_actionSupprimer_les_origines_triggered();

//    // ***

    PasseContexte2D(); /* On passe sur la vue 2D. */

    if (!uniquement_2d)
    {
        if (automate.AutomateConst().AnimationScript() && parametres->R_AutoriseAnimationScripts())
        {
            if (ui->tabWidget->count() > 1)
            {
                CliqueWidget(ui->tabWidget->tabBar()->tabButton(0, QTabBar::RightSide));
            }
        }
        FermeVues3D(false);
    }

    if (automate.AutomateConst().AnimationScript() && parametres->R_AutoriseAnimationScripts())
    {
        if (scene2D->NbVuesDefinies() > 0)
        {
            CliqueAction(ui->actionSupprimer_les_vues);
        }
    }

    on_actionSupprimer_les_vues_triggered();
    on_actionSupprimer_les_origines_triggered();
}

void MainWindow::OuvrirFichier_Automate(const QString &chemin_fichier)
{
    if (automate.AutomateConst().AnimationScript() && parametres->R_AutoriseAnimationScripts())
    {
        if (ValideBarreOutils2D())
        {
            CliqueWidget(bouton_ouvrir);
        }
        else
        {
            CliqueSousMenuContextuel(menu_historique, menu_historique->defaultAction());
        }
    }
    if (plan_courant.compare(chemin_fichier, Qt::CaseSensitive) != 0)
    {
        if (!Ouvre_dxf(chemin_fichier, true))
        {
            automate.AutomateNonConst().Stop(true);
        }
    }
    else
    {
        defFichierPrecedentConfig(chemin_fichier);
    }
}

void MainWindow::FermeFichier_Automate()
{
    PasseContexte2D();
    if (automate.AutomateConst().AnimationScript() && parametres->R_AutoriseAnimationScripts())
    {
//        CliqueAction(ui->actionFermer_le_plan);
        CliqueWidget(ui->tabWidget->tabBar()->tabButton(0, QTabBar::RightSide));
    }
    Ferme_plan(false, false);
}

void MainWindow::Onglet2D_Automate()
{
//    if (plan_courant.isEmpty())
//    {
//        automate.Stop(true);
//    }

    if (automate.AutomateConst().AnimationScript() && parametres->R_AutoriseAnimationScripts())
    {
        animation.ClicTabBar(ui->tabWidget, 0);
    }
    PasseContexte2D();
}

void MainWindow::Onglet3D_Automate(int id)
{
    if ((id >= ui->tabWidget->count()) || (id < 0))
    {
        automate.AutomateNonConst().Stop(true);
    }

    if (automate.AutomateConst().AnimationScript() && parametres->R_AutoriseAnimationScripts())
    {
        animation.ClicTabBar(ui->tabWidget, id);
    }

    ui->tabWidget->setCurrentIndex(id);
}

void MainWindow::Anime3D_Automate()
{
    if (!Contexte2D())
    {
        if (automate.AutomateConst().AnimationScript() && parametres->R_AutoriseAnimationScripts())
        {
            Interface3d *interface_3d = Interface3DCourante();
            if (!interface_3d)
            {
                return;
            }
            interface_3d->AnimeRotationScene();
        }
    }
}

void MainWindow::DefVue_Automate(Perspective3D::vues2D_t vue, const QRectF &rect)
{
    QRectF cadre_vue(rect.x(), -rect.y(), rect.width(), -rect.height());

    if (automate.AutomateConst().AnimationScript() && parametres->R_AutoriseAnimationScripts())
    {
        vue2D->CadrageAnim(QRectF(rect.x(), -(rect.y()+rect.height()), rect.width(), rect.height()));

        if (vue == Perspective3D::vues2D_t::VUEFACE)
        {
            CliqueAction(ui->actionVue_Face);
        }
        else if (vue == Perspective3D::vues2D_t::VUECOTE)
        {
            CliqueAction(ui->actionVue_cote);
        }
        else if (vue == Perspective3D::vues2D_t::VUEHAUT)
        {
            CliqueAction(ui->actionVue_haute);
        }
        else if (vue == Perspective3D::vues2D_t::VUEMULT)
        {
            CliqueAction(ui->actionVues_automatiques);
        }

        scene2D->AnimRectSelection(cadre_vue, vue);
    }
    else
    {
        vue2D->fitInView(QRectF(rect.x(), -(rect.y()+rect.height()), rect.width(), rect.height()), Qt::KeepAspectRatio);
        vue2D->centerOn(rect.x()+(rect.width()*.5), -(rect.y()+(rect.height()*.5))); // Place la vue de face au centre de la vue
    }
    scene2D->Ajout_RectSelection(cadre_vue, vue);

//    if (vue == Perspective3D::vues2D_t::VUEFACE)
//    {
//        ui->actionVue_Face->setChecked(true);
//    }
//    else if (vue == Perspective3D::vues2D_t::VUECOTE)
//    {
//        ui->actionVue_cote->setChecked(true);
//    }
//    else if (vue == Perspective3D::vues2D_t::VUEHAUT)
//    {
//        ui->actionVue_haute->setChecked(true);
//    }

    NouveauRectSelection(vue, true);
}

void MainWindow::DefOrigine_Automate(Perspective3D::vues2D_t vue, const QPointF &point)
{
    if (point.isNull())
        return;

    QPointF origine(point.x(), -point.y());

    if (automate.AutomateConst().AnimationScript() && parametres->R_AutoriseAnimationScripts())
    {
        vue2D->CentreSurAnime(origine);

        if (vue == Perspective3D::vues2D_t::VUEFACE)
        {
            CliqueAction(ui->actionOrigine_face);
        }
        else if (vue == Perspective3D::vues2D_t::VUECOTE)
        {
            CliqueAction(ui->actionOrigine_cote);
        }
        else if (vue == Perspective3D::vues2D_t::VUEHAUT)
        {
            CliqueAction(ui->actionOrigine_haute);
        }

//        CliqueWidget(vue2D);
        scene2D->AnimOrigine(origine, vue);
    }
    else
    {
        vue2D->centerOn(origine);
    }

    scene2D->AjoutOrigine(origine, vue);
    NouvelleOrigine(vue);
}

void MainWindow::Generation_Automate(Perspective3D::ParametresPerspective parametres_p3d, bool attend_exec, qreal param1)
{
    if (automate.AutomateConst().AnimationScript() && parametres->R_AutoriseAnimationScripts())
    {
        CliqueAction(ui->actionGenerer);
    }
    if (scene2D->VuesDefinies())
    {
        if (!Generation3D(parametres_p3d, param1, false))
        {
            return;
        }

        if (attend_exec) /* Attend la fin de la génération */
        {
            animation.Sommeil();
            unsigned int n_onglets = ListeVues3D->size();
            if (n_onglets)
            {
                const Interface3d *dernier_onglet = ListeVues3D->back();
                while (true)
                {
                    if (ListeVues3D->size() != n_onglets) /* Un onglet a été fermé ! */
                    {
                        ArretAnimationsAutomate();
                        break;
                    }
                    if (!dernier_onglet->Perspective())
                    {
                        break; // Erreur...
                    }
                    if (dernier_onglet->Perspective()->FinGeneration())
                    {
                        break;
                    }
                    animation.Sommeil(50);
                }
            }
        }
    }
}

void MainWindow::ForceConfig_Automate(const Perspective3D::ParametresPerspective &parametres_p3d)
{
    QString txt_info = tr("Un script demande de modifier la configuration liée à la génération 3D.");
    txt_info += "\n";

    QString txt_modifs = tr("Les paramètres modifiés sont les suivants :");
    unsigned int n_modifs = 0;

    const char *tabulation = "\n\n    ";
    const char *tabulation2 = "\n          ";

    if (!CompareE(parametres_p3d.Tolerance, parametres->R_ToleranceDessin()))
    {
        txt_modifs += tabulation;
        txt_modifs += tr("Tolérances dessin") + " : " + QString::number(parametres_p3d.Tolerance);
        ++n_modifs;
    }

    if ((parametres_p3d.TailleDiagoUniforme > 0) != parametres->R_ActiveEchelleUniforme())
    {
        txt_modifs += tabulation;

        if (parametres_p3d.TailleDiagoUniforme > 0)
        {
            if (parametres_p3d.TailleDiagoUniforme != parametres->R_ValeurEchelleUniforme())
            {
                txt_modifs += tr("Activation de l'echelle uniforme à la valeur") + " " + QString::number(parametres_p3d.TailleDiagoUniforme);
                ++n_modifs;
            }
            else
            {
                txt_modifs += tr("Activation de l'echelle uniforme");
            }
        }
        else
        {
            txt_modifs += tr("Désactivation de l'echelle uniforme");
        }
        ++n_modifs;
    }
    else
    {
//        if (parametres_p3d.TailleDiagoUniforme != parametres->R_ValeurEchelleUniforme())
//        {
//            txt_modifs += tabulation;
//            txt_modifs += tr("Diagonale uniforme") + " : " + QString::number(parametres_p3d.TailleDiagoUniforme);
//            ++n_modifs;
//        }
    }

    if (parametres_p3d.Arrondi_dessin != parametres->R_ArrondiEntites())
    {
        txt_modifs += tabulation;
        txt_modifs += tr("Arrondi coordonnées (chiffres après la virgule)") + " : " + QString::number(parametres_p3d.Arrondi_dessin);
        ++n_modifs;
    }

    if (parametres_p3d.Divisionscercles != parametres->R_DivisionsCourbes())
    {
        txt_modifs += tabulation;
        txt_modifs += tr("Nombre de divisions des courbes") + " : " + QString::number(parametres_p3d.Divisionscercles);
        ++n_modifs;
    }

    if (parametres_p3d.Projections != parametres->R_Projections())
    {
        txt_modifs += tabulation;
        txt_modifs += tr("Projections des vues") + " : ";
        if ((parametres_p3d.Projections & Perspective3D::vues2D_t::VUEFACE) != (parametres->R_Projections() & Perspective3D::vues2D_t::VUEFACE))
        {
            txt_modifs += tr("FACE") + " ";
        }
        if ((parametres_p3d.Projections & Perspective3D::vues2D_t::VUECOTE) != (parametres->R_Projections() & Perspective3D::vues2D_t::VUECOTE))
        {
            txt_modifs += tr("COTE") + " ";
        }
        if ((parametres_p3d.Projections & Perspective3D::vues2D_t::VUEHAUT) != (parametres->R_Projections() & Perspective3D::vues2D_t::VUEHAUT))
        {
            txt_modifs += tr("HAUT");
        }
        ++n_modifs;
    }

    if (parametres_p3d.Informations != parametres->R_Informations())
    {
        txt_modifs += tabulation;
        txt_modifs += tr("Informations affichées 3D") + " : ";
        if (parametres_p3d.Informations == Perspective3D::infos3d_t::INUL)
        {
            txt_modifs += tr("Aucune");
        }
        else
        {
            if ((parametres_p3d.Informations & Perspective3D::infos3d_t::IDPOINTS) != (parametres->R_Informations() & Perspective3D::infos3d_t::IDPOINTS))
            {
                txt_modifs += tabulation2;
                txt_modifs += tr("Identifiants des points");
            }
            if ((parametres_p3d.Informations & Perspective3D::infos3d_t::IDLIGNES) != (parametres->R_Informations() & Perspective3D::infos3d_t::IDLIGNES))
            {
                txt_modifs += tabulation2;
                txt_modifs += tr("Identifiants des lignes");
            }
            if ((parametres_p3d.Informations & Perspective3D::infos3d_t::ICOORDSPOINTS) != (parametres->R_Informations() & Perspective3D::infos3d_t::ICOORDSPOINTS))
            {
                txt_modifs += tabulation2;
                txt_modifs += tr("Coordonnées des points");
            }
            if ((parametres_p3d.Informations & Perspective3D::infos3d_t::IVECTEURNORM) != (parametres->R_Informations() & Perspective3D::infos3d_t::IVECTEURNORM))
            {
                txt_modifs += tabulation2;
                txt_modifs += tr("Vecteurs normaux des surfaces");
            }
            if ((parametres_p3d.Informations & Perspective3D::infos3d_t::IVALNORMAUX) != (parametres->R_Informations() & Perspective3D::infos3d_t::IVALNORMAUX))
            {
                txt_modifs += tabulation2;
                txt_modifs += tr("Valeur des vecteurs normaux");
            }
            if ((parametres_p3d.Informations & Perspective3D::infos3d_t::IDSURFACES) != (parametres->R_Informations() & Perspective3D::infos3d_t::IDSURFACES))
            {
                txt_modifs += tabulation2;
                txt_modifs += tr("Identifiants des surfaces");
            }
            if ((parametres_p3d.Informations & Perspective3D::infos3d_t::IDCOURBES) != (parametres->R_Informations() & Perspective3D::infos3d_t::IDCOURBES))
            {
                txt_modifs += tabulation2;
                txt_modifs += tr("Identifiants des courbes");
            }
        }
        ++n_modifs;
    }

    if (parametres_p3d.Tailletexte3d != parametres->R_TailleTexte3D())
    {
        txt_modifs += tabulation;
        txt_modifs += tr("Taille du texte 3D") + " : " + QString::number(parametres_p3d.Tailletexte3d);
        ++n_modifs;
    }

    if (parametres_p3d.Filaire_uniquement != parametres->R_FilaireUniquement())
    {
        txt_modifs += tabulation;
        txt_modifs += tr("Mode filaire") + " : " + (parametres_p3d.Filaire_uniquement ? tr("Activé") : tr("Désactivé"));
        ++n_modifs;
    }

    if (parametres_p3d.MultiThreading != parametres->R_MultiThreading())
    {
        txt_modifs += tabulation;
        txt_modifs += tr("Mulithreading") + " : " + (parametres_p3d.MultiThreading ? tr("Activé") : tr("Désactivé"));
        ++n_modifs;
    }


    const Perspective3D::params_gen3d_t params_gen_3d = parametres->R_ParametresGen3D();

    if (parametres_p3d.Parametres3d != params_gen_3d)
    {
        txt_modifs += tabulation;
        txt_modifs += tr("Paramètres de génération 3D") + " : ";
        if (parametres_p3d.Parametres3d == Perspective3D::params_gen3d_t::PGEN3DNUL)
        {
            txt_modifs += tr("Désactivés");
        }
        else
        {
            if ((parametres_p3d.Parametres3d & Perspective3D::params_gen3d_t::DIVISION_SURF) != (params_gen_3d & Perspective3D::params_gen3d_t::DIVISION_SURF))
            {
                txt_modifs += tabulation2;
                txt_modifs += tr("Réduction des surfaces");
            }
            if ((parametres_p3d.Parametres3d & Perspective3D::params_gen3d_t::CONTROLE_ENVELOPPE) != (params_gen_3d & Perspective3D::params_gen3d_t::CONTROLE_ENVELOPPE))
            {
                txt_modifs += tabulation2;
                txt_modifs += tr("Contrôle de l'enveloppe");
            }
            if ((parametres_p3d.Parametres3d & Perspective3D::params_gen3d_t::SUPPR_LIGNES_ORPHELINES) != (params_gen_3d & Perspective3D::params_gen3d_t::SUPPR_LIGNES_ORPHELINES))
            {
                txt_modifs += tabulation2;
                txt_modifs += tr("Suppression des lignes orphelines");
            }
            if ((parametres_p3d.Parametres3d & Perspective3D::params_gen3d_t::SUPPR_ALIGNEMENT_LIGNES) != (params_gen_3d & Perspective3D::params_gen3d_t::SUPPR_ALIGNEMENT_LIGNES))
            {
                txt_modifs += tabulation2;
                txt_modifs += tr("Suppression des segments alignés");
            }
            if ((parametres_p3d.Parametres3d & Perspective3D::params_gen3d_t::SUPPR_CHEVAUCHEMENT_LIGNES) != (params_gen_3d & Perspective3D::params_gen3d_t::SUPPR_CHEVAUCHEMENT_LIGNES))
            {
                txt_modifs += tabulation2;
                txt_modifs += tr("Suppression des chevauchement de segments");
            }
            if ((parametres_p3d.Parametres3d & Perspective3D::params_gen3d_t::SUPPR_CROISEMENT_LIGNES) != (params_gen_3d & Perspective3D::params_gen3d_t::SUPPR_CROISEMENT_LIGNES))
            {
                txt_modifs += tabulation2;
                txt_modifs += tr("Suppression des croisements de segments");
            }
            if ((parametres_p3d.Parametres3d & Perspective3D::params_gen3d_t::SUPPR_VOISINES_MEME_PLAN) != (params_gen_3d & Perspective3D::params_gen3d_t::SUPPR_VOISINES_MEME_PLAN))
            {
                txt_modifs += tabulation2;
                txt_modifs += tr("Suppression des surfaces voisines de même plan");
            }
            if ((parametres_p3d.Parametres3d & Perspective3D::params_gen3d_t::SUPPR_COLLISION_LIGNES_SURFACES) != (params_gen_3d & Perspective3D::params_gen3d_t::SUPPR_COLLISION_LIGNES_SURFACES))
            {
                txt_modifs += tabulation2;
                txt_modifs += tr("Suppression des surfaces en collision avec des lignes");
            }
        }
        ++n_modifs;
    }

    if (parametres_p3d.Taille_surfaces != parametres->R_TailleSurfaces())
    {
        txt_modifs += tabulation;
        txt_modifs += tr("Taille maximum des surfaces") + " : " + QString::number(parametres_p3d.Taille_surfaces);
        ++n_modifs;
    }

    if (parametres_p3d.Norme_vues != parametres->R_NormeVues2D())
    {
        txt_modifs += tabulation;
        if (parametres_p3d.Norme_vues == Perspective3D::normevues_t::NORME_VUES_ISO)
            txt_modifs += tr("Norme vues") + " : " + tr("ISO");
        else if (parametres_p3d.Norme_vues == Perspective3D::normevues_t::NORME_VUES_US)
            txt_modifs += tr("Norme vues") + " + " + tr("Amérique du Nord");
        else
            txt_modifs += tr("Norme vues") + " + " + tr("Inconnu");
        ++n_modifs;
    }

    if (parametres_p3d.Id_ral != parametres->R_IdentRAL3D())
    {
        txt_modifs += tabulation;
        txt_modifs += tr("Couleur RAL") + " : " + QString::number(parametres_p3d.Id_ral);
        ++n_modifs;
    }

    txt_modifs += "\n\n";
    txt_modifs += tr("Si vous refusez, le script sera arrêté et rien ne sera modifié.");

    if (!n_modifs)
    {
        txt_info += tabulation;
        txt_info += tr("Mais les configurations sont identiques.");

        if (!(MesgDialog(this, tr("Information"), txt_info, QMessageBox::Information, true) > 0))
        {
            ArretAnimationsAutomate();
        }

    }
    else
    {
        txt_info += txt_modifs;

        if ((MesgDialog(this, tr("Information"), txt_info, QMessageBox::Information, true) > 0))
        {
            if (automate.AutomateConst().AnimationScript() && parametres->R_AutoriseAnimationScripts())
            {
                CliqueAction(ui->actionParametres);
            }

            if (!parametres->ForceConfigP3D(parametres_p3d))
            {
                ArretAnimationsAutomate();
            }
        }
        else
        {
            ArretAnimationsAutomate();
        }
    }
}

void MainWindow::FermeGeneration_Automate()
{
    if (ui->tabWidget->count() > 1)
    {
        if (automate.AutomateConst().AnimationScript() && parametres->R_AutoriseAnimationScripts())
        {
            ui->tabWidget->setCurrentIndex(ui->tabWidget->count()-1);
//            CliqueAction(ui->actionFermer_le_plan);
            CliqueWidget(ui->tabWidget->tabBar()->tabButton(ui->tabWidget->count()-1, QTabBar::RightSide));
        }

        FermeOnglet(ui->tabWidget->count()-1, false); /* Ferme la dernière vue */
    }
}

void MainWindow::NouveauxParametres(bool nouveaux_shaders)
/* Appelé quand l'utilisateur vient de valider de nouveaux paramètres dans la fenêtre de configuration. */
{
    ui->actionParametres->setChecked(false);

    Perspective3D::vues2D_t projs = parametres->R_Projections();
    Perspective3D::infos3d_t infos = parametres->R_Informations();
    ui->actionModele_filaire->setChecked(parametres->Chk_FilaireUniquement()->isChecked());

#ifdef SUPPORT_VISION2D
    scene2D->defCouleurTrace(QColor(parametres->R_CouleurTraceVision2D()));
    scene2D->defTailleGrille(parametres->R_TailleGrilleTraceVision2D());
#endif // SUPPORT_VISION2D

    scene2D->ChangeCouleurFond(parametres->R_CouleurFond2D());

    GereGenerationBarreOutils();

    if (ValideBarreOutils2D())
    {
        cmb_tolerance->setCurrentIndex(0);
        const unsigned int taille_icones_outils = parametres->R_TailleIconesOutils2D();
        barreoutils2d->setIconSize(QSize(taille_icones_outils, taille_icones_outils));
        bouton_ouvrir->setIconSize(QSize(taille_icones_outils, taille_icones_outils));

//        if (parametres->R_InterfacePetitEcran())
//        {
//            ui->tabWidget->setStyleSheet(QString("QTabWidget::tab-bar { left : 70px; }"));
//            barreoutils2d->defForceCompacte(true);
//        }
//        else
//        {
//            ui->tabWidget->setStyleSheet(QString("QTabWidget::tab-bar { left : 5px; }"));
//            barreoutils2d->defForceCompacte(false);
//        }
    }

    if (parametres->R_AutoriseAnimationScripts())
    {
        animation.ConfigurationTempsReference(parametres->R_TempsReferenceAnimation());
        animation.ConfigurationSouris(parametres->R_VitesseCurseurAnimation(), parametres->R_VitesseClicAnimation());
    }

    animation.ConfigurationAudio(parametres->R_ActiveBruitagesAudio(), parametres->R_VolumeAudioD(), parametres->R_PeripheriqueAudio());
    animation.ConfigurationDiction(parametres->R_ActiveSyntheseVocale(), parametres->R_LangueAudio(), parametres->R_VolumeAudioD(), chemin_installation);

#if QT_VERSION >= QT_VERSION_CHECK(5,4,0)
    ui->tabWidget->tabBar()->setAutoHide(parametres->R_ReductionBarreOnglets2D());
#endif

    /* Regénère les modèles: */
    if (ListeVues3D->size())
    {
        int taille_txt = parametres->R_TailleTexte3D();
        int opacitesolide = parametres->R_OpaciteSolide();

        for(unsigned int i=0; i<ListeVues3D->size(); ++i)
        {
            if (ListeVues3D->at(i))
            {
                Interface3d &inter3d = *ListeVues3D->at(i);

                inter3d.RegenProjections(projs, infos, taille_txt, false);
                inter3d.defOpacite(opacitesolide);
                inter3d.defImprimante3D(parametres->R_AfficheZoneImpression3D(), parametres->R_TailleZoneImpression3D_X(), parametres->R_TailleZoneImpression3D_Y(), parametres->R_TailleZoneImpression3D_Z());
                inter3d.DefEclairageDynamique(parametres->R_EclairageScene());
                inter3d.defAffichageAccroche(parametres->R_AfficheAccrochage3D());
                inter3d.defLissageAffichage3D(parametres->R_LissageAffichage3D());

                if (nouveaux_shaders && parametres->R_ActiveGLSL())
                {
                    inter3d.ReinitAffichage();
                }
                else
                {
                    inter3d.TransfertModele();
                }

                inter3d.defCouleurFond(parametres->R_CouleurFond3D(), true);
                inter3d.defCouleursScene(parametres->R_CouleurSommets3D(), parametres->R_CouleurSegments3D(), parametres->R_CouleurImprimante3D(), parametres->R_CouleurReperesSommets3D(), parametres->R_CouleurReperesSegments3D(), parametres->R_CouleurReperesFaces3D());

                inter3d.InitAnimationSolide();
                inter3d.ChangeEtatBarreOutils((!parametres->R_InterfacePetitEcran() && !parametres->R_InterfacePanoramique()));

                if (nouveaux_shaders)
                {
                    if (parametres->R_ActiveGLSL())
                    {
                        inter3d.defEtatShaders(true);
                        AppliqueShadersInterface3D(&inter3d, true, parametres->R_ActiveGLSLPost());
                    }
                    else
                    {
                        inter3d.defEtatShaders(false);
                    }
                }
                inter3d.defModeAffichage(parametres->R_AffichageOrthographique(), nouveaux_shaders, inter3d.ProprietesPlan().vue_reference);
            }
        }

        if (!Contexte2D()) /* Paramètres à appliquer uniquement sur l'onglet 3D courant. */
        {
            Interface3d *inter3d_courante = Interface3DCourante();
            if (inter3d_courante)
            {
                const unsigned int id_ral = parametres->R_IndexRAL();
                inter3d_courante->defIdCouleurSolide(id_ral);
                inter3d_courante->defTypeAffichage(parametres->R_TypeAffichage3D());
                inter3d_courante->defTempoShaderPost(parametres->R_TempoShadersPost());

                if (cmb_type_affichage_widget_onglets)
                {
                    cmb_type_affichage_widget_onglets->setCurrentIndex(parametres->R_TypeAffichage3D());
                }
#ifdef AFFICHE_SELECTION_COULEUR_SOLIDE_PANORAMIQUE
                if (cmb_couleurs_scenes_widget_onglets)
                {
                    if (int(id_ral) < cmb_couleurs_scenes_widget_onglets->count())
                    {
                        cmb_couleurs_scenes_widget_onglets->setCurrentIndex(id_ral);
                    }
                }
#endif // AFFICHE_SELECTION_COULEUR_SOLIDE_PANORAMIQUE

                inter3d_courante->ExecShaderPost();
            }
        }
    }

    if (parametres->LicenceValideP3D())
    {
        ui->actionModele_filaire->setEnabled(true);
    }

    GereFenetre();
}

void MainWindow::NouvelleLicence()
{
    PeupleMenuOuvrir();
}

void MainWindow::FinScript_Automate()
/* Fin normale de l'automate. */
{
    if (GestionScripts->isVisible())
    {
        GestionScripts->close();
    }
    NouvelleScene2D();
}

void MainWindow::Export3D_Automate(const QString &nom_fichier)
{
    if (!Contexte2D())
    {
//        qDebug() << "Export Automate : " << nom_fichier;

        EnregistreModele(parametres->R_CheminExport() + QDir::separator() + nom_fichier);
    }
}

void MainWindow::TraceGroupe_Automate()
{
    /* Création de la PScene2D si elle n'existe pas déjà: */
    if (!persp_scene_2D)
    {
        persp_scene_2D = new Perspective3D::PScene2D;
    }

    if (persp_scene_2D->AjoutGroupe())
    {
        scene2D->NouveauGroupeEnts();
    }
}

void MainWindow::TraceLigne_Automate(qreal x1, qreal y1, qreal x2, qreal y2, QRgb couleur)
{
    /* Création de la PScene2D si elle n'existe pas déjà: */
    if (!persp_scene_2D)
    {
        persp_scene_2D = new Perspective3D::PScene2D;
    }

    const Perspective3D::Pligne2D ligne(Perspective3D::Ppoint2D(x1, y1), Perspective3D::Ppoint2D(x2, y2), IDNUL, false, false, qRgbP(couleur));
    const int id = persp_scene_2D->AjoutLigne(ligne);
    if (id != IDNUL)
    {
        scene2D->AjoutEntLigne(ligne, id, persp_scene_2D->Groupes().size()-1);
    }
}

void MainWindow::TraceCourbe_Automate(qreal centre_x, qreal centre_y, qreal longueur, qreal hauteur, qreal angle1, qreal angle2, qreal rotation, QRgb couleur)
{
    /* Création de la PScene2D si elle n'existe pas déjà: */
    if (!persp_scene_2D)
    {
        persp_scene_2D = new Perspective3D::PScene2D;
    }

    Perspective3D::Pellipse2D ellipse(Perspective3D::Ppoint2D(centre_x, centre_y), longueur, hauteur, angle1, angle2, rotation);
    ellipse.defCouleur(qRgbP(couleur));

    const int id = persp_scene_2D->AjoutEllipse(ellipse);
    if (id != IDNUL)
    {
        scene2D->AjoutEntEllipse(ellipse, id, persp_scene_2D->Groupes().size()-1);
    }
}

void MainWindow::TraceTexte_Automate(const QString &str, qreal origine_x, qreal origine_y, qreal hauteur_texte, qreal rotation, QRgb couleur)
{
    /* Création de la PScene2D si elle n'existe pas déjà: */
    if (!persp_scene_2D)
    {
        persp_scene_2D = new Perspective3D::PScene2D;
    }

    Perspective3D::PTexte2D texte(str.toStdString().data(), Perspective3D::Ppoint2D(origine_x, origine_y), hauteur_texte, rotation);
    texte.defCouleur(qRgbP(couleur));
    const int id = persp_scene_2D->AjoutTexte(texte);
    if (id != IDNUL)
    {
        scene2D->AjoutEntTexte(texte, id, persp_scene_2D->Groupes().size()-1);
    }
}

void MainWindow::TracePoint_Automate(qreal x, qreal y, QRgb couleur)
{
    /* Création de la PScene2D si elle n'existe pas déjà: */
    if (!persp_scene_2D)
    {
        persp_scene_2D = new Perspective3D::PScene2D;
    }

    const Perspective3D::Ppoint2D point(x, y);
    const int id = persp_scene_2D->AjoutPoint(point);
    if (id != IDNUL)
    {
        scene2D->AjoutEntPoint(point, id, persp_scene_2D->Groupes().size()-1, QColor(couleur));
    }
}

void MainWindow::NouvelleLangueEnv_Automate(int langue, bool exec_interpreteur)
{
    if (exec_interpreteur)
    {
        QStringList liste_commandes_interpreteur;
        if (interpreteur.ListeCommandes(liste_commandes_interpreteur))
        {
            /* Chargement de la nouvelle langue de l'interpréteur */
            w_exec_commandes->initCompletion(liste_commandes_interpreteur, false);
        }
#ifdef DEBUG
//        qDebug() << "Nouvelle langue automate (" << langue << ");
//        qDebug() << "Nouvelle langue automate (" << langue << ") :: " << liste_commandes_interpreteur;
#else
        P_UNUSED(langue); /* Comme ça le compilo fait pas chier... */
#endif // DEBUG
    }
}

void MainWindow::QuestionBool_Automate(const QString &question, bool valeur_defaut, bool *v, bool *annuler_exec)
{
    DialogBool question_bool(tr("Question automate"), question, valeur_defaut, &animation, false, false, this);
    if (question_bool.Accepte())
    {
        *v = question_bool.Reponse();
        *annuler_exec = false;
        return;
    }
    else
    {
        ArretAnimationsAutomate();
        *annuler_exec = true;
    }
}

void MainWindow::QuestionInt_Automate(const QString &question, int valeur_defaut, int *v, bool *annuler_exec)
{
    DialogInt question_int(tr("Question automate"), question, valeur_defaut, -1000000, 1000000, &animation, false, false, this);
    if (question_int.Accepte())
    {
        *v = question_int.Reponse();
        *annuler_exec = false;
        return;
    }
    else
    {
        ArretAnimationsAutomate();
        *annuler_exec = true;
    }
}

void MainWindow::QuestionFloat_Automate(const QString &question, double valeur_defaut, double *v, bool *annuler_exec)
{
    DialogDouble question_double(tr("Question automate"), question, valeur_defaut, -1000000.0, 1000000.0, &animation, false, false, this);
    if (question_double.Accepte())
    {
        *v = question_double.Reponse();
        *annuler_exec = false;
        return;
    }
    else
    {
        ArretAnimationsAutomate();
        *annuler_exec = true;
    }
}

void MainWindow::AideParametres(unsigned int onglet)
{
    if (onglet == PONGLET_ERGONOMIE) { Aide->OuvrePage("parametrage.html#ergonomie"); }
    else if (onglet == PONGLET_DESSIN) { Aide->OuvrePage("parametrage.html#dessin"); }
    else if (onglet == PONGLET_SOLIDE3D) { Aide->OuvrePage("parametrage.html#gen3d"); }
    else if (onglet == PONGLET_AFFICHAGE3D) { Aide->OuvrePage("parametrage.html#affichage3d"); }
    else if (onglet == PONGLET_EXPORT) { Aide->OuvrePage("parametrage.html#export"); }
    else if (onglet == PONGLET_PRODUIT) { Aide->OuvrePage("parametrage.html#licence"); }
    else { return; }
    Aide->show();
}

void MainWindow::PeupleMenuOuvrir()
/* Rempli la combo "Ouvrir..." */
{
    const int lg_chemin_max = 75;
    const bool affiche_id = true;

    menu_historique->clear();
//    menu_historique->setTearOffEnabled(true);

    if (!parametres->LicenceValideP3D())
    {
        menu_historique->addAction(ui->actionDebloquer_le_logiciel);
        ui->actionDebloquer_le_logiciel->setData(-2);
    }

    menu_historique->setDefaultAction(ui->actionOuvrir);
    ui->actionOuvrir->setData(-1);
    menu_historique->addAction(ui->actionOuvrir);

    ListeHistorique = parametres->R_Historique(TYPE_FICHIER);

    if (!ListeHistorique.empty() && !(ListeHistorique.size()==1 && ListeHistorique.at(0).isEmpty()))
    {
        menu_historique->addSection(tr("Historique"))->setData(-1);

        for(int i=0; i<ListeHistorique.size(); ++i)
        {
            QAction *act = new QAction(menu_historique);
            if (affiche_id)
            {
                if (i < 9) /* Décalage de 1, c'est normal... */
                {
                    act->setText(QString::number(i+1) + ":    " + TronqueChemin(ListeHistorique[i], lg_chemin_max));
                }
                else
                {
                    act->setText(QString::number(i+1) + ":  " + TronqueChemin(ListeHistorique[i], lg_chemin_max));
                }
            }
            else
            {
                act->setText(TronqueChemin(ListeHistorique[i], lg_chemin_max));
            }

            TypesFichiers type = TypeFichier(ListeHistorique[i]);

            if (type == TypesFichiers::DXF_3D)
            {
                act->setIcon(QIcon(":/icones/plan_dxf.png"));
            }
            else if (type == TypesFichiers::BMP || type == TypesFichiers::PNG || type == TypesFichiers::JPG || type == TypesFichiers::TGA || type == TypesFichiers::GIF)
            {
                act->setIcon(QIcon(":/icones/plan_image.png"));
            }
            else if (type == TypesFichiers::STL)
            {
                act->setIcon(QIcon(":/icones/cube_perspective.png"));
            }
            else if (type == TypesFichiers::Z6S)
            {
                act->setIcon(QIcon(":/icones/script.png"));
            }
            act->setData(i);

            menu_historique->addAction(act);
        }
    }
//    GereIconesOutils2D();
}

bool MainWindow::ArretTimer(int *id)
{
    if (*id != -1)
    {
        const int copie_timer = *id;
        *id = -1;
        killTimer(copie_timer);
        return true;
    }
    return false;
}

void MainWindow::ControleVersionServeur(const QString &version_serveur)
/* Contrôle si la version du client Zetta6 est aligné sur la version du serveur. */
{
//    MesgDialog(this, "ControleVersionServeur", version_serveur, QMessageBox::Information);
    if (version_serveur.length()<30)
    {
        const unsigned int taille_ico = 18;
        QStringList ls = version_serveur.split('.');

        if (ls.size() == 3)
        {
            bool valide_majeur = false;
            bool valide_mineur = false;
            bool valide_correctif = false;

            int version_majeur = ls[0].toInt(&valide_majeur);
            int version_mineur = ls[1].toInt(&valide_mineur);
            int version_revision = ls[2].toInt(&valide_correctif);

            if (valide_majeur && valide_mineur && valide_correctif) /* A partir de là on est sûr de connaitre la version sur le serveur. */
            {
                if (version_majeur == VERSION_P3D_MAJEUR) /* La version majeur doit être la même, sinon le jeu de clé est différent */
                {
                    if ((version_mineur != VERSION_P3D_MINEUR) || (version_revision != VERSION_P3D_REVISION)) /* Mise à jour avec la même clé de licence */
                    {
//                        AfficheMesgStatus2(tr("Mise à jour disponible : %1").arg(version_serveur));

                        label_barre_status_maj->setPixmap(QPixmap(":/icones/installer-qt.png").scaledToHeight(taille_ico, Qt::SmoothTransformation));
                        label_barre_status_maj->setToolTip(tr("Mise à jour du logiciel disponible : %1").arg(version_serveur));
                    }
                    else /* Le client est à jour */
                    {
//                        AfficheMesgStatus2(tr("Zetta6 est à jour : %1").arg(version_serveur));

                        label_barre_status_maj->setPixmap(QPixmap(":/icones/valide.png").scaledToHeight(taille_ico, Qt::SmoothTransformation));
                        label_barre_status_maj->setToolTip(tr("Le logiciel est à jour : %1").arg(version_serveur));
                    }
                }
                else /* Mise à jour majeur (donc avec une clé de licence différente). */
                {
                    if (parametres->LicenceValideP3D()
#ifdef SUPPORT_VISION2D
                            || parametres->LicenceValideV2D()
#endif // SUPPORT_VISION2D
                            )
                    {
//                        AfficheMesgStatus2(tr("Nouvelle version disponible (incompatible avec votre clé de licence) : %1").arg(version_serveur));

                        label_barre_status_maj->setPixmap(QPixmap(":/icones/invalide.png").scaledToHeight(taille_ico, Qt::SmoothTransformation));
                        label_barre_status_maj->setToolTip(tr("Nouvelle version du logiciel disponible (incompatible avec votre clé de licence actuelle) : %1").arg(version_serveur));
                    }
                    else
                    {
//                        AfficheMesgStatus2(tr("Nouvelle version disponible : %1").arg(version_serveur));

                        label_barre_status_maj->setPixmap(QPixmap(":/icones/installer-qt.png").scaledToHeight(taille_ico, Qt::SmoothTransformation));
                        label_barre_status_maj->setToolTip(tr("Mise à jour du logiciel disponible : %1").arg(version_serveur));
                    }
                }
            }
            else /* Impossible d'obtenir la version sur le serveur ?! */
            {
                label_barre_status_maj->setPixmap(QPixmap(":/icones/invalide.png").scaledToHeight(taille_ico, Qt::SmoothTransformation));
                label_barre_status_maj->setToolTip(tr("Impossible d'obtenir la version du logiciel sur le serveur").arg(version_serveur));
            }
        }
    }
}

void MainWindow::ExecCommandeInterpreteur(const QString &cmd)
{
    if (!cmd.isEmpty())
    {
        if (interpreteur.execCommande(cmd) == AUTOMATE_OK)
        {
            w_exec_commandes->ajoutCompletion(cmd);
        }
        else
        {
            AfficheMesgStatus(tr("Erreur lors de l'exécution de la commande : ' %1 '").arg(cmd));
        }
    }
}

void MainWindow::InsertFichier(const QString &nfichier)
/* Insert un fichier par glissé-déposé */
{
    ArretAnimationsAutomate();
    Ferme_plan();

#ifdef DEBUG
    qDebug() << "Ouverture du fichier (insertion) : " << nfichier;
#endif // DEBUG
//    Ouvrir(nfichier);
    fichier_insert_tempo = nfichier;
    id_timer_fichier_insert = startTimer(120);
}

void MainWindow::MasqueConsole()
{
    tempo_affiche_console = 0;
    ui->actionAfficheConsole->setChecked(false);
    widget_console->hide();
}

void MainWindow::AfficheConsole(bool fermeture_auto)
{
    ui->actionAfficheConsole->setChecked(true);

    if (fermeture_auto)
    {
        tempo_affiche_console = 8000; // Delai en ms de temps d'affichage de la console.
        masque_console = true;
    }
    else
    {
        masque_console = false;
    }

    int diff_ht_console = widget_console->height()-TAILLE_Y_CONSOLE_2D;
    widget_console->setGeometry(widget_console->x(), widget_console->y()+(diff_ht_console), widget_console->width(), TAILLE_Y_CONSOLE_2D);
    vue2D->setGeometry(vue2D->x(), vue2D->y(), vue2D->width(), vue2D->height()+diff_ht_console);
    widget_console->show();

    if (fermeture_auto)
    {
        if (!Init)
        {
            if (id_tempo_affiche_console != -1)
            {
                ArretTimer(&id_tempo_affiche_console);
            }
            id_tempo_affiche_console = startTimer(IntervalTempo);
        }
    }
}

void MainWindow::AjoutConsole(const QString &txt, bool affichage_console)
{
    if (!txt.isEmpty())
    {
        console->appendPlainText(txt);
    }

    if (affichage_console)
    {
        AfficheConsole(true);
    }
}

QString MainWindow::DialogOuvrir(const QString &format_type)
/* Boite de dialogue pour ouvrir un fichier de tout type supporté */
{
    QString nfichier;
    QStringList hist_d = parametres->R_Historique(TYPE_DOSSIER);

    Type_DialogFichier dialog_ouvrir(Q_NULLPTR, tr("Ouvrir..."), parametres->R_DernierDossier_Ouverture(), format_type);
    dialog_ouvrir.setHistory(hist_d);
    dialog_ouvrir.setFileMode(QFileDialog::ExistingFile);
    dialog_ouvrir.setViewMode(parametres->R_TypeAffichageOuvrir());
    dialog_ouvrir.setAcceptMode(QFileDialog::AcceptOpen);
    dialog_ouvrir.setLabelText(QFileDialog::Accept, tr("Ouvrir"));
    dialog_ouvrir.setLabelText(QFileDialog::Reject, tr("Annuler"));

//    if (!parametres->R_InterfacePetitEcran())
//    {
//        dialog_ouvrir.window()->setMinimumSize(906,560);
//    }

    bool etat_dialog = dialog_ouvrir.exec();

    parametres->defTypeAffichageOuvrir(dialog_ouvrir.viewMode());

    if (etat_dialog != QDialog::Accepted)
    {
        return QString();
    }

    QStringList ls = dialog_ouvrir.selectedFiles();
    if (ls.size())
    {
        nfichier = ls[0];
    }
    return nfichier;
}

QString MainWindow::DialogEnregistrer(const QString &chemin_dest, const QString &format_type, QString *select_type_fichier)
/* Boite de dialogue pour ouvrir un fichier de tout type supporté */
{
    QString nfichier;
    QStringList hist_d = parametres->R_Historique(TYPE_DOSSIER_ENREG);

    Type_DialogFichier dialog_ouvrir(Q_NULLPTR, tr("Enregistrer le fichier sous..."), chemin_dest, format_type);
    dialog_ouvrir.setHistory(hist_d);
    dialog_ouvrir.setFileMode(QFileDialog::AnyFile);
    dialog_ouvrir.setViewMode(parametres->R_TypeAffichageOuvrir());
    dialog_ouvrir.setAcceptMode(QFileDialog::AcceptSave);
    dialog_ouvrir.setLabelText(QFileDialog::Accept, tr("Enregistrer"));
    dialog_ouvrir.setLabelText(QFileDialog::Reject, tr("Annuler"));

    if (!parametres->R_InterfacePetitEcran())
    {
        dialog_ouvrir.window()->resize(906,560);
    }

    bool etat_dialog = dialog_ouvrir.exec();

    parametres->defTypeAffichageOuvrir(dialog_ouvrir.viewMode());
    *select_type_fichier = dialog_ouvrir.selectedNameFilter();

    if (etat_dialog != QDialog::Accepted)
    {
        return QString();
    }

    QStringList ls = dialog_ouvrir.selectedFiles();
    if (ls.size())
    {
        nfichier = ls[0];
    }
    return nfichier;
}

TypesFichiers MainWindow::Ouvrir(const QString &nfichier, bool autorise_scripts, bool regen_plan)
/* Lance la fonction adaptée pour ouvrir le fichier donné en argument. Renvoi le type de fichier ouvert ou TYPE_FICHIER_NUL si le chemin est invalide ou le type de fichier non géré. */
{
    if (nfichier.isEmpty())
    {
        return TypesFichiers::NUL;
    }

    if (!QFile::exists(nfichier))
    {
        AjoutConsole(tr("Erreur lors de l'ouverture du fichier %1\n(le fichier n'existe pas ou n'est pas accessible).").arg(nfichier));
        MesgDialog(this, tr("Erreur"), tr("Erreur lors de l'ouverture du fichier %1\n(le fichier n'existe pas ou n'est pas accessible).").arg(nfichier), QMessageBox::Information);
        return TypesFichiers::NUL;
    }

    TypesFichiers type = TypeFichier(nfichier);

    if (type == TypesFichiers::DXF_3D)
    {
        defIconeStatus(":/icones/plan_dxf.png");
        if (Ouvre_dxf(nfichier, regen_plan))
        {
            return type;
        }
        return TypesFichiers::NUL;
    }
    else if (type == TypesFichiers::STL)
    {
        defIconeStatus(":/icones/cube_perspective.png");
//        if (Ouvre_STL_2D(nfichier, regen_plan)) // Conversion du fichier STL en plan 2D.
//        {
//            return type;
//        }
        if (Ouvre_STL_3D(nfichier)) //Import du fichier STL (3D).
        {
            return type;
        }
        return TypesFichiers::NUL;
    }
    else if (type == TypesFichiers::BMP || type == TypesFichiers::PNG || type == TypesFichiers::JPG || type == TypesFichiers::TGA || type == TypesFichiers::GIF)
    {
#ifdef SUPPORT_VISION2D
        defIconeStatus(":/icones/plan_image.png");
        if (Ouvre_Image(nfichier))
        {
            return type;
        }
        return TypesFichiers::NUL;
#endif // SUPPORT_VISION2D
    }
    else if (type == TypesFichiers::Z6S)
    {
        if (autorise_scripts)
        {
            defIconeStatus(":/icones/script.png");
            if (Ouvre_Script(nfichier))
            {
                return type;
            }
        }
        return TypesFichiers::NUL;
    }
    else
    {
        defIconeStatus(nullptr);
        MesgDialog(this, tr("Information"), tr("Impossible d'ouvrir le fichier \"%1\" (type inconnu).").arg(nfichier), QMessageBox::Information);
    }
    return TypesFichiers::NUL;
}

void MainWindow::defFichierPrecedentConfig(const QString &nfichier)
{
    QDir chemin(nfichier);
    chemin.cdUp();
    const QString nom_fichier = FichierChemin(nfichier);
    const QString fichier_precedent(QDir::cleanPath(chemin.absoluteFilePath(nom_fichier)));
    parametres->defFichier_Precedent(fichier_precedent);
    parametres->defDernierDossier_Ouverture(chemin.absolutePath());
    PeupleMenuOuvrir();
}

bool MainWindow::Ouvre_Script(QString nfichier)
/* Lance l'exécution d'un script pour l'automate. */
{
    if (nfichier.isEmpty())
    {
        nfichier = DialogOuvrir("*." ExtensionScriptsAutomate);
        if (nfichier.isEmpty())
        {
            return false;
        }
    }

    if (!QFile::exists(nfichier))
    {
        AjoutConsole(tr("Erreur lors de l'ouverture du fichier %1 (le fichier n'existe pas)").arg(nfichier));
        MesgDialog(this, tr("Erreur"), tr("Impossible d'ouvrir le script \"%1\"").arg(nfichier), QMessageBox::Information);
        return false;
    }

    defFichierPrecedentConfig(nfichier);

    AjoutConsole(tr("Lancement du script") + " " + nfichier);

    PasseContexte2D();
    SupprimeVue(Perspective3D::vues2D_t::VUEFACE | Perspective3D::vues2D_t::VUECOTE | Perspective3D::vues2D_t::VUEHAUT | Perspective3D::vues2D_t::VUEMULT);
    SupprimeOrigine(Perspective3D::vues2D_t::VUEFACE | Perspective3D::vues2D_t::VUECOTE | Perspective3D::vues2D_t::VUEHAUT | Perspective3D::vues2D_t::VUENUL);
    GereIconesOutils2D();

    if (!parametres->R_AutoriseAnimationScripts())
    {
        GereIconesOutils2D(true);
        animation.ArretAnimations();
    }
    else
    {
        GestionScripts->OuvreScript(nfichier);
        animation.InitAnimations();
    }

//    int r = automate.AutomateNonConst().execFichier(nfichier);

    automate.defFichierExec(nfichier);
    automate.start();
//    automate.execFichier(false); /* Exécution sans le multi-thread. */

    while (true)
    {
        if (automate.isFinished())
        {
            break;
        }
        else
        {
            SommeilTempoQt(50);
        }
    }

    const int r = automate.ResultatExec();

    if (!parametres->R_AutoriseAnimationScripts() || !automate.AutomateConst().AnimationScript())
    {
        GereIconesOutils2D(false);
        if (animation.Anime()) /* La diction fait appel à l'animation ! */
        {
            animation.ArretAnimations();
        }
    }
    else
    {
        animation.ArretAnimations();
    }

    if (r == AUTOMATE_OK)
    {
        AjoutConsole(tr("Script exécuté avec succès."));
//        GereIconesOutils2D();
        return true;
    }
    else if (r == AUTOMATE_ARRET)
    {
        AjoutConsole(tr("Script arrêté."));
        return true;
    }

    AjoutConsole(tr("Script interrompu à la suite d'une erreur.")); // AUTOMATE_ERR
//    MesgDialog(this, tr("Erreur"), tr("Script interrompu à la suite d'une erreur."), QMessageBox::Information);
    FinScript_Automate();
    return false;
}

bool MainWindow::Ouvre_dxf(QString nfichier, bool regen_plan)
{
    if (exec_import)
    {
        return false;
    }
    if (nfichier.isEmpty())
    {
        nfichier = DialogOuvrir("*.dxf");
        if (nfichier.isEmpty())
        {
            return false;
        }
    }

    exec_import = true;
    if (!Ouvre_dxf_int(nfichier, regen_plan))
    {
        MesgDialog(this, tr("Erreur"), tr("Impossible d'ouvrir le fichier %1").arg(nfichier), QMessageBox::Information);
        exec_import = false;
        return false;
    }

    exec_import = false;
    return true;
}

bool MainWindow::Ouvre_dxf_int(const QString &nfichier, bool regen_plan)
/* Ouvre le fichier dxf (2D) donné en argument. Si il est nul, on ouvre une boite de dialogue pour demander un chemin d'accès. */
{
    plan_courant.clear();

    QFile fichier(nfichier);

    OngletFixe = 0;

    if (!fichier.exists(nfichier))
    {
        AjoutConsole(tr("Erreur lors de l'ouverture du fichier %1 (le fichier n'existe pas)").arg(nfichier));
        return false;
    }

    if (!fichier.open(QFile::ReadOnly))
    {
        AjoutConsole(tr("Erreur lors de l'ouverture du fichier %1 (avez vous les droits d'accès ?)").arg(nfichier));
//        MesgDialog(this, tr("Erreur"), tr("Erreur lors de l'ouverture du fichier") + " '" + nfichier + "'", QMessageBox::Information);
        return false;
    }

    if (persp_scene_2D)
    {
        Ferme_plan();
    }

    plan_courant = nfichier;

    PasseContexte2D();
//    ui->tabWidget->setTabIcon(OngletFixe, QIcon(":/icones/plan_dxf.png"));
    persp_scene_2D = new Perspective3D::PScene2D;

    Th_ImportDXF th_import(persp_scene_2D, fichier.fileName(), parametres->R_IgnoreNonImprimableDXF());

    th_import.start();
    SommeilTempoQt(25);
    while (th_import.isRunning())
    {
        const int avancement = th_import.Scene()->Avancement();
        if (avancement >= 0)
        {
            AfficheMesgStatus(tr("Import DXF") + QString(" : %1%").arg(QString::number(avancement)), 1);
        }
        else
        {
            AfficheMesgStatus(tr("Import DXF") + QString(" : -"), 1);
        }
        SommeilTempoQt(5);
//        QThread::msleep(5);
    }

    if (th_import.ValideImport())
    {
        AfficheMesgStatus(tr("Import DXF") + QString(" : 100%"), 5);
    }
    else
    {
        AfficheMesgStatus(tr("Import DXF") + " : " + tr("Erreur"), 5);
        delete persp_scene_2D;
        persp_scene_2D = nullptr;
        return false;
    }

    if (!scene2D->ImportScenePerspective(persp_scene_2D, true))
    {
        const bool ret = (persp_scene_2D->CompteurEntites()==0) ? true : false; /* Ne renvoi pas d'erreur si le fichier est vide. */
        delete persp_scene_2D;
        persp_scene_2D = nullptr;
        if (!ret)
        {
            return false;
        }
    }

    MAJ_IconeOnglet2D();

    AjoutConsole(tr("Ouverture du fichier") + " '" + nfichier + "'");
    defFichierPrecedentConfig(nfichier);

    scene2D->ReinitRect(Perspective3D::vues2D_t::VUEFACE | Perspective3D::vues2D_t::VUECOTE | Perspective3D::vues2D_t::VUEHAUT);

    setWindowTitle(tr("Zetta6") + " - " + nfichier);
    ChargeRectConfig(Perspective3D::vues2D_t::VUEFACE | Perspective3D::vues2D_t::VUECOTE | Perspective3D::vues2D_t::VUEHAUT);

    ChargeOrigine(Perspective3D::vues2D_t::VUEFACE);
    ChargeOrigine(Perspective3D::vues2D_t::VUECOTE);
    ChargeOrigine(Perspective3D::vues2D_t::VUEHAUT);

    if (!regen_plan && parametres->R_GenereModeleAuto() && gen_modele_demarrage) // Si défini dans la config et autorisé par les arguments sur la ligne de commande...
    {
        if (scene2D->VuesDefinies())
        {
            FermeVues3D(true);
            ExecGenerationModele(false); // Lance la génération du modèle.
        }
    }

    GereIconesOutils2D();
    return true;
}

bool MainWindow::Ouvre_STL_2D(const QString &nfichier, bool regen_plan)
/* Conversion d'un fichier STL (3D) vers un plan 2D. */
{
    Perspective3D::Pmaillage3D maillage;

    if (persp_scene_2D)
    {
        Ferme_plan();
    }

    plan_courant = nfichier;

    if (!maillage.ImportSTL(nfichier.toStdString().data(), 9010, true))
    {
        AfficheMesgStatus(tr("Import STL") + " : " + tr("Erreur") + " (" + tr("Import") + ")", 5);
        AjoutConsole(tr("Erreur lors de l'ouverture du fichier %1 (avez vous les droits d'accès ?)").arg(nfichier));
        return false;
    }

    persp_scene_2D = new Perspective3D::PScene2D;

    Perspective3D::PMat4x4_data matrice_proj; // Matrice identité...
//    matrice_proj.lookAt(Perspective3D::Pvec3(0,0,0), Perspective3D::Pvec3(0.5,1,1));

    if (!maillage.ConversionScene2D(*persp_scene_2D, matrice_proj, qRgbP(parametres->R_CouleurTrace2D())))
    {
        AfficheMesgStatus(tr("Import STL") + " : " + tr("Erreur") + QString(" 3D -> 2D"), 5);
        return false;
    }
    AfficheMesgStatus(tr("Import STL") + QString(" : 100%"), 5);

    if (!scene2D->ImportScenePerspective(persp_scene_2D, true))
    {
        const bool ret = (persp_scene_2D->CompteurEntites()==0) ? true : false; /* Ne renvoi pas d'erreur si le fichier est vide. */
        delete persp_scene_2D;
        persp_scene_2D = nullptr;
        if (!ret)
        {
            return false;
        }
    }

    MAJ_IconeOnglet2D();

    AjoutConsole(tr("Ouverture du fichier") + " '" + nfichier + "'");
    defFichierPrecedentConfig(nfichier);

    scene2D->ReinitRect(Perspective3D::vues2D_t::VUEFACE | Perspective3D::vues2D_t::VUECOTE | Perspective3D::vues2D_t::VUEHAUT);

    setWindowTitle(tr("Zetta6") + " - " + nfichier);
    ChargeRectConfig(Perspective3D::vues2D_t::VUEFACE | Perspective3D::vues2D_t::VUECOTE | Perspective3D::vues2D_t::VUEHAUT);

    ChargeOrigine(Perspective3D::vues2D_t::VUEFACE);
    ChargeOrigine(Perspective3D::vues2D_t::VUECOTE);
    ChargeOrigine(Perspective3D::vues2D_t::VUEHAUT);

    if (!regen_plan && parametres->R_GenereModeleAuto() && gen_modele_demarrage) // Si défini dans la config et autorisé par les arguments sur la ligne de commande...
    {
        if (scene2D->VuesDefinies())
        {
            ExecGenerationModele(false); // Lance la génération du modèle.
        }
    }

    GereIconesOutils2D();
    return true;
}

bool MainWindow::Ouvre_STL_3D(const QString &nfichier, bool bloquant)
/* Importe un fichier STL dans une vue 3D. */
{
//    bloquant = true;

    const int r = (MesgDialog(this, tr("Confirmation"), tr("Vous demandez d'importer un fichier STL. Souhaitez vous fusionner les triangles tangents ?\nCela nécessitera plus de calculs mais permettra d'obtenir un solide mieux adapté à l'export dans certains formats."), QMessageBox::Question, true));

    if (r == -1)
    {
        return false;
    }

    const bool fusion_triangles = (r == 1);
    Perspective3D::PScene3D *scene = new Perspective3D::PScene3D;

    Th_ImportSTL3D *th_import = new Th_ImportSTL3D(scene, nfichier, parametres->R_IdentRAL3D(), fusion_triangles);
    th_import->start();

    SommeilTempoQt(25);

    if (bloquant)
    {
        while (th_import->isRunning())
        {
            AfficheMesgStatus(tr("Import STL") + tr(" : ") + tr("En cours...") + QString(" (") + QString::number(th_import->Avancement()) + QString("%)"), 1);
            SommeilTempoQt(5);
        }
        if (th_import->ValideImport())
        {
            AfficheMesgStatus(tr("Import STL") + tr(" : ") + QString("Terminé"), 5);
        }
        else
        {
            AfficheMesgStatus(tr("Import STL") + tr(" : ") + tr("Erreur"), 5);
            AjoutConsole(tr("Erreur lors de l'ouverture du fichier %1.").arg(nfichier));
            return false;
        }
    }

    MAJ_IconeOnglet2D();
    AjoutConsole(tr("Ouverture du fichier") + " '" + nfichier + "'");
    defFichierPrecedentConfig(nfichier);

    NouvelleVue3D(th_import, nfichier);
    GereIconesOutils2D();
    return true;
}

#ifdef SUPPORT_VISION2D
bool MainWindow::Ouvre_Image(QString nfichier)
/* Exécute la conversion d'image matricielle vers une scène 2D (entités vectorielles) */
{
    if (exec_import)
    {
        return false;
    }
    exec_import = true;
    if (!Ouvre_Image_int(nfichier))
    {
        if (!nfichier.isEmpty())
        {
            MesgDialog(this, tr("Erreur"), tr("Erreur lors de l'import du fichier %1").arg(nfichier), QMessageBox::Information);
        }
        exec_import = false;
        return false;
    }
    exec_import = false;
    return true;
}
// SUPPORT_VISION2D...
bool MainWindow::Ouvre_Image_int(QString nfichier)
{
    if (nfichier.isEmpty())
    {
        nfichier = DialogOuvrir(ExtensionsImagesImport);
        if (nfichier.isEmpty())
        {
            return false;
        }
    }

    if (!QFile::exists(nfichier))
    {
        AjoutConsole(tr("Erreur lors de l'ouverture du fichier %1 (le fichier n'existe pas)").arg(nfichier));
        return false;
    }

    /* Affichage de la boite de dialogue des paramètres de conversion image matricielle -> vectoriel. */
    plan_courant.clear();
    OngletFixe = 0;

    if (persp_scene_2D)
    {
        Ferme_plan();
    }

    plan_courant = nfichier;

    PasseContexte2D();
//    ui->tabWidget->setTabIcon(OngletFixe, QIcon(":/icones/plan_image.png"));
    persp_scene_2D = new Perspective3D::PScene2D;

    Perspective3D::ParametresVision parametres_v2d;
    if (!parametres->AssigneParametresVision2D(parametres_v2d, ConfigVision::DOMAINE_IMPORT))
    {
        AfficheMesgStatus(tr("Import image") + " : " + tr("Erreur de récupération des paramètres."), 5);
#ifdef DEBUG
        qDebug() << "MainWindow::Ouvre_Image_int :: Erreur de la récupération des paramètres Vision2D.";
#endif // DEBUG
        return false;
    }

    Th_ImportImage th_import(persp_scene_2D, parametres_v2d, nfichier);

    th_import.start();
    SommeilTempoQt(25);
    while (th_import.isRunning())
    {
        const int avancement = th_import.Scene()->Avancement();
        if (avancement >= 0)
        {
            AfficheMesgStatus(tr("Import image") + QString(" : %1%").arg(QString::number(avancement)), 1);
        }
        else
        {
            AfficheMesgStatus(tr("Import image") + QString(" : -"), 1);
        }
        SommeilTempoQt(5);
//        QThread::msleep(5);
    }

    if (th_import.ValideImport())
    {
        AfficheMesgStatus(tr("Import image") + QString(" : 100%"), 5);
    }
    else
    {
        AfficheMesgStatus(tr("Import image") + " : " + tr("Erreur"), 5);

        if (MesgErreurVision2D(th_import.ResultatImport()))
        {
        }

#ifndef DEBUG
        delete persp_scene_2D;
        persp_scene_2D = nullptr;
        return false;
#endif // DEBUG
    }

#ifdef DEBUG_API_P3D
    scene2D->Nettoyage();
    const Perspective3D::Vision *v2d = th_import.Vision2DInst();
    if (v2d)
    {
        scene2D->AjoutImage(v2d->MatriceDebug(0), Perspective3D::Ppoint2D(0,0));
    }
#else
    scene2D->Nettoyage();
#endif // DEBUG_API_P3D

    const Perspective3D::resultat_vision2d_t resultat_v2d = th_import.ResultatImport();
    if ( (resultat_v2d == Perspective3D::resultat_vision2d_t::V2D_OK) ||
         (resultat_v2d == Perspective3D::resultat_vision2d_t::V2D_ASSEMBLAGE) )
    {
        if (!scene2D->ImportScenePerspective(persp_scene_2D, false))
        {
#ifndef DEBUG
            delete persp_scene_2D;
            persp_scene_2D = nullptr;
            return false;
#endif // DEBUG
        }

        MAJ_IconeOnglet2D();

        AjoutConsole(tr("Ouverture du fichier") + " '" + nfichier + "'");
        defFichierPrecedentConfig(nfichier);

        scene2D->ReinitRect(Perspective3D::vues2D_t::VUEFACE | Perspective3D::vues2D_t::VUECOTE | Perspective3D::vues2D_t::VUEHAUT);

        setWindowTitle(tr("Zetta6") + " - " + nfichier);
        ChargeRectConfig(Perspective3D::vues2D_t::VUEFACE | Perspective3D::vues2D_t::VUECOTE | Perspective3D::vues2D_t::VUEHAUT);

        ChargeOrigine(Perspective3D::vues2D_t::VUEFACE);
        ChargeOrigine(Perspective3D::vues2D_t::VUECOTE);
        ChargeOrigine(Perspective3D::vues2D_t::VUEHAUT);

//        if (!regen_plan && parametres->R_GenereModeleAuto() && gen_modele_demarrage) // Si défini dans la config et autorisé par les arguments sur la ligne de commande...
//        {
//            if (scene2D->VuesDefinies())
//            {
//                ExecGenerationModele(false); // Lance la génération du modèle.
//            }
//        }

        GereIconesOutils2D();
    }
    return true;
}
#endif // SUPPORT_VISION2D

void MainWindow::NouveauRectSelection(Perspective3D::vues2D_t vue, bool ignore_gen_auto)
/* L'utilisateur vient de définir un nouveau rectangle de sélection (signal émis depuis la vue 2D), on l'enregistre dans les paramètres pour l'afficher à la prochaine ouverture. */
{
    const bool mode_interne = true;
    const qreal delta_min_cadre = 5.0;

    const QGraphicsRectItem *rect_vue_ptr = scene2D->RectVueConst(vue);

    if (!persp_scene_2D || !persp_scene_2D->ValideScene() || !scene2D->ValideElement(rect_vue_ptr))
    {
        SupprimeVue(Perspective3D::vues2D_t::VUEFACE | Perspective3D::vues2D_t::VUECOTE | Perspective3D::vues2D_t::VUEHAUT | Perspective3D::vues2D_t::VUEMULT);
        return;
    }

    if (vue == Perspective3D::vues2D_t::VUEFACE)
    {
        QRectF rectface = rect_vue_ptr->boundingRect().normalized();
        if ((rectface.width() < delta_min_cadre) || (rectface.height() < delta_min_cadre))
        {
            SupprimeVue(Perspective3D::vues2D_t::VUEFACE | Perspective3D::vues2D_t::VUECOTE | Perspective3D::vues2D_t::VUEHAUT | Perspective3D::vues2D_t::VUEMULT);
            return;
        }

        parametres->defRectSelection(plan_courant, "vface", rectface.x(), rectface.y(), rectface.width(), rectface.height());
        ui->actionVue_Face->setChecked(true);
    }
    else if (vue == Perspective3D::vues2D_t::VUECOTE)
    {
        QRectF rectcote = rect_vue_ptr->boundingRect().normalized();
        if ((rectcote.width() < delta_min_cadre) || (rectcote.height() < delta_min_cadre))
        {
            SupprimeVue(Perspective3D::vues2D_t::VUEFACE | Perspective3D::vues2D_t::VUECOTE | Perspective3D::vues2D_t::VUEHAUT | Perspective3D::vues2D_t::VUEMULT);
            return;
        }

        parametres->defRectSelection(plan_courant, "vcote", rectcote.x(), rectcote.y(), rectcote.width(), rectcote.height());
        ui->actionVue_cote->setChecked(true);
    }
    else if (vue == Perspective3D::vues2D_t::VUEHAUT)
    {
        QRectF recthaut = rect_vue_ptr->boundingRect().normalized();
        if ((recthaut.width() < delta_min_cadre) || (recthaut.height() < delta_min_cadre))
        {
            SupprimeVue(Perspective3D::vues2D_t::VUEFACE | Perspective3D::vues2D_t::VUECOTE | Perspective3D::vues2D_t::VUEHAUT | Perspective3D::vues2D_t::VUEMULT);
            return;
        }

        parametres->defRectSelection(plan_courant, "vhaut", recthaut.x(), recthaut.y(), recthaut.width(), recthaut.height());
        ui->actionVue_haute->setChecked(true);
    }
    else if (vue == Perspective3D::vues2D_t::VUEMULT) /* Vue multiple, à séparer... */
    {
#ifdef DEBUG
        scene2D->ControleIdsEntites(persp_scene_2D);
#endif // DEBUG

        const QRectF rectmult = rect_vue_ptr->boundingRect().normalized();

        if ((rectmult.width() < delta_min_cadre) || (rectmult.height() < delta_min_cadre)) /* Qt a un peu de mal à gérer les collisions si le cadre est trop petit. */
        {
            SupprimeVue(Perspective3D::vues2D_t::VUEFACE | Perspective3D::vues2D_t::VUECOTE | Perspective3D::vues2D_t::VUEHAUT | Perspective3D::vues2D_t::VUEMULT);
            return;
        }

        Perspective3D::PerspectiveVuesAuto *separatrice = Perspective3D::PerspectiveVuesAuto::Construct();

        if (mode_interne)
        {
            const int compteur_entites = separatrice->AjoutEntites(*persp_scene_2D, Perspective3D::Prect2D(rectmult.x(), -(rectmult.y()), rectmult.x()+rectmult.width(), -(rectmult.y()+rectmult.height())));
            if (compteur_entites <= 2) // Les vues seront supprimées si la sélection est considérée comme invalide (trop peu d'éléments).
            {
                SupprimeVue(Perspective3D::vues2D_t::VUEFACE | Perspective3D::vues2D_t::VUECOTE | Perspective3D::vues2D_t::VUEHAUT | Perspective3D::vues2D_t::VUEMULT);
                return;
            }

#ifdef DEBUG
            qDebug() << "Nombre d'entités pour la séparation des vues : " << compteur_entites;
#endif // DEBUG
        }
        else
        {
            const QList<QGraphicsItem *> elements = scene2D->items(rectmult, Qt::ContainsItemBoundingRect, Qt::AscendingOrder);
//            QList<QGraphicsItem *> elements = scene2D->items(rectmult, Qt::IntersectsItemBoundingRect, Qt::AscendingOrder);

//            if (elements.size() <= 2) // Les vues seront supprimées si la sélection est considérée comme invalide (trop peu d'éléments).
//            {
//                SupprimeVue(Perspective3D::vues2D_t::VUEFACE | Perspective3D::vues2D_t::VUECOTE | Perspective3D::vues2D_t::VUEHAUT | Perspective3D::vues2D_t::VUEMULT);
//                return;
//            }

            const Perspective3D::PStdVectGroupeEnts2D &groupes_ents_perspective = persp_scene_2D->Groupes();

            int compteur_entites = 0;
            int compteur_entites_courbes = 0;

            for (QList<QGraphicsItem *>::const_iterator i = elements.constBegin(); i != elements.constEnd(); ++i)
            {
                const QGraphicsItem *entite = *i;

                if (entite->type() == QGraphicsItemGroup::Type) /* On ignore si il s'agit d'une entité de groupe */
                {
                    continue;
                }
                else if (entite->type() == QGraphicsPixmapItem::Type)
                {
                    continue;
                }
                else if (entite->type() == QGraphicsTextItem::Type)
                {
                    continue;
                }

                const qulonglong user_role_ent = entite->data(Qt::UserRole).toULongLong();
                const qint32 id_entite = qint32(user_role_ent & 0xFFFFFFFF); /* Récupère l'id de l'élément */
                const qint32 id_groupe_entite = qint32(user_role_ent >> 32); /* Récupère le groupe de l'élément */

                if (id_entite < 0)
                {
                    continue;
                }
                if (id_groupe_entite < 0)
                {
                    continue;
                }
                if (static_cast<size_t>(id_groupe_entite) >= groupes_ents_perspective.size())
                {
                    continue;
                }

                const Perspective3D::PGroupeEnts2D *groupe = groupes_ents_perspective[id_groupe_entite];

                if (entite->type() == QGraphicsRectItem::Type) /* On ignore si il s'agit d'un rectangle de sélection */
                {
                    const Perspective3D::PStdVectPoints2D &listepoints = groupe->ListePoints();
                    if (static_cast<size_t>(id_entite) >= listepoints.size()) // Erreur d'alignement entre le groupe et la scène 2D ?!
                    {
                        continue;
                    }

                    const Perspective3D::Ppoint2D &p = listepoints[id_entite];

                    if (rectmult.contains(QPointF(p.X(), -(p.Y()))))
                    {
                        separatrice->AjoutLigne2D(Perspective3D::Pligne2D(p, p));
                        ++compteur_entites;
                    }
                }
                else if (entite->type() == QGraphicsLineItem::Type) /* On a affaire à une ligne. */
                {
                    const Perspective3D::PStdVectLignes2D &listelignes = groupe->ListeLignes();
                    if (static_cast<size_t>(id_entite) >= listelignes.size()) // Erreur d'alignement entre le groupe et la scène 2D ?!
                    {
                        continue;
                    }

                    const Perspective3D::Pligne2D &l = listelignes[id_entite]; /* Récupère l'élement dans l'interface dxf (commence par 1) */

                    // Contrôle la position de la ligne dans le rectangle, Qt a parfois de ratés avec Scene2D.items():
                    if (rectmult.contains(QPointF(l.P1Const().X(), -(l.P1Const().Y()))) && rectmult.contains(QPointF(l.P2Const().X(), -(l.P2Const().Y()))))
                    {
                        separatrice->AjoutLigne2D(l);
                        ++compteur_entites;
                    }
                    else
                    {
                        ; // Entitée ignorée ?!
                    }

                }
                else if (entite->type() == QGraphicsEllipseItem::Type) /* On a affaire à une ellipse. */
                {
                    const Perspective3D::PStdVectEllipses2D &listeellipses = groupe->ListeEllipses();
                    if (static_cast<size_t>(id_entite) >= listeellipses.size()) // Erreur d'alignement entre le groupe et la scène 2D ?!
                    {
                        continue;
                    }

                    const Perspective3D::Pellipse2D &e = listeellipses[id_entite];
#if 0
                    if (rectmult.contains(QPointF(e.CentreConst().X(), -(e.CentreConst().Y()))))
#else
                    Perspective3D::Prect2D rect_el = e.rectangle();
                    if (rectmult.contains(QPointF(rect_el.Pmin().X(), -(rect_el.Pmin().Y()))) || rectmult.contains(QPointF(rect_el.Pmax().X(), -(rect_el.Pmax().Y()))))
#endif // 0
                    {
                        separatrice->AjoutEllipse2D(e);
                        ++compteur_entites_courbes;
                    }
                }
                else
                {
                    QRectF rect = entite->boundingRect().normalized();
                    AjoutConsole(tr("Attention: Entitée graphiques 2D non gérée vue %1 à la position").arg(tr(Perspective3D::NomVue(vue))) + " " + QString::number(rect.x()+(rect.width()*.5)) + "," + QString::number(-(rect.y() + (rect.height()*.5))));
                }
            }

            if (compteur_entites <= 2 && !compteur_entites_courbes) // Les vues seront supprimées si la sélection est considérée comme invalide (trop peu d'éléments).
            {
                SupprimeVue(Perspective3D::vues2D_t::VUEFACE | Perspective3D::vues2D_t::VUECOTE | Perspective3D::vues2D_t::VUEHAUT | Perspective3D::vues2D_t::VUEMULT);
                return;
            }

#ifdef DEBUG
            qDebug() << "Nombre d'entités pour la séparation des vues : " << compteur_entites <<  " : courbe=" << compteur_entites_courbes;
#endif // DEBUG
        }

        if (separatrice->Separation(parametres->R_NormeVues2D())) // Extrait les vues
        {
            Perspective3D::Prect2D rf = separatrice->RectFace();
            Perspective3D::Prect2D rc = separatrice->RectCote();
            Perspective3D::Prect2D rh = separatrice->RectHaut();

            //AjoutConsole(tr("Ok."));

            SupprimeVue(Perspective3D::vues2D_t::VUEFACE | Perspective3D::vues2D_t::VUECOTE | Perspective3D::vues2D_t::VUEHAUT | Perspective3D::vues2D_t::VUEMULT);

            unsigned int cpt_vues = 0;

            if (!rc.Nul())
            {
                rc.InvY(); // Pour satisfaire le système de coordonnées de Qt.
                scene2D->Ajout_RectSelection(QRectF(rc.Pmin().X(), rc.Pmin().Y(), rc.Largeur(), rc.Hauteur()), Perspective3D::vues2D_t::VUECOTE, false);
                QRectF rectcote = scene2D->RectVueConst(Perspective3D::vues2D_t::VUECOTE)->boundingRect().normalized();
                parametres->defRectSelection(plan_courant, "vcote", rectcote.x(), rectcote.y(), rectcote.width(), rectcote.height());
//                ++cpt_vues; // Pas dans ce cas là, pour éviter la révolution avec les vues automatiques.
                ui->actionVue_cote->setChecked(true);
            }

            if (!rf.Nul())
            {
                rf.InvY();
                scene2D->Ajout_RectSelection(QRectF(rf.Pmin().X(), rf.Pmin().Y(), rf.Largeur(), rf.Hauteur()), Perspective3D::vues2D_t::VUEFACE, false);
                QRectF rectface = scene2D->RectVueConst(Perspective3D::vues2D_t::VUEFACE)->boundingRect().normalized();
//                vue2D->centerOn(rectface.center()); // Place la vue de face au centre de la vue
                parametres->defRectSelection(plan_courant, "vface", rectface.x(), rectface.y(), rectface.width(), rectface.height());
                ui->actionVue_Face->setChecked(true);
                ++cpt_vues;
            }

            if (!rh.Nul())
            {
                rh.InvY();
                scene2D->Ajout_RectSelection(QRectF(rh.Pmin().X(), rh.Pmin().Y(), rh.Largeur(), rh.Hauteur()), Perspective3D::vues2D_t::VUEHAUT, false);
                QRectF recthaut = scene2D->RectVueConst(Perspective3D::vues2D_t::VUEHAUT)->boundingRect().normalized();
                parametres->defRectSelection(plan_courant, "vhaut", recthaut.x(), recthaut.y(), recthaut.width(), recthaut.height());
                ui->actionVue_haute->setChecked(true);
                ++cpt_vues;
            }

            if (cpt_vues)
            {
                AjoutConsole(tr("Vues automatiques repérées") + " (" + QString::number(cpt_vues) + ")");
                if (parametres->R_Gen3DVuesAuto() && !ignore_gen_auto)
                {
//                    ExecGenerationModele(parametres->R_MenuContextuel2D()); /* Supprime les vues à la suite si on utilise le menu contextuel. */
                    ExecGenerationModele(false);
                }
            }
        }
        else
        {
#ifdef DEBUG
            qDebug() << "Erreur avec la séparation des vues ?!";
#endif // DEBUG

#ifdef MODE_VUEMULT_STRICT
            AjoutConsole(tr("Impossible de trouver les vues. Il peut être nécessaire de laisser plus d'espace entre les vues et de s'assurer de l'alignement."));
            scene2D->ReinitRect(Perspective3D::vues2D_t::VUEMULT);
#else
            AjoutConsole(tr("Une seule vue a été définie, peut être n'est ce pas le résultat attendu. Vous pouvez dans ce cas sélectionner les vues séparément."));

            if (scene2D->ConvVueMult()) /* Quand la fonction renvoi true c'est que les trois vues sont définies sur la vue 2D (peu importe de quelle manière). */
            {
                QRectF rectface = scene2D->RectVueConst(Perspective3D::vues2D_t::VUEFACE)->boundingRect().normalized();
                parametres->defRectSelection(plan_courant, "vface", rectface.x(), rectface.y(), rectface.width(), rectface.height());
                QRectF rectcote = scene2D->RectVueConst(Perspective3D::vues2D_t::VUECOTE)->boundingRect().normalized();
                parametres->defRectSelection(plan_courant, "vcote", rectcote.x(), rectcote.y(), rectcote.width(), rectcote.height());
                QRectF recthaut = scene2D->RectVueConst(Perspective3D::vues2D_t::VUEHAUT)->boundingRect().normalized();
                parametres->defRectSelection(plan_courant, "vhaut", recthaut.x(), recthaut.y(), recthaut.width(), recthaut.height());

                ui->actionVue_Face->setChecked(true);
                ui->actionVue_cote->setChecked(true);
                ui->actionVue_haute->setChecked(true);

                if (parametres->R_Gen3DVuesAuto() && !ignore_gen_auto)
                {
//                    ExecGenerationModele(parametres->R_MenuContextuel2D()); /* Supprime les vues à la suite si on utilise le menu contextuel. */
                    ExecGenerationModele(false);
                }
            }
            else // Sauvegarde indépendamment les vues:
            {
                if (scene2D->RectVueDefini(Perspective3D::vues2D_t::VUEFACE))
                {
                    QRectF rectface = scene2D->RectVueConst(Perspective3D::vues2D_t::VUEFACE)->boundingRect().normalized();
                    parametres->defRectSelection(plan_courant, "vface", rectface.x(), rectface.y(), rectface.width(), rectface.height());
                    ui->actionVue_Face->setChecked(true);
                }
                else
                {
                    ui->actionVue_Face->setChecked(false);
                    parametres->SupprRectSelection(plan_courant, "vface");
                }

                if (scene2D->RectVueDefini(Perspective3D::vues2D_t::VUECOTE))
                {
                    QRectF rectcote = scene2D->RectVueConst(Perspective3D::vues2D_t::VUECOTE)->boundingRect().normalized();
                    parametres->defRectSelection(plan_courant, "vcote", rectcote.x(), rectcote.y(), rectcote.width(), rectcote.height());
                    ui->actionVue_cote->setChecked(true);
                }
                else
                {
                    ui->actionVue_cote->setChecked(false);
                    parametres->SupprRectSelection(plan_courant, "vcote");
                }

                if (scene2D->RectVueDefini(Perspective3D::vues2D_t::VUEHAUT))
                {
                    QRectF recthaut = scene2D->RectVueConst(Perspective3D::vues2D_t::VUEHAUT)->boundingRect().normalized();
                    parametres->defRectSelection(plan_courant, "vhaut", recthaut.x(), recthaut.y(), recthaut.width(), recthaut.height());
                    ui->actionVue_haute->setChecked(true);
                }
                else
                {
                    ui->actionVue_haute->setChecked(false);
                    parametres->SupprRectSelection(plan_courant, "vhaut");
                }
            }
#endif // MODE_VUEMULT_STRICT
        }
        delete separatrice;
    }
    GereIconesOutils2D();
}

void MainWindow::NouvelleOrigine(Perspective3D::vues2D_t vue)
/* L'utilisateur vient de définir manuellement une nouvelle origine. */
{
    if (vue == Perspective3D::vues2D_t::VUEFACE)
    {
        parametres->DefPointOrigine(plan_courant, "oface", scene2D->OrigineFace().x(), scene2D->OrigineFace().y());
        ui->actionOrigine_face->setChecked(true);
    }
    else if (vue == Perspective3D::vues2D_t::VUECOTE)
    {
        parametres->DefPointOrigine(plan_courant, "ocote", scene2D->OrigineCote().x(), scene2D->OrigineCote().y());
        ui->actionOrigine_cote->setChecked(true);
    }
    else if (vue == Perspective3D::vues2D_t::VUEHAUT)
    {
        parametres->DefPointOrigine(plan_courant, "ohaut", scene2D->OrigineHaut().x(), scene2D->OrigineHaut().y());
        ui->actionOrigine_haute->setChecked(true);
    }
    GereIconesOutils2D();
}

void MainWindow::SupprimeOrigine(Perspective3D::vues2D_t vue)
/* L'utilisateur vient de supprimer une origine... */
{
    if (vue & Perspective3D::vues2D_t::VUEFACE)
    {
        scene2D->ReinitOrigine(Perspective3D::vues2D_t::VUEFACE);
        parametres->SupprPointOrigine(plan_courant, "oface");
        ui->actionOrigine_face->setChecked(false);
    }
    if (vue & Perspective3D::vues2D_t::VUECOTE)
    {
        scene2D->ReinitOrigine(Perspective3D::vues2D_t::VUECOTE);
        parametres->SupprPointOrigine(plan_courant, "ocote");
        ui->actionOrigine_cote->setChecked(false);
    }
    if (vue & Perspective3D::vues2D_t::VUEHAUT)
    {
        scene2D->ReinitOrigine(Perspective3D::vues2D_t::VUEHAUT);
        parametres->SupprPointOrigine(plan_courant, "ohaut");
        ui->actionOrigine_haute->setChecked(false);
    }
    if (vue & Perspective3D::vues2D_t::VUENUL)
    {
        scene2D->ReinitOrigine(Perspective3D::vues2D_t::VUENUL);
    }
    GereIconesOutils2D();
}

void MainWindow::OuvreFichierHistorique(QAction *action)
{
    if (animation.Anime()) /* Refuse si l'on est dans une animation (ou une diction) */
    {
        return;
    }

    const int id = action->data().toInt();

    if (id == -2) /* Raccourcis de déblocage de licence. */
    {
        return;
    }
    else if (id == -1) /* Fichier ne faisant pas parti de l'historique, on utilise la boite de dialogue pour en ouvrir un manuellement */
    {
        QString nfichier = DialogOuvrir();
        if (!nfichier.isEmpty())
        {
            Ouvrir(nfichier);
        }
    }
    else
    {
//        Ouvrir(action->text());
        if (id >= 0 && id < ListeHistorique.size())
        {
            const QString prec = ListeHistorique[id]; // Copie.
//            qDebug() << action->text() << ":" << prec;
            Ouvrir(prec);
        }
    }
}

void MainWindow::GereMenuContextuel2D(QPoint pos)
{
    if (scene2D->NbVuesDefinies() == 3)
    {
        ExecGenerationModele(false);
    }
    else
    {
        AfficheMenuContextuel2D(pos);
    }
}

void MainWindow::AfficheMenuContextuel2D(QPoint pos)
{
    MenuContextuel2D.clear();
//    MenuContextuel2D.setTitle(tr("2D"));

    MenuContextuel2D.addMenu(menu_historique);
//    menu_historique->setTitle(tr("Ouvrir"));

    if (ui->action_MAJ_plan->isEnabled())
        { MenuContextuel2D.addAction(ui->action_MAJ_plan); }
    if (ui->actionScripts->isEnabled())
        { MenuContextuel2D.addAction(ui->actionScripts); }
    MenuContextuel2D.addSeparator();

    if (ui->actionGenerer->isEnabled())
         { MenuContextuel2D.addAction(ui->actionGenerer); }
    MenuContextuel2D.addSeparator();

    if (ui->actionVues_automatiques->isEnabled())
        { MenuContextuel2D.addAction(ui->actionVues_automatiques); }
    if (ui->actionVue_Face->isEnabled())
        { MenuContextuel2D.addAction(ui->actionVue_Face); }
    if (ui->actionVue_cote->isEnabled())
        { MenuContextuel2D.addAction(ui->actionVue_cote); }
    if (ui->actionVue_haute->isEnabled())
        { MenuContextuel2D.addAction(ui->actionVue_haute); }
    if (ui->actionSupprimer_les_vues->isEnabled())
        { MenuContextuel2D.addAction(ui->actionSupprimer_les_vues); }
    MenuContextuel2D.addSeparator();

    if (ui->actionExtrusion->isEnabled())
        { MenuContextuel2D.addAction(ui->actionExtrusion); }
    if (ui->actionRevolution->isEnabled())
        { MenuContextuel2D.addAction(ui->actionRevolution); }
    MenuContextuel2D.addSeparator();

    if (ui->actionOrigine_face->isEnabled())
        { MenuContextuel2D.addAction(ui->actionOrigine_face); }
    if (ui->actionOrigine_cote->isEnabled())
        { MenuContextuel2D.addAction(ui->actionOrigine_cote); }
    if (ui->actionOrigine_haute->isEnabled())
        { MenuContextuel2D.addAction(ui->actionOrigine_haute); }
    if (ui->actionSupprimer_les_origines->isEnabled())
        { MenuContextuel2D.addAction(ui->actionSupprimer_les_origines); }
    MenuContextuel2D.addSeparator();

    if (ui->actionParametres->isEnabled())
        { MenuContextuel2D.addAction(ui->actionParametres); }
//    if (ui->actionModele_filaire->isEnabled())
//        { MenuContextuel2D.addAction(ui->actionModele_filaire); }
    MenuContextuel2D.addSeparator();

    if (ui->actionAfficheConsole->isEnabled())
        { MenuContextuel2D.addAction(ui->actionAfficheConsole); }
    if (ui->actionAide->isEnabled())
        { MenuContextuel2D.addAction(ui->actionAide); }

    QPoint pos_m = pos-QPoint(0, MenuContextuel2D.height()/2);
    if (pos_m.x() < 0)
    {
        pos_m.setX(0);
    }
    if (pos_m.y() < 0)
    {
        pos_m.setY(0);
    }
    MenuContextuel2D.popup(pos_m);
}

void MainWindow::ExecFichier_Documentation(const QString &s)
/* Demande d'ouverture de script depuis la documentation... */
{
    if (s.isEmpty())
    {
        return;
    }

    ArretAnimationsAutomate();

    if (Aide->isVisible())
    {
        Aide->close();
    }

    Ouvrir(s);
}

void MainWindow::OuvreConfig_Documentation(const QString &s)
{
    int onglet = -1;

    if (s.compare(tr("ergonomie"), Qt::CaseInsensitive) == 0)
    {
        onglet = PONGLET_ERGONOMIE;
    }
    else if (s.compare(tr("dessin"), Qt::CaseInsensitive) == 0)
    {
        onglet = PONGLET_DESSIN;
    }
    else if (s.compare(tr("gen3d"), Qt::CaseInsensitive) == 0)
    {
        onglet = PONGLET_SOLIDE3D;
    }
    else if (s.compare(tr("vue3d"), Qt::CaseInsensitive) == 0)
    {
        onglet = PONGLET_AFFICHAGE3D;
    }
    else if (s.compare(tr("export"), Qt::CaseInsensitive) == 0)
    {
        onglet = PONGLET_EXPORT;
    }
    else if (s.compare(tr("zetta6"), Qt::CaseInsensitive) == 0)
    {
        onglet = PONGLET_PRODUIT;
    }

//    qDebug() << "Ouvre config documentation : " << s << " : " << onglet;

    if (onglet != -1)
    {
        parametres->show();
        parametres->ForceOnglet(onglet);
    }
}

void MainWindow::FermetureAide(QCloseEvent *ev)
{
    ui->actionAide->setChecked(false);
    ev->accept();
}

void MainWindow::FermetureParametres()
{
    ui->actionParametres->setChecked(false);
}

void MainWindow::ChargeRectConfig(Perspective3D::vues2D_t vues)
/* Charge un rectangle de sélection de vue pour le fichier courant dans la config. */
{
    int cpt_vues = 0;
    const bool animation = false; // parametres->R_AutoriseAnimationScripts();

    /* Charge les rectangles. */
    if (vues & Perspective3D::vues2D_t::VUECOTE)
    {
        QRectF rect = parametres->R_RectSelection(plan_courant, "vcote");
        if (!rect.isNull())
        {
           scene2D->Ajout_RectSelection(rect, Perspective3D::vues2D_t::VUECOTE, false);
           ui->actionVue_cote->setChecked(true);
           ++cpt_vues;
        }
    }
    if (vues & Perspective3D::vues2D_t::VUEFACE)
    {
        QRectF rect = parametres->R_RectSelection(plan_courant, "vface");
        if (!rect.isNull())
        {
           scene2D->Ajout_RectSelection(rect, Perspective3D::vues2D_t::VUEFACE, false);
           ui->actionVue_Face->setChecked(true);
           ++cpt_vues;
        }
    }
    if (vues & Perspective3D::vues2D_t::VUEHAUT)
    {
        QRectF rect = parametres->R_RectSelection(plan_courant, "vhaut");
        if (!rect.isNull())
        {
           scene2D->Ajout_RectSelection(rect, Perspective3D::vues2D_t::VUEHAUT, false);
           ui->actionVue_haute->setChecked(true);
           ++cpt_vues;
        }
    }

    /* Adapte la vue en fonction des rectangles dans la configuration: */
    if (cpt_vues == 0)
    {
        vue2D->AffichageGlobalVue();
    }
    else if (cpt_vues == 3)
    {
        QRectF rect;
        QRectF rectf = parametres->R_RectSelection(plan_courant, "vface");
        if (!rectf.isNull())
        {
            rect = rect.united(rectf);
        }
        QRectF rectc = parametres->R_RectSelection(plan_courant, "vcote");
        if (!rectc.isNull())
        {
            rect = rect.united(rectc);
        }
        QRectF recth = parametres->R_RectSelection(plan_courant, "vhaut");
        if (!recth.isNull())
        {
            rect = rect.united(recth);
        }

        if (animation)
        {
            vue2D->CadrageAnim(rect);
        }
        else
        {
            vue2D->centerOn(rect.center());
        }
    }
    else
    {
        if (vues & Perspective3D::vues2D_t::VUEFACE)
        {
            QRectF rect = parametres->R_RectSelection(plan_courant, "vface");
            if (!rect.isNull())
            {
                if (animation)
                {
                    vue2D->CadrageAnim(rect);
                }
                else
                {
                    vue2D->centerOn(rect.center());
                }
                return;
            }
        }
        if (vues & Perspective3D::vues2D_t::VUECOTE)
        {
            QRectF rect = parametres->R_RectSelection(plan_courant, "vcote");
            if (!rect.isNull())
            {
                if (animation)
                {
                    vue2D->CadrageAnim(rect);
                }
                else
                {
                    vue2D->centerOn(rect.center());
                }
                return;
            }
        }
        if (vues & Perspective3D::vues2D_t::VUEHAUT)
        {
            QRectF rect = parametres->R_RectSelection(plan_courant, "vhaut");
            if (!rect.isNull())
            {
                if (animation)
                {
                    vue2D->CadrageAnim(rect);
                }
                else
                {
                    vue2D->centerOn(rect.center());
                }
                return;
            }
        }
    }
}

void MainWindow::ChargeOrigine(Perspective3D::vues2D_t vue)
{
    QPointF p;
    if (vue == Perspective3D::vues2D_t::VUEFACE)
    {
        p = parametres->R_PointOrigine(plan_courant, "oface");
    }
    else if (vue == Perspective3D::vues2D_t::VUECOTE)
    {
        p = parametres->R_PointOrigine(plan_courant, "ocote");
    }
    else if (vue == Perspective3D::vues2D_t::VUEHAUT)
    {
        p = parametres->R_PointOrigine(plan_courant, "ohaut");
    }

    if (!p.isNull())
    {
        if (vue == Perspective3D::vues2D_t::VUEFACE)
        {
            ui->actionOrigine_face->setChecked(true);
        }
        else if (vue == Perspective3D::vues2D_t::VUECOTE)
        {
            ui->actionOrigine_cote->setChecked(true);
        }
        else if (vue == Perspective3D::vues2D_t::VUEHAUT)
        {
            ui->actionOrigine_haute->setChecked(true);
        }
        scene2D->AjoutOrigine(p, vue);
    }
}

void MainWindow::Ferme_plan(bool reinit_console, bool supprime_precedent_historique)
/* Ferme le fichier de plan en cours. */
{
    plan_courant.clear();
    if (persp_scene_2D)
    {
        if (reinit_console)
        {
            console->clear();
            tempo_affiche_console = 0;
        }
        else
        {
            AjoutConsole(tr("Fermeture."));
        }
        persp_scene_2D->Vide();
        delete persp_scene_2D;
        persp_scene_2D = nullptr;
    }

    scene2D->Nettoyage();

    if (supprime_precedent_historique)
    {
        parametres->defFichier_Precedent(QString());
    }
//    ui->tabWidget->setTabIcon(OngletFixe, QIcon(":/icones/plan_vide.png"));
    MAJ_IconeOnglet2D();
    defIconeStatus(nullptr);
    setWindowTitle(tr("Zetta6"));
}

void MainWindow::MAJ_IconeOnglet2D()
{
    const int id_onglet = OngletFixe;
#ifdef SUPPORT_VISION2D
    if (vue2D->ModeEdition())
    {
        ui->tabWidget->setTabIcon(id_onglet, QIcon(":/icones/plan_image.png"));
    }
    else
#endif // SUPPORT_VISION
    {
        if (persp_scene_2D)
        {
            ui->tabWidget->setTabIcon(id_onglet, QIcon(":/icones/plan_dxf.png"));
        }
        else
        {
            ui->tabWidget->setTabIcon(id_onglet, QIcon(":/icones/plan_vide.png"));
        }
    }
}

int MainWindow::EnvoiElementsPerspective3D(Perspective3D::Perspective *perspective, Perspective3D::vues2D_t vue, const QGraphicsRectItem *rect, bool mode_interne)
/* Envoi les éléments de la vue donnée en argument à Perspective 3D. Renvoi le nombre d'éléments envoyé. */
{
    if ((vue == Perspective3D::vues2D_t::VUENUL) || (vue == Perspective3D::vues2D_t::VUERREUR) | (vue == Perspective3D::vues2D_t::VUEMULT))
    {
        AjoutConsole(tr("Attention: Vue %i nulle.\n").arg(QString::number(PENUM_CAST_INT(vue))));
        return 0;
    }

    int compteur_ents = 0;

    QRectF rect_vue = rect->rect();

    if (mode_interne) /* On passse par la bibliothèque pour sélectionner les entités. */
    {
        return perspective->AjoutEntitesVue(Perspective3D::Prect2D(rect_vue.x(), -rect_vue.y(), rect_vue.x()+rect_vue.width(), -(rect_vue.y()+rect_vue.height())), vue, *persp_scene_2D);
    }
    else
    {
        if (CompareE(rect_vue.y(), 0.)) /* Apparemment Qt (fonction items()) a un problème quand la coordonnée vaut 0. */
        {
            rect_vue.setY(-1);
            rect_vue.setHeight(rect_vue.height()+1);
        }
        if (CompareE(rect_vue.x(), 0.)) /* Même tarif. */
        {
            rect_vue.setX(-1);
            rect_vue.setWidth(rect_vue.width()+1);
        }

        QList<QGraphicsItem *> elements = scene2D->items(rect_vue, Qt::ContainsItemShape, Qt::AscendingOrder);
//        QList<QGraphicsItem *> elements = scene2D->items(rect_vue, Qt::ContainsItemBoundingRect, Qt::AscendingOrder);

        const Perspective3D::PStdVectGroupeEnts2D &groupes_ents_perspective = persp_scene_2D->Groupes();

        for (QList<QGraphicsItem *>::iterator i = elements.begin(); i != elements.end(); ++i)
        {
            const QGraphicsItem *entite = *i;

            if (entite->type() == QGraphicsItemGroup::Type)
            {
                continue;
            }
            else if (entite->type() == QGraphicsPixmapItem::Type)
            {
                continue;
            }
            else if (entite->type() == QGraphicsTextItem::Type)
            {
                continue;
            }

            const qulonglong user_role_ent = entite->data(Qt::UserRole).toULongLong();
            const qint32 id_entite = qint32(user_role_ent & 0xFFFFFFFF); /* Récupère l'id de l'élément */
            const qint32 id_groupe_entite = qint32(user_role_ent >> 32); /* Récupère le groupe de l'élément */

            if (id_entite < 0)
            {
                continue;
            }
            if (id_groupe_entite < 0)
            {
                continue;
            }
            if (static_cast<size_t>(id_groupe_entite) >= groupes_ents_perspective.size())
            {
                continue;
            }

            const Perspective3D::PGroupeEnts2D *groupe = groupes_ents_perspective[id_groupe_entite];
            if (!groupe->Valide())
            {
                continue;
            }

            if (entite->type() == QGraphicsRectItem::Type) /* On ignore si il s'agit d'un rectangle de sélection */
            {
                const Perspective3D::PStdVectPoints2D &listepoints = groupe->ListePoints();
                if (static_cast<size_t>(id_entite) >= listepoints.size()) // Erreur d'alignement entre le groupe et la scène 2D ?!
                {
                    continue;
                }

                const Perspective3D::Ppoint2D &p = listepoints[id_entite];
                if (rect_vue.contains(QPointF(p.X(), -p.Y())))
                {
                    perspective->AjoutPoint2D(p, vue);
                    ++compteur_ents;
                }
            }
            else if (entite->type() == QGraphicsLineItem::Type) /* On a affaire à une ligne. */
            {
                const Perspective3D::PStdVectLignes2D &listelignes = groupe->ListeLignes();
                if (static_cast<size_t>(id_entite) >= listelignes.size()) // Erreur d'alignement entre le groupe et la scène 2D ?!
                {
                    continue;
                }

                const Perspective3D::Pligne2D &l = listelignes[id_entite]; /* Récupère l'élement dans l'interface dxf (commence par 1) */
                if (rect_vue.contains(QPointF(l.P1Const().X(), -(l.P1Const().Y()))) && rect_vue.contains(QPointF(l.P2Const().X(), -(l.P2Const().Y()))))
                {
                    if (l.Point())
                    {
                        perspective->AjoutPoint2D((l.P1Const()+l.P2Const())*.5, vue);
                        ++compteur_ents;
                    }
                    else
                    {
                        perspective->AjoutLigne2D(l, vue); /* Ajoute la ligne dans Perspective3D. */
                        ++compteur_ents;
                    }
                }
            }
            else if (entite->type() == QGraphicsEllipseItem::Type) /* On a affaire à une ellipse. */
            {
                const Perspective3D::PStdVectEllipses2D &listeellipses = groupe->ListeEllipses();
                if (static_cast<size_t>(id_entite) >= listeellipses.size()) // Erreur d'alignement entre le groupe et la scène 2D ?!
                {
                    continue;
                }

                const Perspective3D::Pellipse2D &e = listeellipses[id_entite];
                if (rect_vue.contains(QPointF(e.CentreConst().X(), -(e.CentreConst().Y()))))
                {
                    perspective->AjoutEllipse2D(e, vue);
                    ++compteur_ents;
                }
            }
            else
            {
                QRectF rect = entite->boundingRect().normalized();
                AjoutConsole(tr("Attention: Entitée graphiques 2D non gérée vue %1 à la position").arg(tr(Perspective3D::NomVue(vue))) + " " + QString::number(rect.x()+(rect.width()*.5)) + "," + QString::number(-(rect.y() + (rect.height()*.5))));
            }
        }
    }
    return compteur_ents;
}

void MainWindow::FermeVues3D(bool manuel)
/* Ferme toutes les vues 3D. */
{
    bool restaure_etat_animation = false;

    if (manuel)
    {
        ArretAnimationsAutomate();
    }
    else
    {
        restaure_etat_animation = animation.Anime();
        animation.ArretAnimations(); /* Arrêt des animations pour gérer la fermeture des onglets et ne pas créer de blocage. */
    }

    for(int i=ui->tabWidget->count()-1; i>=NombreVues2D; --i)
    {
        FermeOnglet3D(i);
    }

    if (!manuel)
    {
        if (restaure_etat_animation)
        {
            animation.InitAnimations();
        }
    }

//    ListeVues3D->clear();
}

bool MainWindow::FermeOnglet3D(int index)
/* Ferme un onglet 3D dont l'id est donné en argument. */
{
    if (Contexte2D(index)) /* On demande la fermeture de la vue 2D ?! */
    {
        return false;
    }

    animation.ArretAnimations();

    if (index >= ui->tabWidget->count())
    {
        return false;
    }

    if (index >= NombreVues2D)
    {
        Interface3d *interface3d = ListeVues3D->at(index-NombreVues2D);
        parametres->defAfficheProprietes3D(interface3d->ProprietesVisible());

        if (ui->tabWidget->widget(index)->close())
        {
            if ((ui->tabWidget->count()-1) == int(ListeVues3D->size()))
            {
                interface3d->ArretGeneration();
                delete interface3d;
                StdVectSupprId(*ListeVues3D, index-NombreVues2D);

                for(int i=index; i<int(ListeVues3D->size()); ++i)
                {
                    ListeVues3D->at(i)->defIdOnglet(i);
                }

                return true;
            }
            else
            {
#ifdef DEBUG
                qDebug() << "FermeOnglet3D : Attention, décalage entre le nombre d'onglets et la liste des vues 3D : " << (ui->tabWidget->count()-1) << "!=" << ListeVues3D->size();
#endif // DEBUG
            }
        }
    }
    return false;
}

void MainWindow::FermeOnglet(int index, bool arret_script)
{
    bool restaure_etat_animation = !arret_script && animation.Anime();

//    if (arret_script && animation.Anime())
        animation.ArretAnimations(); /* Arrêt des animations pour gérer la fermeture des onglets et ne pas créer de blocage. */

    if (Contexte2D(index)) // Vue 2D
    {
        ArretAnimationsAutomate(); /* On s'assure de ne pas avoir d'exécution de script... */

//        if (PleinEcranApp(this) && ui->tabWidget->count() == 1) // Ferme l'application si on est en mode plein écran.
//        {
//            close();
//        }
//        else
            if (ui->tabWidget->count() > 1)
        {
            if (MesgDialog(this, tr("Confirmation"), tr("Fermer tous les onglets ?"), QMessageBox::Question) > 0)
            {
                FermeVues3D(true);
                Ferme_plan(true);
            }
        }
        else
        {
            Ferme_plan(true);
        }
    }
    else // Une des vues 3D
    {
        const int n_onglets = ui->tabWidget->count();
        if (n_onglets < 2) { return; }

        if (arret_script)
        {
            ArretAnimationsAutomate();
        }

        if (FermeOnglet3D(index))
        {
            // Renomme les onglets:
            for(int i=1; i<n_onglets; ++i)
            {
                QString s = ui->tabWidget->tabText(i);
                QStringList ls = s.split(":");
                ui->tabWidget->setTabText(i, ls[0] + ":" + QString::number(i));
            }
        }
    }
    GereIconesOutils2D();
    if (restaure_etat_animation)
    {
        animation.InitAnimations();
    }
}

void MainWindow::on_tabWidget_tabCloseRequested(int index)
{
    FermeOnglet(index, true);

    if (!Contexte2D(index))
    {
        Interface3d *interface_courante = Interface3DCourante();
        if (interface_courante)
        {
            interface_courante->ExecShaderPost();
        }
    }
}

void MainWindow::on_tabWidget_currentChanged(int index)
{
    for(int i=1; i<ui->tabWidget->count(); ++i)
    {
        Interface3d &interface3d = *(ListeVues3D->at(i-NombreVues2D));
        if (i != index) // Ferme la fenêtre des propriétés
        {
#ifdef ACTIVE_PROPRIETES3D_DOCK
            interface3d.FermeProprietes();
#endif // ACTIVE_PROPRIETES3D_DOCK
            interface3d.TermineShaderPost();
        }
        else
        {
            interface3d.ExecShaderPost();
        }
    }

    if (Contexte2D(index)) // Vue 2D
    {
//        if (ValideBarreOutils2D())
//        {
//            if (barreoutils2d->isFloating())
//                barreoutils2d->show();
//            else
//            {
////                barreoutils2d->setEnabled(true);
//            }

//            barreoutils2d->setEnabled(true);
//        }
        ui->statusBar->setHidden(false);
        automate.AutomateNonConst().Continue(); /* Si l'automate a été mis en pause, on reprend. */

        ui->actionAfficheConsole->setCheckable(true);
    }
    else // L'une des vues 3D.
    {
//        if (barreoutils2d->isFloating())
//            barreoutils2d->hide();
//        else
//        {
////            barreoutils2d->setEnabled(false);
//        }

//        barreoutils->setEnabled(false);

//        if (index < int(ListeVues3D->size()))
//        {
//            ListeVues3D->at(index)->Vue_Modele()->RedimAuto();
//        }

        ui->statusBar->setHidden(true);
        if (GestionScripts->isVisible())
        {
            GestionScripts->close();
        }
        ui->actionAfficheConsole->setCheckable(false);
    }
    GereOutilsBarreOnglets();
    GereIconesOutils2D();
}

void MainWindow::GereIconesOutils2D(bool verrouille)
/* Active/Désactive les icones de la barre d'outils suivant le contexte du logiciel */
{
    if (verrouille)
    {
        ui->actionVues_automatiques->setEnabled(false);
        menu_historique->setEnabled(false);
        if (ValideBarreOutils2D())
        {
            bouton_ouvrir->setEnabled(false);
        }
        ui->actionOuvrir->setEnabled(false);
        ui->actionFermer_le_plan->setEnabled(false);
        ui->actionExtrusion->setEnabled(false);
        ui->actionRevolution->setEnabled(false);
        ui->actionVue_Face->setEnabled(false);
        ui->actionVue_cote->setEnabled(false);
        ui->actionVue_haute->setEnabled(false);
        ui->actionSupprimer_les_vues->setEnabled(false);
        ui->actionOrigine_face->setEnabled(false);
        ui->actionOrigine_cote->setEnabled(false);
        ui->actionOrigine_haute->setEnabled(false);
        ui->actionSupprimer_les_origines->setEnabled(false);
        ui->actionGenerer->setEnabled(false);
        ui->actionParametres->setEnabled(false);
        ui->actionModele_filaire->setEnabled(false);
        ui->actionAide->setEnabled(false);
        ui->actionAfficheConsole->setEnabled(false);
        ui->action_MAJ_plan->setEnabled(false);
        ui->actionScripts->setEnabled(false);
        return;
    }
    else
    {
        menu_historique->setEnabled(true);
        if (ValideBarreOutils2D())
        {
            bouton_ouvrir->setEnabled(true);
        }
        ui->actionAide->setEnabled(true);
        ui->actionAfficheConsole->setEnabled(true);
        ui->actionParametres->setEnabled(true);
        ui->action_MAJ_plan->setEnabled(true);
    }

    if (Contexte2D()) // Vue 2D
    {
        ui->actionOuvrir->setEnabled(true);
        menu_historique->setEnabled(true);
        if (ValideBarreOutils2D())
        {
            bouton_ouvrir->setEnabled(true);
        }
        ui->actionParametres->setEnabled(true);
        ui->actionScripts->setEnabled(true);

        ui->actionModele_filaire->setEnabled(parametres->LicenceValideP3D());

        if (persp_scene_2D) // Un fichier DXf a été ouvert
        {
            ui->action_MAJ_plan->setEnabled(true);
            ui->actionVues_automatiques->setEnabled(true);
            ui->actionFermer_le_plan->setEnabled(true);
            ui->actionExtrusion->setEnabled(true);
            ui->actionRevolution->setEnabled(true);
            ui->actionVue_Face->setEnabled(true);
            ui->actionVue_cote->setEnabled(true);
            ui->actionVue_haute->setEnabled(true);

            ui->actionOrigine_face->setEnabled(scene2D->RectVueDefini(Perspective3D::vues2D_t::VUEFACE));
            ui->actionOrigine_cote->setEnabled(scene2D->RectVueDefini(Perspective3D::vues2D_t::VUECOTE));
            ui->actionOrigine_haute->setEnabled(scene2D->RectVueDefini(Perspective3D::vues2D_t::VUEHAUT));

//            if (scene2D->RectVueDefini(Perspective3D::vues2D_t::VUEHAUT))
//            {
//                ui->actionExtrusion->setEnabled(false);
//                ui->actionRevolution->setEnabled(false);
//            }
//            else
//            {
//                if (scene2D->RectVueDefini(VUE_REVOLUTION))
//                {
//                    ui->actionExtrusion->setEnabled(false);
//                }
//                if (scene2D->RectVueDefini(VUE_EXTRUSION))
//                {
//                    ui->actionRevolution->setEnabled(false);
//                }
//            }

            if (scene2D->RectVueDefini(Perspective3D::vues2D_t::VUEFACE) || scene2D->RectVueDefini(Perspective3D::vues2D_t::VUECOTE) || scene2D->RectVueDefini(Perspective3D::vues2D_t::VUEHAUT))
            {
                ui->actionSupprimer_les_vues->setEnabled(true);
//                ui->actionSupprimer_les_origines->setEnabled(true);
                if (scene2D->RectVueDefini(VUE_EXTRUSION) || scene2D->RectVueDefini(VUE_REVOLUTION)) // Au moins vue des vues pour l'extrusion ou la révolution.
                {
                    ui->actionGenerer->setEnabled(true);
                }
            }
            else
            {
                ui->actionSupprimer_les_vues->setEnabled(false);
//                ui->actionSupprimer_les_origines->setEnabled(false);
                ui->actionGenerer->setEnabled(false);
            }
            ui->actionSupprimer_les_origines->setEnabled(scene2D->OrigineDefinie(Perspective3D::vues2D_t::VUEFACE) || scene2D->OrigineDefinie(Perspective3D::vues2D_t::VUECOTE) || scene2D->OrigineDefinie(Perspective3D::vues2D_t::VUEHAUT));
        }
        else // Aucun plan ouvert.
        {
            ui->action_MAJ_plan->setEnabled(false);
            ui->actionVues_automatiques->setEnabled(false);
            ui->actionFermer_le_plan->setEnabled(false);
            ui->actionExtrusion->setEnabled(false);
            ui->actionRevolution->setEnabled(false);
            ui->actionVue_Face->setEnabled(false);
            ui->actionVue_cote->setEnabled(false);
            ui->actionVue_haute->setEnabled(false);
            ui->actionSupprimer_les_vues->setEnabled(false);
            ui->actionOrigine_face->setEnabled(false);
            ui->actionOrigine_cote->setEnabled(false);
            ui->actionOrigine_haute->setEnabled(false);
            ui->actionSupprimer_les_origines->setEnabled(false);
            ui->actionGenerer->setEnabled(false);
        }
    }
    else // Une des vues 3D.
    {
        ui->actionVues_automatiques->setEnabled(false);
        menu_historique->setEnabled(false);
        if (ValideBarreOutils2D())
        {
            bouton_ouvrir->setEnabled(false);
        }
        ui->actionOuvrir->setEnabled(false);
        ui->action_MAJ_plan->setEnabled(false);
        ui->actionFermer_le_plan->setEnabled(true);
        ui->actionExtrusion->setEnabled(false);
        ui->actionRevolution->setEnabled(false);
        ui->actionVue_Face->setEnabled(false);
        ui->actionVue_cote->setEnabled(false);
        ui->actionVue_haute->setEnabled(false);
        ui->actionSupprimer_les_vues->setEnabled(false);
        ui->actionOrigine_face->setEnabled(false);
        ui->actionOrigine_cote->setEnabled(false);
        ui->actionOrigine_haute->setEnabled(false);
        ui->actionSupprimer_les_origines->setEnabled(false);
        ui->actionGenerer->setEnabled(false);
        ui->actionParametres->setEnabled(true);
        ui->actionModele_filaire->setEnabled(false);
        ui->actionScripts->setEnabled(false);
    }
}

void MainWindow::GereIconeGestionnaireScripts(bool etat)
{
//    ui->actionScripts->setChecked(DialogScripts.isVisible());
    ui->actionScripts->setChecked(etat);
}

void MainWindow::ImprimeModele()
{
    Interface3d *interface_3d = Interface3DCourante();
    if (!interface_3d) { return; }

    QPrinter printer;
    parametres->ConfigurationImprimante(&printer); /* Récupère la dernière configuration utilisateur sur l'imprimante. */

    QPrintDialog *dialog = new QPrintDialog(&printer, this);
    dialog->setAttribute(Qt::WA_DeleteOnClose, true);
    dialog->setOption(QAbstractPrintDialog::PrintToFile, true);
    dialog->setOption(QAbstractPrintDialog::PrintShowPageSize, true);
    dialog->setOption(QAbstractPrintDialog::PrintCollateCopies, true);
    dialog->setOption(QAbstractPrintDialog::PrintCurrentPage, true);


//    ui->buttonBox->button(QDialogButtonBox::Apply)->setText(tr("Appliquer"));

    if (dialog->exec() != QDialog::Accepted ) { return; }

//    if (!printer.isValid())
//    {
//        MesgDialog(this, tr("Erreur"), tr("L'imprimante ne semble pas valide.<br />Impression annulée."), QMessageBox::Critical);
//        return;
//    }

    parametres->defConfigurationImprimante(dialog->printer()); /* Sauvegarde des paramètres d'impression. */

    QPainter painter;

    if (!painter.begin(&printer))
    {
        MesgDialog(this, tr("Erreur"), tr("L'imprimante ne semble pas valide<br />Impression annulée."), QMessageBox::Information);
        return;
    }

    QSize taille_papier = painter.viewport().size();

    VueModele3D *vuemod = interface_3d->Vue_Modele();
    Perspective3D::PCouleur couleur_scene(Perspective3D::RAL::Index(vuemod->IdCouleurScene()));

    vuemod->defCouleurFond(qRgb(255, 255, 255));
    vuemod->AffichageCentreSurface(IDNUL);
    vuemod->AffichagePoint(IDNUL);
    interface_3d->SelectionSurface(0, false, false, true);
    vuemod->MiseAJourAffichage(true);

    QImage capture = vuemod->CaptureEcran(); /* Capture d'écran. */

    /* Cadrage: */
    capture = capture.scaledToHeight(taille_papier.height(), Qt::SmoothTransformation);
    QImage image = capture.copy((capture.width()/2)-(taille_papier.width()/2), 0, taille_papier.width(), capture.height());

    QSize size = image.size();
    size.scale(taille_papier, Qt::KeepAspectRatio);

    painter.setViewport(5, 5, size.width()-10, size.height()-10);
    painter.setWindow(image.rect());

    /* Insertion de l'image: */
    painter.drawImage(2, 2, image);

    /* Ajout du texte: */
    QString str_credit = parametres->R_CreditExport(true);
    if (!str_credit.isEmpty())
    {
        painter.setPen(QPen(qColor(couleur_scene.Div())));
        QFont font_p = painter.font();
        font_p.setBold(true);
        const unsigned int taille_font = taille_papier.height()/30;
        font_p.setPixelSize(taille_font);
        font_p.setWeight(QFont::Normal);
        painter.setFont(font_p);

        const QRect rect_image = image.rect();
        const int espacement_texte = taille_font*1.5;
//        QString date_c = QDateTime::currentDateTime().toString(tr("dd/MM/yyyy"));
//        painter.drawText(QRect(rect_image.x(), rect_image.height()-espacement_texte, rect_image.width()-(espacement_texte/2), espacement_texte), Qt::AlignRight, tr("Généré par Zetta6 le %1").arg(date_c));

        painter.drawText(QRect(rect_image.x(), rect_image.height()-espacement_texte, rect_image.width()-(espacement_texte/2), espacement_texte), Qt::AlignRight, str_credit);
    }
    painter.end();

    vuemod->defCouleurFond(parametres->R_CouleurFond3D());
    vuemod->MiseAJourAffichage();
}

void MainWindow::AfficheConfigAffichage3D(int id_type_config)
{
    parametres->show();
    parametres->ForceOnglet(PONGLET_AFFICHAGE3D, false);
    parametres->ForceSousOngletAffichage(id_type_config);
}

void MainWindow::SelectionModeAffichage3D(int index)
{
    Interface3d *inter_3d = Interface3DCourante();
    if (inter_3d)
    {
        inter_3d->ForceModeAffichage(index, true);
    }
}

void MainWindow::SelectionCouleur3D(int index)
{
    Interface3d *inter_3d = Interface3DCourante();
    if (inter_3d)
    {
        inter_3d->ForceSelectionCouleur(index, false);
    }
}

void MainWindow::SelectionCouleurPersp3D(int index)
{
    Interface3d *inter_3d = Interface3DCourante();
    if (inter_3d)
    {
        inter_3d->ForceSelectionCouleur(index, true);
    }
}

void MainWindow::ExportImageModele()
{
    Interface3d *interface_3d = Interface3DCourante();
    if (!interface_3d) { return; }

    VueModele3D *vuemod = interface_3d->Vue_Modele();

    QString fichier = FichierChemin(plan_courant);

    QString nfichier;
    QString type_fichier;

    const QString type_export_conf = parametres->R_TypeExport2DDefaut();

    if (type_export_conf == "PNG")
    {
        const QString format_type = QString("%1 (*.png);;%2 (*.jpeg)").arg(tr("Image PNG"), tr("Image JPEG"));
        nfichier = DialogEnregistrer(parametres->R_CheminExport() + QDir::separator() + fichier + ".png", format_type, &type_fichier);
    }
    else if (type_export_conf == "JPEG")
    {
        const QString format_type = QString("%2 (*.jpeg);;%1 (*.png)").arg(tr("Image PNG"), tr("Image JPEG"));
        nfichier = DialogEnregistrer(parametres->R_CheminExport() + QDir::separator() + fichier + ".jpeg", format_type, &type_fichier);
    }
    else
    {
#ifdef DEBUG
        qDebug() << "ExportImageModele() :: Erreur de type par défaut inconnu : " << type_export_conf;
#endif
        const QString format_type = QString("%1 (*.png);;%2 (*.jpeg)").arg(tr("Image PNG"), tr("Image JPEG"));
        nfichier = DialogEnregistrer(parametres->R_CheminExport() + QDir::separator() + fichier + ".png", format_type, &type_fichier);
    }

    if (nfichier.isEmpty())
    {
        return;
    }

    Perspective3D::PCouleur couleur_scene(Perspective3D::RAL::Index(vuemod->IdCouleurScene()));

    /* Désactivation de toute éventuelle sélection */
    vuemod->AffichageCentreSurface(IDNUL);
    vuemod->AffichagePoint(IDNUL);
    interface_3d->SelectionSurface(0, false, false, true);
//    vuemod->defCouleurFond(PCouleur_GL(0, 0, 0, 0));
    vuemod->MiseAJourAffichage(true);

    QImage image;
    QImage capture = vuemod->CaptureEcran();

    if (parametres->R_Export2DVignette()) /* Cadrage de l'image (au centre et réduction de la taille des bords): */
    {
        if (capture.width() > capture.height())
        {
            capture = capture.scaledToHeight(parametres->R_TailleVignetteExport2D(), Qt::SmoothTransformation);
        }
        else
        {
            capture = capture.scaledToWidth(parametres->R_TailleVignetteExport2D(), Qt::SmoothTransformation);
        }

        const int centre_x = capture.width()/2;
        const int centre_y = capture.height()/2;

        if (capture.width() > capture.height())
        {
            image = capture.copy(centre_x-centre_y, 0, capture.height(), capture.height());
        }
        else
        {
            image = capture.copy(centre_y-centre_x, 0, capture.width(), capture.width());
        }
    }
    else
    {
        image = capture;
    }

    image.setText("Author", "Zetta6");

    /* Ajout du texte "Généré par Zetta6" sur l'image: */
    QString str_credit = parametres->R_CreditExport();

    if (!str_credit.isEmpty())
    {
        QPainter paint(&image);
        paint.setPen(QPen(qColor(couleur_scene.Div())));
        QFont font_p = paint.font();
        font_p.setBold(true);
        const unsigned int taille_font = parametres->R_TailleVignetteExport2D()/20;
        font_p.setPixelSize(taille_font);
        font_p.setWeight(QFont::Bold);
        paint.setFont(font_p);

        const QRect rect_image = image.rect();
        const int espacement_texte = taille_font*1.5;
        paint.drawText(QRect(rect_image.x(), rect_image.height()-espacement_texte, rect_image.width()-(espacement_texte/2), espacement_texte), Qt::AlignRight, str_credit);
    }

    if (!nfichier.isEmpty())
    {
        if (type_fichier.contains(".png"))
        {
            image.save(nfichier, "PNG", -1);
            parametres->defTypeExport2DDefaut("PNG");
        }
        else if (type_fichier.contains(".jpeg"))
        {
            image.save(nfichier, "JPEG", -1);
            parametres->defTypeExport2DDefaut("JPEG");
        }
        else
        {
            MesgDialog(this, tr("Erreur"), tr("Type de fichier invalide, l'opération a été annulée."), QMessageBox::Information);
            return;
        }
    }
}

#ifdef SUPPORT_GIF
void MainWindow::EnregistreAnimationModele()
{
    Interface3d *interface_3d = Interface3DCourante();
    if (!interface_3d) { return; }

    VueModele3D *vuemod = interface_3d->Vue_Modele();

    QString fichier = FichierChemin(plan_courant);

    QString type_fichier;
    const QString format_type = QString("%1 (*.gif)").arg(tr("GIF animé"));
    QString nfichier = DialogEnregistrer(parametres->R_CheminExport() + QDir::separator() + fichier + ".gif", format_type, &type_fichier);

    if (nfichier.isEmpty())
    {
        return;
    }
    else
    {
        if (!nfichier.endsWith(".gif", Qt::CaseInsensitive))
        {
            MesgDialog(this, tr("Erreur"), tr("Type de fichier invalide, l'opération a été annulée."), QMessageBox::Information);
            return;
        }
    }

    animation.InitAnimations();
    if (!animation.CurseurPoint(QPoint(250, 250), EcranWidget(this)))
    {
        ;
    }
    animation.ArretAnimations();

    Perspective3D::PCouleur couleur_scene(Perspective3D::RAL::Index(vuemod->IdCouleurScene()));

    const Perspective3D::Pvec3 axe_perspective(-16.5, 45., 0.);
    const Perspective3D::Pvec3 position_perspective(0., 0., 1.5);
    const int increment_angles = 10; /* Différent d'angle entre chaque images. */

    const bool cadrage_vignette = true; // parametres->R_Export2DVignette() ???
    const unsigned int taille_vignettes = parametres->R_TailleVignetteExport2D();

    Perspective3D::Pvec3 axe_rotation = axe_perspective;
    axe_rotation.defX(axe_rotation.X()-15); /* La version animée est légèrement plus inclinée que la vue en perspective. */
    const unsigned int delai_images = 15; /* Délai en centièmes de secondes entre chaque images. */

    {
        ZGif image_gif(nfichier.toStdString().data(), taille_vignettes, taille_vignettes, 0, delai_images);
        QString str_credit = parametres->R_CreditExport();

        for(int inc_angle=increment_angles; inc_angle<=360; inc_angle+=increment_angles) /* Créé la rotation sur 360° */
        {
            vuemod->AffichageCentreSurface(IDNUL);
            vuemod->AffichagePoint(IDNUL);
            interface_3d->SelectionSurface(0, false, false, true);
            vuemod->AssigneVue(axe_rotation, position_perspective); /* Assigne la rotation. */

            /* Capture d'écran */
            QImage capture = vuemod->CaptureEcran();
            QImage image;

            if (cadrage_vignette)
            {
                if (capture.width() > capture.height())
                {
                    capture = capture.scaledToHeight(taille_vignettes, Qt::SmoothTransformation);
                }
                else
                {
                    capture = capture.scaledToWidth(taille_vignettes, Qt::SmoothTransformation);
                }

                const int centre_x = capture.width()/2;
                const int centre_y = capture.height()/2;

                if (capture.width() > capture.height())
                {
                    image = capture.copy(centre_x-centre_y, 0, capture.height(), capture.height());
                }
                else
                {
                    image = capture.copy(centre_y-centre_x, 0, capture.width(), capture.width());
                }
            }
            else
            {
                image = capture;
            }

            /* Ajout du texte de crédit sur l'image: */
            if (!str_credit.isEmpty())
            {
                QPainter paint(&image);
                paint.setPen(QPen(qColor(couleur_scene.Div())));
                QFont font_p = paint.font();
                font_p.setBold(true);
                const unsigned int taille_font = taille_vignettes/20;
                font_p.setPixelSize(taille_font);
                font_p.setWeight(QFont::Bold);
                paint.setFont(font_p);

                const QRect rect_image = image.rect();
                const int espacement_texte = taille_font*1.5;
                paint.drawText(QRect(rect_image.x(), rect_image.height()-espacement_texte, rect_image.width()-(espacement_texte/2), espacement_texte), Qt::AlignRight, str_credit);
            }
            /* Ajout de l'image dans le GIF. */
            image_gif.AjoutImage(image);

            axe_rotation.defY(axe_perspective.Y()-inc_angle); /* Prépare la rotation suivante */
        }
        /* Fin de portée, la sauvegarde du fichier GIF se fera à la destruction... */
    }

    /* Repasse en vue perspective. */
    vuemod->AssigneVue(axe_perspective, position_perspective);

    const unsigned int n_images = 360/increment_angles;
    const float delai_animation = float(n_images * delai_images)/100.;
    MesgDialog(this, tr(""), tr("L'animation a été enregistrée avec succès dans le fichier<br />%1<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;%2 images de %3x%3 pixels<br />&nbsp;&nbsp;&nbsp;&nbsp;Durée d'une boucle : %4 secondes")
               .arg(nfichier, QString::number(n_images), QString::number(taille_vignettes), QString::number(delai_animation, 'f', 2)), QMessageBox::Information);
}
#endif // SUPPORT_GIF

Interface3d *MainWindow::Interface3DCourante() const
/* Renvoi l'instance d'interface pour suivant l'onglet. Renvoi un pointeur nul si invalide */
{
    if (Contexte2D()) { return nullptr; }
    return ListeVues3D->at(ui->tabWidget->currentIndex()-NombreVues2D);
}

Perspective3D::Perspective *MainWindow::PerspectiveCourantNonConst() const
/* Renvoi l'instance Perspective pour suivant l'onglet. */
{
    if (!Contexte2D())
    {
        const Interface3d *interface_3d = Interface3DCourante();
        if (!interface_3d)
        {
            return nullptr;
        }
        return interface_3d->Perspective();
    }
    return nullptr;
}

const Perspective3D::Perspective *MainWindow::PerspectiveCourant() const
/* Renvoi l'instance Perspective pour suivant l'onglet. */
{
    return PerspectiveCourantNonConst();
}

const Perspective3D::PScene3D* MainWindow::ModeleCourant() const
/* Renvoi le modèle 3D courant. */
{
    if (!Contexte2D())
    {
        const Interface3d *interface_3d = Interface3DCourante();
        if (!interface_3d)
        {
            return nullptr;
        }
        return interface_3d->SceneP3DConst();
    }
    return nullptr;
}

void MainWindow::EnregistreModele(const QString &chemin_fichier, TypesFichiers type_fichier)
{
    if (Contexte2D()) { return; }

    const Perspective3D::PScene3D *scene3d = ModeleCourant();

    if (!scene3d)
    {
        MesgDialog(this, tr("Erreur"), tr("Aucune instance 3D, l'enregistrement a été annulé (%1).").arg(chemin_fichier), QMessageBox::Information);
        return;
    }

    if (!chemin_fichier.isEmpty())
    {
        if (type_fichier == TypesFichiers::NUL)
        {
            type_fichier = TypeFichier(chemin_fichier);
        }
        if (type_fichier == TypesFichiers::NUL) /* Erreur, impossible de déterminer le type d'export ! */
        {
            MesgDialog(this, tr("Erreur"), tr("Impossible de déterminer le type de fichier.\nL'enregistrement a été annulé (%1).").arg(chemin_fichier), QMessageBox::Information);
            return;
        }

        QString nfichier(chemin_fichier);
        QDir chemin(nfichier);
        chemin.cdUp();

        TypesFichiers type_nom_fichier = TypeFichier(nfichier);
        parametres->defDernierDossier_SauvegardeMod(chemin.absolutePath());

        if (type_fichier == TypesFichiers::DXF_3D)
        {
            if (type_nom_fichier == TypesFichiers::NUL) { nfichier += ".dxf"; }

            if (!scene3d->ExportDXF(nfichier.toStdString().data(), parametres->R_ParametresExportDXF()))
            {
                AjoutConsole(tr("Erreur lors de la création du fichier '%1' (droits d'accès ?).").arg(nfichier));
                MesgDialog(this, tr("Erreur"), tr("Erreur lors de l'enregistrement du fichier %1\n\nPeut être n'y a t'il rien à exporter si vous n'avez pas les surface sur le solide et que vous n'exportez pas le modèle filaire,\nou peut être n'avez-vous pas les droits en écriture ?").arg(nfichier), QMessageBox::Information);
                return;
            }
        }
        else if (type_fichier  == TypesFichiers::DXF_2D)
        {
            Perspective3D::PScene2D scene2d_tmp;
            Perspective3D::PMat4x4_data matrice_proj; // Matrice identité...

            if (!scene3d->Maillage().ConversionScene2D(scene2d_tmp, matrice_proj, qRgbP(parametres->R_CouleurTrace2D()))) /* Conversion 3D -> 2D. */
            {
                AjoutConsole(tr("Erreur lors de la conversion de la scène 3D vers la 2D.").arg(nfichier));
                MesgDialog(this, tr("Erreur"), tr("Erreur lors de la conversion de la scène 3D vers la 2D.").arg(nfichier), QMessageBox::Information);
                return;
            }

            if (!scene2d_tmp.ExportDXF(nfichier.toStdString()))
            {
                AjoutConsole(tr("Erreur lors de la création du fichier '%1' (droits d'accès ?).").arg(nfichier));
                MesgDialog(this, tr("Erreur"), tr("Erreur lors de l'enregistrement du fichier %1\n\nPeut être n'y a t'il rien à exporter si vous n'avez pas les surface sur le solide et que vous n'exportez pas le modèle filaire,\nou peut être n'avez-vous pas les droits en écriture ?").arg(nfichier), QMessageBox::Information);
                return;
            }
        }
        else if (type_fichier == TypesFichiers::STL)
        {
            if (type_nom_fichier == TypesFichiers::NUL) { nfichier += ".stl"; }

            bool separe_solides_stl = parametres->R_SepareSolidesSTL(); /* Droit on enregistrer dans des fichiers différents les solides de la scène ? */

            if (!scene3d->ExportSTL(nfichier.toStdString().data(), separe_solides_stl))
            {
                AjoutConsole(tr("Erreur lors de la création du fichier '%1' (droits d'accès ?).").arg(nfichier));
                MesgDialog(this, tr("Erreur"), tr("Erreur lors de l'enregistrement du fichier %1\n\nPeut être n'y a t'il rien à exporter si vous n'avez pas les surface sur le solide et que vous n'exportez pas le modèle filaire,\nou peut être n'avez-vous pas les droits en écriture ?").arg(nfichier), QMessageBox::Information);
                return;
            }
        }
        else if (type_fichier == TypesFichiers::AMF)
        {
            if (type_nom_fichier == TypesFichiers::NUL) { nfichier += ".amf"; }

            if (!scene3d->ExportAMF(nfichier.toStdString().data(), parametres->R_CompressionZIPAMF()))
            {
                AjoutConsole(tr("Erreur lors de la création du fichier '%1' (droits d'accès ?).").arg(nfichier));
                MesgDialog(this, tr("Erreur"), tr("Erreur lors de l'enregistrement du fichier %1\n\nPeut être n'y a t'il rien à exporter si vous n'avez pas les surface sur le solide et que vous n'exportez pas le modèle filaire,\nou peut être n'avez-vous pas les droits en écriture ?").arg(nfichier), QMessageBox::Information);
                return;
            }
        }
        else if (type_fichier == TypesFichiers::x3DS)
        {
            if (type_nom_fichier == TypesFichiers::NUL) { nfichier += ".3ds"; }

            if (!scene3d->Export3DS(nfichier.toStdString().data()))
            {
                AjoutConsole(tr("Erreur lors de la création du fichier '%1' (droits d'accès ?).").arg(nfichier));
                MesgDialog(this, tr("Erreur"), tr("Erreur lors de l'enregistrement du fichier %1\n\nPeut être n'y a t'il rien à exporter si vous n'avez pas les surface sur le solide et que vous n'exportez pas le modèle filaire,\nou peut être n'avez-vous pas les droits en écriture ?").arg(nfichier), QMessageBox::Information);
                return;
            }
        }
        else if (type_fichier == TypesFichiers::DAE)
        {
            if (type_nom_fichier == TypesFichiers::NUL) { nfichier += ".dae"; }

            if (!scene3d->ExportCollada(nfichier.toStdString().data(), parametres->R_ExportCouleurCollada()))
            {
                AjoutConsole(tr("Erreur lors de la création du fichier '%1' (droits d'accès ?).").arg(nfichier));
                MesgDialog(this, tr("Erreur"), tr("Erreur lors de l'enregistrement du fichier %1\n\nPeut être n'y a t'il rien à exporter si vous n'avez pas les surface sur le solide et que vous n'exportez pas le modèle filaire,\nou peut être n'avez-vous pas les droits en écriture ?").arg(nfichier), QMessageBox::Information);
                return;
            }
        }
        else if (type_fichier == TypesFichiers::PLY)
        {
            if (type_nom_fichier == TypesFichiers::NUL) { nfichier += ".ply"; }

            if (!scene3d->ExportPLY(nfichier.toStdString().data()))
            {
                AjoutConsole(tr("Erreur lors de la création du fichier '%1' (droits d'accès ?).").arg(nfichier));
                MesgDialog(this, tr("Erreur"), tr("Erreur lors de l'enregistrement du fichier %1\n\nPeut être n'y a t'il rien à exporter si vous n'avez pas les surface sur le solide et que vous n'exportez pas le modèle filaire,\nou peut être n'avez-vous pas les droits en écriture ?").arg(nfichier), QMessageBox::Information);
                return;
            }
        }
        else if (type_fichier == TypesFichiers::OBJ)
        {
            if (type_nom_fichier == TypesFichiers::NUL) { nfichier += ".obj"; }

            if (!scene3d->ExportOBJ(nfichier.toStdString().data(), parametres->R_GroupeSolidesOBJ()))
            {
                AjoutConsole(tr("Erreur lors de la création du fichier '%1' (droits d'accès ?).").arg(nfichier));
                MesgDialog(this, tr("Erreur"), tr("Erreur lors de l'enregistrement du fichier %1\n\nPeut être n'y a t'il rien à exporter si vous n'avez pas les surface sur le solide et que vous n'exportez pas le modèle filaire,\nou peut être n'avez-vous pas les droits en écriture ?").arg(nfichier), QMessageBox::Information);
                return;
            }
        }
        else if (type_fichier == TypesFichiers::X3D)
        {
            if (type_nom_fichier == TypesFichiers::NUL) { nfichier += ".x3d"; }

            if (!scene3d->ExportX3D(nfichier.toStdString().data()))
            {
                AjoutConsole(tr("Erreur lors de la création du fichier '%1' (droits d'accès ?).").arg(nfichier));
                MesgDialog(this, tr("Erreur"), tr("Erreur lors de l'enregistrement du fichier %1\n\nPeut être n'y a t'il rien à exporter si vous n'avez pas les surface sur le solide et que vous n'exportez pas le modèle filaire,\nou peut être n'avez-vous pas les droits en écriture ?").arg(nfichier), QMessageBox::Information);
                return;
            }
        }
        else
        {
            MesgDialog(this, tr("Erreur"), tr("Type de fichier invalide, l'enregistrement a été annulé (%1).").arg(nfichier), QMessageBox::Information);
            return;
        }
        AjoutConsole(tr("Fichier %1 enregistré.").arg(nfichier));
    }
}

void MainWindow::EnregistreModele()
{
//    const QString &nom_plan_modele = plan_courant;

    Interface3d *interface3d_courante = Interface3DCourante();
    if (!interface3d_courante)
    {
        return;
    }
    const QString &nom_plan_modele = interface3d_courante->ProprietesPlan().fichier_plan.isEmpty() ? tr("sans_nom") : interface3d_courante->ProprietesPlan().fichier_plan;

    QString fichier3d = FichierChemin(nom_plan_modele);

    if (fichier3d.contains("-P3D", Qt::CaseInsensitive))
    {
        if (fichier3d.endsWith(".dxf", Qt::CaseInsensitive))
        {
            fichier3d.replace(QString(".dxf"), QString(""), Qt::CaseInsensitive);
        }
        else if (fichier3d.endsWith(".stl", Qt::CaseInsensitive))
        {
            fichier3d.replace(QString(".stl"), QString(""), Qt::CaseInsensitive);
        }
    }
    else
    {
        if (fichier3d.endsWith(".dxf", Qt::CaseInsensitive))
        {
            fichier3d.replace(QString(".dxf"), QString("-P3D"), Qt::CaseInsensitive);
        }
        else if (fichier3d.endsWith(".stl", Qt::CaseInsensitive))
        {
            fichier3d.replace(QString(".stl"), QString("-P3D"), Qt::CaseInsensitive);
        }
    }

    QString type_fichier;
    QString nfichier;

    const QString type_export_conf = parametres->R_TypeExport3DDefaut();

    static const QString tr_dxf = tr("Plan DXF") + " (*.dxf)";
    static const QString tr_3ds = tr("Maillage 3ds Max") + " (*.3ds)";
    static const QString tr_stl = tr("Stéréolithographie") + " (*.stl)";
    static const QString tr_obj = tr("Wavefront Object") + " (*.obj)";
    static const QString tr_dae = tr("Collada") + " (*.dae)";
    static const QString tr_amf = tr("Additive Manufacturing File") + " (*.amf)";
    static const QString tr_ply = tr("Stanford Triangle Format") + " (*.ply)";
    static const QString tr_x3d = tr("Extensible 3D") + " (*.x3d)";

#define FORGE_ARGS_EXPORT(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) arg1 + ";;" + arg2 + ";;" + arg3 + ";;" + arg4 + ";;" + arg5 + ";;" + arg6 + ";;" + arg7 + ";;" + arg8

    if (type_export_conf == "DXF")
    {
        const QString format_type = FORGE_ARGS_EXPORT(tr_dxf, tr_3ds, tr_stl, tr_amf, tr_dae, tr_obj, tr_ply, tr_x3d);
        nfichier = DialogEnregistrer(parametres->R_CheminExport() + QDir::separator() + fichier3d + ".dxf", format_type, &type_fichier);
    }
    else if (type_export_conf == "3DS")
    {
        const QString format_type = FORGE_ARGS_EXPORT(tr_3ds, tr_dxf, tr_stl, tr_amf, tr_dae, tr_obj, tr_ply, tr_x3d);
        nfichier = DialogEnregistrer(parametres->R_CheminExport() + QDir::separator() + fichier3d + ".3ds", format_type, &type_fichier);
    }
    else if (type_export_conf == "STL")
    {
        const QString format_type = FORGE_ARGS_EXPORT(tr_stl, tr_amf, tr_dxf, tr_3ds, tr_dae, tr_obj, tr_ply, tr_x3d);
        nfichier = DialogEnregistrer(parametres->R_CheminExport() + QDir::separator() + fichier3d + ".stl", format_type, &type_fichier);
    }
    else if (type_export_conf == "AMF")
    {
        const QString format_type = FORGE_ARGS_EXPORT(tr_amf, tr_stl, tr_dxf, tr_3ds, tr_dae, tr_obj, tr_ply, tr_x3d);
        nfichier = DialogEnregistrer(parametres->R_CheminExport() + QDir::separator() + fichier3d + ".amf", format_type, &type_fichier);
    }
    else if (type_export_conf == "DAE")
    {
        const QString format_type = FORGE_ARGS_EXPORT(tr_dae, tr_stl, tr_amf, tr_dxf, tr_3ds, tr_obj, tr_ply, tr_x3d);
        nfichier = DialogEnregistrer(parametres->R_CheminExport() + QDir::separator() + fichier3d + ".dae", format_type, &type_fichier);
    }
    else if (type_export_conf == "PLY")
    {
        const QString format_type = FORGE_ARGS_EXPORT(tr_ply, tr_stl, tr_amf, tr_dxf, tr_3ds, tr_dae, tr_obj, tr_x3d);
        nfichier = DialogEnregistrer(parametres->R_CheminExport() + QDir::separator() + fichier3d + ".ply", format_type, &type_fichier);
    }
    else if (type_export_conf == "OBJ")
    {
        const QString format_type = FORGE_ARGS_EXPORT(tr_obj, tr_stl, tr_amf, tr_dxf, tr_3ds, tr_dae, tr_ply, tr_x3d);
        nfichier = DialogEnregistrer(parametres->R_CheminExport() + QDir::separator() + fichier3d + ".obj", format_type, &type_fichier);
    }
    else if (type_export_conf == "X3D")
    {
        const QString format_type = FORGE_ARGS_EXPORT(tr_x3d, tr_stl, tr_amf, tr_dxf, tr_3ds, tr_dae, tr_obj, tr_ply);
        nfichier = DialogEnregistrer(parametres->R_CheminExport() + QDir::separator() + fichier3d + ".x3d", format_type, &type_fichier);
    }
    else /* Impossible en principe... y aurait t'il un bug ?! */
    {
#ifdef DEBUG
        qDebug() << "EnregistreModele() :: Erreur de type inconnu : " << type_export_conf;
#endif
    }

#undef FORGE_ARGS_EXPORT

    if (!nfichier.isEmpty())
    {
        EnregistreModele(nfichier, TypeFichier(type_fichier));
    }
}

void MainWindow::EnregistreModele_2D()
{
    Interface3d *interface3d_courante = Interface3DCourante();
    if (!interface3d_courante)
    {
        return;
    }
    const QString &nom_plan_modele = interface3d_courante->ProprietesPlan().fichier_plan.isEmpty() ? tr("sans_nom") : interface3d_courante->ProprietesPlan().fichier_plan;

    QString fichier3d = FichierChemin(nom_plan_modele);

    if (fichier3d.contains("-P3D", Qt::CaseInsensitive))
    {
        if (fichier3d.endsWith(".dxf", Qt::CaseInsensitive))
        {
            fichier3d.replace(QString(".dxf"), QString(""), Qt::CaseInsensitive);
        }
        else if (fichier3d.endsWith(".stl", Qt::CaseInsensitive))
        {
            fichier3d.replace(QString(".stl"), QString(""), Qt::CaseInsensitive);
        }
    }
    else
    {
        if (fichier3d.endsWith(".dxf", Qt::CaseInsensitive))
        {
            fichier3d.replace(QString(".dxf"), QString("-P3D"), Qt::CaseInsensitive);
        }
        else if (fichier3d.endsWith(".stl", Qt::CaseInsensitive))
        {
            fichier3d.replace(QString(".stl"), QString("-P3D"), Qt::CaseInsensitive);
        }
    }

    QString type_fichier;
    QString nfichier = DialogEnregistrer(parametres->R_CheminExport() + QDir::separator() + fichier3d + ".dxf", tr("Plan DXF 2D") + " (*.dxf)", &type_fichier);

    if (!nfichier.isEmpty())
    {
        EnregistreModele(nfichier, TypesFichiers::DXF_2D);
    }
}

void MainWindow::GenPatron()
{
    Perspective3D::Perspective *persp_courant = PerspectiveCourantNonConst();
    if (!persp_courant) { return; }

    bool valide = persp_courant->GenPatron(false);

    if (valide)
    {
        Th_Perspective_n *inst_perspective = new Th_Perspective_n(persp_courant);
        inst_perspective->start();
        NouvelleVue3D(inst_perspective, true, false);
    }
    else // Erreur
    {
        MesgDialog(this, tr("Erreur"), tr("Erreur lors de la génération du patron"), QMessageBox::Information);
    }
}

/* *** */

void MainWindow::on_actionFermer_le_plan_triggered()
{
    on_tabWidget_tabCloseRequested(ui->tabWidget->currentIndex());
//    GereIconesOutils2D();
}

void MainWindow::on_action_MAJ_plan_triggered()
/* L'utilisateur demande la mise à jour du dessin... */
{
    if (!Contexte2D()) { return; }

    if (!plan_courant.isEmpty())
    {
        QString copie_plan_courant = plan_courant; /* Avant fermeture du plan... */
        Ferme_plan();
        Ouvrir(copie_plan_courant, false);
//        TypesFichiers type = Ouvrir(copie_plan_courant, false);
//        if (type == TypesFichiers::DXF_3D && parametres->R_RegenModeles_MAJ())
//        {
//            FermeVues3D(true);
//            ExecGenerationModele(false);
//        }
    }
    else // Le logiciel n'a ouvert aucun plan dans cette session, on récupère le plan précédent.
    {
        QString fichier_precedent = parametres->R_Fichier_Precedent();
        if (!fichier_precedent.isEmpty())
        {
            Ouvrir(fichier_precedent, false, true);
        }
        else
        {
            AjoutConsole(tr("Aucun fichier de plan ouvert..."));
        }
    }
    GereIconesOutils2D();
}

void MainWindow::on_actionExtrusion_triggered()
{
//    on_actionSupprimer_les_origines_triggered();
    on_actionSupprimer_les_vues_triggered();
    scene2D->DessineRect(VUE_EXTRUSION);
    AjoutConsole(tr("Extrusion: Définir la vue de face..."));
    GereIconesOutils2D();
}

void MainWindow::on_actionRevolution_triggered()
/* Demande une surface de révolution */
{
//    on_actionSupprimer_les_origines_triggered();
    on_actionSupprimer_les_vues_triggered();
    scene2D->DessineRect(VUE_REVOLUTION);
    AjoutConsole(tr("Révolution: Définir la vue de profil..."));
    GereIconesOutils2D();
}

void MainWindow::on_actionVues_automatiques_triggered()
/* Mode vues automatiques. */
{
    if (!Contexte2D())
    {
        on_actionVue_oblique_3d_triggered();
    }
    else
    {
        on_actionSupprimer_les_vues_triggered();
        scene2D->DessineRect(Perspective3D::vues2D_t::VUEMULT);
        AjoutConsole(tr("Vues automatiques. Tracez un cadre englobant l'ensemble du plan."));
    }
}

void MainWindow::on_actionVue_Face_triggered()
{
    if (!Contexte2D())
    {
        on_actionVue_de_face_3d_triggered();
    }
    else
    {
        scene2D->ReinitOrigine(Perspective3D::vues2D_t::VUEFACE);
        ui->actionOrigine_face->setChecked(false);
        scene2D->DessineRect(Perspective3D::vues2D_t::VUEFACE);
        AjoutConsole(tr("Définir la vue de face..."));
        GereIconesOutils2D();
    }
}

void MainWindow::on_actionVue_cote_triggered()
{
    if (!Contexte2D())
    {
        on_actionVue_de_cote_3d_triggered();
    }
    else
    {
        scene2D->ReinitOrigine(Perspective3D::vues2D_t::VUECOTE);
        ui->actionOrigine_cote->setChecked(false);
        scene2D->DessineRect(Perspective3D::vues2D_t::VUECOTE);
        AjoutConsole(tr("Définir la vue de côté..."));
        GereIconesOutils2D();
    }
}

void MainWindow::on_actionVue_haute_triggered()
{
    if (!Contexte2D())
    {
        on_actionVue_de_dessus_3d_triggered();
    }
    else
    {
        scene2D->ReinitOrigine(Perspective3D::vues2D_t::VUEHAUT);
        ui->actionOrigine_haute->setChecked(false);
        scene2D->DessineRect(Perspective3D::vues2D_t::VUEHAUT);
        AjoutConsole(tr("Définir la vue haute..."));
        GereIconesOutils2D();
    }
}

void MainWindow::SupprimeVue(Perspective3D::vues2D_t vue)
{
    if (!Contexte2D()) { return; }
    if (vue & Perspective3D::vues2D_t::VUEFACE)
    {
        scene2D->ReinitRect(Perspective3D::vues2D_t::VUEFACE);
        parametres->SupprRectSelection(plan_courant, "vface");
        ui->actionVue_Face->setChecked(false);
    }
    if (vue & Perspective3D::vues2D_t::VUECOTE)
    {
        scene2D->ReinitRect(Perspective3D::vues2D_t::VUECOTE);
        parametres->SupprRectSelection(plan_courant, "vcote");
        ui->actionVue_cote->setChecked(false);
    }
    if (vue & Perspective3D::vues2D_t::VUEHAUT)
    {
        scene2D->ReinitRect(Perspective3D::vues2D_t::VUEHAUT);
        parametres->SupprRectSelection(plan_courant, "vhaut");
        ui->actionVue_haute->setChecked(false);
    }
    if (vue & Perspective3D::vues2D_t::VUEMULT)
    {
        scene2D->ReinitRect(Perspective3D::vues2D_t::VUEMULT);
    }
    GereIconesOutils2D();
}

void MainWindow::on_actionSupprimer_les_vues_triggered()
{
    if (!Contexte2D()) { return; }
    on_actionSupprimer_les_origines_triggered();
    SupprimeVue(Perspective3D::vues2D_t::VUEFACE | Perspective3D::vues2D_t::VUECOTE | Perspective3D::vues2D_t::VUEHAUT | Perspective3D::vues2D_t::VUEMULT);
    AjoutConsole(tr("Supprime les vues."));
    GereIconesOutils2D();
}

void MainWindow::on_actionOrigine_face_triggered()
{
    if (!Contexte2D()) { return; }
    if (scene2D->RectVueDefini(Perspective3D::vues2D_t::VUEFACE))
    {
        AjoutConsole(tr("Définir l'origine de la vue de face..."));
        scene2D->DefOrigine(Perspective3D::vues2D_t::VUEFACE);
    }
    else
    {
        MesgDialog(this, tr("Information"), tr("Il faut définir la vue sur le plan avant de spécifier l'origine."), QMessageBox::Information);
    }
    GereIconesOutils2D();
}

void MainWindow::on_actionOrigine_cote_triggered()
{
    if (!Contexte2D()) { return; }
    if (scene2D->RectVueDefini(Perspective3D::vues2D_t::VUECOTE))
    {
        AjoutConsole(tr("Définir l'origine de la vue de côté..."));
        scene2D->DefOrigine(Perspective3D::vues2D_t::VUECOTE);
    }
    else
    {
        MesgDialog(this, tr("Information"), tr("Il faut définir la vue sur le plan avant de spécifier l'origine."), QMessageBox::Information);
    }
    GereIconesOutils2D();
}

void MainWindow::on_actionOrigine_haute_triggered()
{
    if (!Contexte2D()) { return; }
    if (scene2D->RectVueDefini(Perspective3D::vues2D_t::VUEHAUT))
    {
        AjoutConsole(tr("Définir l'origine de la vue haute..."));
        scene2D->DefOrigine(Perspective3D::vues2D_t::VUEHAUT);
    }
    else
    {
        MesgDialog(this, tr("Information"), tr("Il faut définir la vue sur le plan avant de spécifier l'origine."), QMessageBox::Information);
    }
    GereIconesOutils2D();
}

void MainWindow::on_actionSupprimer_les_origines_triggered()
{
    if (!Contexte2D()) { return; }
    SupprimeOrigine(Perspective3D::vues2D_t::VUEFACE | Perspective3D::vues2D_t::VUECOTE | Perspective3D::vues2D_t::VUEHAUT | Perspective3D::vues2D_t::VUENUL);
    AjoutConsole(tr("Origines supprimées"));
    GereIconesOutils2D();
}

const std::string& MainWindow::ChargeShaderInterface3D(Interface3d *interface3d, unsigned int contexte)
/* Charge les shaders dans une interface 3D (sélection en fonction du contexte). Renvoi le contenu du rapport de compilation (vide si tout s'est bien passé). */
{
//    if (!parametres->R_ActiveGLSL())
//        return false;

    const unsigned int contexte_param = (contexte == PGL::CONTEXTE_POST) ? CONTEXTE_GLSL_POST : CONTEXTE_GLSL_MODELE;
    const QByteArray vertex_shader = parametres->R_VertexShader_Courant(contexte_param);
    const QByteArray pixels_shader = parametres->R_PixelShader_Courant(contexte_param);

//    const bool contexte_post = contexte & PGL::CONTEXTE_POST;
//    qDebug() << "Ajout du vertex shader (contexte post = " << contexte_post << ") : " << vertex_shader;
//    qDebug() << "Ajout du pixel shader (contexte post = " << contexte_post << ") : " << pixels_shader;

    bool valide_taille_min_vs = vertex_shader.size() > 3;
    bool valide_taille_min_ps = pixels_shader.size() > 3;

    if (valide_taille_min_vs && valide_taille_min_ps)
    {
        return interface3d->InitialisationShaders(vertex_shader.data(), pixels_shader.data(), contexte);
    }
    else if (valide_taille_min_vs)
    {
        return interface3d->InitialisationShaders(vertex_shader.data(), 0, contexte);
    }
    else if (valide_taille_min_ps)
    {
        return interface3d->InitialisationShaders(0, pixels_shader.data(), contexte);
    }
    return interface3d->InitialisationShaders(0, 0, contexte);
}

bool MainWindow::AppliqueShadersInterface3D(Interface3d *interface3d, bool modele, bool post)
{
    bool v1 = false;
    bool v2 = false;

//    interface3d->AttendInitialisationGL();
    interface3d->defEtatShaders(true);

    if (modele)
    {
        const std::string &journal_shaders_modele = ChargeShaderInterface3D(interface3d, PGL::CONTEXTE_MODELE);
        if (journal_shaders_modele.size() > 1)
        {
            if (parametres->isVisible())
                parametres->close();
            const QString qstr_log = QString::fromStdString(journal_shaders_modele);
            AjoutConsole(qstr_log, true);
            MesgDialog(0, tr("Erreur GLSL"), tr("Erreur de compilation du programme GLSL du solide:\n") + qstr_log, QMessageBox::Information, false, false);
#ifdef DEBUG
            qDebug() << "Erreur d'initialisation des shaders ?!";
#endif
        }
        else
        {
            v1 = true;
        }
    }
    else
    {
        v1 = true;
    }

    if (post)
    {
        const std::string &journal_shaders_post = ChargeShaderInterface3D(interface3d, PGL::CONTEXTE_POST);
        if (journal_shaders_post.size() > 1)
        {
            if (parametres->isVisible())
                parametres->close();
            const QString qstr_log = QString::fromStdString(journal_shaders_post);
            AjoutConsole(qstr_log, true);
            MesgDialog(0, tr("Erreur GLSL"), tr("Erreur de compilation du programme GLSL de post-traitement:\n") + qstr_log, QMessageBox::Information, false, false);
#ifdef DEBUG
            qDebug() << "Erreur d'initialisation des shaders (post) ?!";
#endif
        }
        else
        {
            v2 = true;
            interface3d->defTempoShaderPost(parametres->R_TempoShadersPost());
        }
    }
    else
    {
        v2 = true;
    }

    return v1 && v2;
}

void MainWindow::NouvelleVue3D(Th_Perspective *th_p, bool mode_2d, bool supprime_vues)
/* Ouvre et paramètre un onglet de vue 3D pour le fil d'exécution Perspective3D qui vient d'être lancé. */
{
    Perspective3D::Perspective *perspective = th_p->PerspectiveNonConst();

    if (!perspective)
    {
        return;
    }

    if (supprime_vues)
    {
        on_actionSupprimer_les_vues_triggered(); /* Si les vues 2D doivent être supprimées, ça doit être avant de passer sur un onglet 3D. */
    }

    bool vue_face = false;
    bool vue_cote = false;
    bool vue_haut = false;
    bool vue_extrusion = false;
    bool vue_revolution = false;

    Perspective3D::vues2D_t vue_reference = Perspective3D::vues2D_t::VUEMULT;

    if (scene2D->RectVueDefini(Perspective3D::vues2D_t::VUEFACE))
    {
        if (VUE_EXTRUSION == Perspective3D::vues2D_t::VUEFACE)
        {
            vue_extrusion = true;
        }
        else if (VUE_REVOLUTION == Perspective3D::vues2D_t::VUEFACE)
        {
            vue_revolution = true;
        }
        vue_face = true;
    }
    if (scene2D->RectVueDefini(Perspective3D::vues2D_t::VUECOTE))
    {
        if (VUE_EXTRUSION == Perspective3D::vues2D_t::VUECOTE)
        {
            vue_extrusion = true;
        }
        else if (VUE_REVOLUTION == Perspective3D::vues2D_t::VUECOTE)
        {
            vue_revolution = true;
        }
        vue_cote = true;
    }
    if (scene2D->RectVueDefini(Perspective3D::vues2D_t::VUEHAUT))
    {
        if (VUE_EXTRUSION == Perspective3D::vues2D_t::VUEHAUT)
        {
            vue_extrusion = true;
        }
        else if (VUE_REVOLUTION == Perspective3D::vues2D_t::VUEHAUT)
        {
            vue_revolution = true;
        }
        vue_haut = true;
    }

    if (vue_face && vue_cote && vue_haut)
    {
        vue_reference = Perspective3D::vues2D_t::VUEMULT;
    }
    else if (vue_extrusion)
    {
        vue_reference = VUE_EXTRUSION;
    }
    else if (vue_revolution)
    {
        vue_reference = VUE_REVOLUTION;
    }

    PGL::Params_GL params_gl;
    PGL::Params_Perspective_GL params_pgl;

    parametres->AssigneParametresGL(params_gl);
    parametres->AssigneParametresPerspectiveGL(params_pgl, *perspective, mode_2d);

    Modele3DInst modele_affichage(th_p);

    Interface3d *interface3d = new Interface3d(animation, modele_affichage, ui->tabWidget->count(), params_gl, params_pgl, id_feuille_style, parametres->R_TypeAffichage3D(),
                                               (!parametres->R_InterfacePetitEcran() && !parametres->R_InterfacePanoramique()), parametres->R_AfficheAccrochage3D(),
                                               parametres->R_AfficheProprietes3D(),
                                               vue_reference);

//    interface3d->AttendInitialisationGL();

    connect(interface3d, SIGNAL(ImprimeModele()), this, SLOT(ImprimeModele()));
    connect(interface3d, SIGNAL(ExportImageModele()), this, SLOT(ExportImageModele()));
#ifdef SUPPORT_GIF
    connect(interface3d, SIGNAL(EnregistreAnimationModele()), this, SLOT(EnregistreAnimationModele()));
#endif // SUPPORT_GIF
    connect(interface3d, SIGNAL(ExportModele()), this, SLOT(EnregistreModele()));
    connect(interface3d, SIGNAL(ExportModele_2D()), this, SLOT(EnregistreModele_2D()));
    connect(interface3d, SIGNAL(ExportScript(const ProprietesPlan_t &, const Perspective3D::Perspective &)), this, SLOT(EnregistreScript(const ProprietesPlan_t &, const Perspective3D::Perspective &)));
    connect(interface3d, SIGNAL(GenPatron()), this, SLOT(GenPatron()));
    connect(interface3d, SIGNAL(ConfigAffichage(int)), this, SLOT(AfficheConfigAffichage3D(int)));

    ProprietesPlan_t &proprietes_plan = interface3d->ProprietesPlanNonConst();
    proprietes_plan.ignore = false;
    proprietes_plan.id_ral = perspective->Parametres().Id_ral;
    proprietes_plan.type_affichage = parametres->R_TypeAffichage3D();

    if (!plan_courant.isEmpty())
    {
        proprietes_plan.fichier_plan = plan_courant;
    }
    if (vue_face)
    {
        proprietes_plan.rectFace = scene2D->RectVueConst(Perspective3D::vues2D_t::VUEFACE)->rect();
    }
    if (vue_cote)
    {
        proprietes_plan.rectCote = scene2D->RectVueConst(Perspective3D::vues2D_t::VUECOTE)->rect();
    }
    if (vue_haut)
    {
        proprietes_plan.rectHaut = scene2D->RectVueConst(Perspective3D::vues2D_t::VUEHAUT)->rect();
    }
    if (!scene2D->OrigineFace().isNull())
    {
        proprietes_plan.origineFace = scene2D->OrigineFace();
    }
    if (!scene2D->OrigineCote().isNull())
    {
        proprietes_plan.origineCote = scene2D->OrigineCote();
    }
    if (!scene2D->OrigineHaut().isNull())
    {
        proprietes_plan.origineHaut = scene2D->OrigineHaut();
    }
    if (!CompareE(th_p->ParamGen1(), 0.))
    {
        proprietes_plan.param_gen1 = th_p->ParamGen1();
    }
    proprietes_plan.vue_reference = vue_reference;

    ListeVues3D->push_back(interface3d);

    connect(interface3d, SIGNAL(FinGeneration(int)), this, SLOT(FinGeneration(int)));

    QString nom_onglet;

    if (!perspective->LicenceActive())
    {
        nom_onglet += tr("(Demo)") + " ";
    }

    if (mode_2d)
    {
        nom_onglet += tr("Développé") + ":" + QString::number(ui->tabWidget->count());
    }
    else
    {
        nom_onglet += tr("3D") + ":" + QString::number(ui->tabWidget->count());
    }

    ui->tabWidget->addTab(interface3d,  nom_onglet);

    if (mode_2d)
    {
        ui->tabWidget->setTabIcon(ui->tabWidget->count()-1, QIcon(":/icones/patron.png"));
    }
    else
    {
        ui->tabWidget->setTabIcon(ui->tabWidget->count()-1, QIcon(":/icones/cube_perspective.png"));
    }

    if (ui->tabWidget->count())
    {
        ui->tabWidget->setCurrentIndex(ui->tabWidget->count()-1); /* On passe sur l'onglet de la vue 3D. */
    }

    if (params_gl.active_shaders)
    {
        AppliqueShadersInterface3D(interface3d, params_gl.active_shaders, params_gl.active_fbo);
    }

    cmb_type_affichage_widget_onglets->setCurrentIndex(proprietes_plan.type_affichage);

#ifdef AFFICHE_SELECTION_COULEUR_SOLIDE_PANORAMIQUE
    const puint index_ral = Perspective3D::RAL::RechercheIndex(proprietes_plan.id_ral);

    if (int(index_ral) < cmb_couleurs_scenes_widget_onglets->count())
    {
        cmb_couleurs_scenes_widget_onglets->setCurrentIndex(index_ral);
    }
#endif // AFFICHE_SELECTION_COULEUR_SOLIDE_PANORAMIQUE
}

void MainWindow::NouvelleVue3D(Th_ImportSTL3D *th_import, const QString &fichier_plan)
/* Ouvre et paramètre un onglet de vue 3D. */
{
    PGL::Params_GL params_gl;
    PGL::Params_Perspective_GL params_pgl;

    parametres->AssigneParametresGL(params_gl);
    parametres->AssigneParametresPerspectiveGL(params_pgl);

    Modele3DInst modele_affichage(th_import);

    Interface3d *interface3d = new Interface3d(animation, modele_affichage, ui->tabWidget->count(), params_gl, params_pgl, id_feuille_style, parametres->R_TypeAffichage3D(),
                                               (!parametres->R_InterfacePetitEcran() && !parametres->R_InterfacePanoramique()),
                                               parametres->R_AfficheAccrochage3D(), parametres->R_AfficheProprietes3D(),
                                               Perspective3D::vues2D_t::VUEMULT);

//    interface3d->AttendInitialisationGL();

    connect(interface3d, SIGNAL(ImprimeModele()), this, SLOT(ImprimeModele()));
    connect(interface3d, SIGNAL(ExportImageModele()), this, SLOT(ExportImageModele()));
#ifdef SUPPORT_GIF
    connect(interface3d, SIGNAL(EnregistreAnimationModele()), this, SLOT(EnregistreAnimationModele()));
#endif // SUPPORT_GIF
    connect(interface3d, SIGNAL(ExportModele()), this, SLOT(EnregistreModele()));
    connect(interface3d, SIGNAL(ExportModele_2D()), this, SLOT(EnregistreModele_2D()));
//    connect(interface3d, SIGNAL(ExportScript(const ProprietesPlan_t &, const Perspective3D::Perspective &)), this, SLOT(EnregistreScript(const ProprietesPlan_t &, const Perspective3D::Perspective &)));
    connect(interface3d, SIGNAL(GenPatron()), this, SLOT(GenPatron()));
    connect(interface3d, SIGNAL(ConfigAffichage(int)), this, SLOT(AfficheConfigAffichage3D(int)));

    ProprietesPlan_t &proprietes_plan = interface3d->ProprietesPlanNonConst();
    proprietes_plan.ignore = true;
//    plan_courant = fichier_plan;
    proprietes_plan.fichier_plan = fichier_plan;
    proprietes_plan.id_ral = th_import->Scene3D()->CouleurRAL();
    proprietes_plan.type_affichage = parametres->R_TypeAffichage3D();

    ListeVues3D->push_back(interface3d);

    connect(interface3d, SIGNAL(FinGeneration(int)), this, SLOT(FinGeneration(int)));

    QString nom_onglet = tr("3D") + ":" + QString::number(ui->tabWidget->count());

    ui->tabWidget->addTab(interface3d,  nom_onglet);
    ui->tabWidget->setTabIcon(ui->tabWidget->count()-1, QIcon(":/icones/cube_perspective.png"));

    if (ui->tabWidget->count())
    {
        ui->tabWidget->setCurrentIndex(ui->tabWidget->count()-1); /* On passe sur l'onglet de la vue 3D. */
    }

    if (params_gl.active_shaders)
    {
        AppliqueShadersInterface3D(interface3d, params_gl.active_shaders, params_gl.active_fbo);
    }

    cmb_type_affichage_widget_onglets->setCurrentIndex(proprietes_plan.type_affichage);

#ifdef AFFICHE_SELECTION_COULEUR_SOLIDE_PANORAMIQUE
    const puint index_ral = Perspective3D::RAL::RechercheIndex(proprietes_plan.id_ral);

    if (int(index_ral) < cmb_couleurs_scenes_widget_onglets->count())
    {
        cmb_couleurs_scenes_widget_onglets->setCurrentIndex(index_ral);
    }
#endif // AFFICHE_SELECTION_COULEUR_SOLIDE_PANORAMIQUE
}

bool MainWindow::Generation3D(Perspective3D::ParametresPerspective &parametres_p3d, qreal param1, bool supprime_vues)
/* Envoi la génération avec l'état actuel de l'application et les paramètres pour Perspective3D donnés en argument
    (les paramètres concernant les origines seront écrasés).
    Le paramètre supplémentaire est lié au mode Extrusion ou Révolution
        (optionnel, si il reste à sa valeur par défaut, on utilise les paramètres de la configuration).
    Si supprime_vues vaut true, les vues sur le plan 2D seront supprimées avant la génération. */
{
    const bool plan_ouvert = plan_courant.isEmpty();

    if (!Contexte2D())
    {
        return false;
    }

    const QGraphicsRectItem *rectface_ptr = scene2D->RectVueConst(Perspective3D::vues2D_t::VUEFACE);
    const QGraphicsRectItem *rectcote_ptr = scene2D->RectVueConst(Perspective3D::vues2D_t::VUECOTE);
    const QGraphicsRectItem *recthaut_ptr = scene2D->RectVueConst(Perspective3D::vues2D_t::VUEHAUT);

    int score = 0;
    if (scene2D->ValideElement(rectface_ptr)) { ++score; }
    if (scene2D->ValideElement(recthaut_ptr)) { ++score; }
    if (scene2D->ValideElement(rectcote_ptr)) { ++score; }

    parametres_p3d.Licence = parametres->R_CleeActivation();
    parametres_p3d.SalageLicence = parametres->R_SalageActivation();

    if (score == 3) // 3 vues définies
    {
        /* Envoi les entités 2D dans Perspective 2D. */

        Perspective3D::Perspective *perspective = Perspective3D::Perspective::Construct(parametres_p3d);

        int compteur_ents = 0;
        compteur_ents += EnvoiElementsPerspective3D(perspective, Perspective3D::vues2D_t::VUEFACE, rectface_ptr);
        compteur_ents += EnvoiElementsPerspective3D(perspective, Perspective3D::vues2D_t::VUECOTE, rectcote_ptr);
        compteur_ents += EnvoiElementsPerspective3D(perspective, Perspective3D::vues2D_t::VUEHAUT, recthaut_ptr);
        if (compteur_ents == 0)
        {
            AjoutConsole(tr("Erreur") + " : " + tr("Aucun éléments dans les vues."));
            return false;
        }

        /* Envoi des origines */
        QPointF origineface = plan_ouvert ? parametres->R_PointOrigine(plan_courant, "oface") : scene2D->OrigineFace();
        QPointF originecote = plan_ouvert ? parametres->R_PointOrigine(plan_courant, "ocote") : scene2D->OrigineCote();
        QPointF originehaut = plan_ouvert ? parametres->R_PointOrigine(plan_courant, "ohaut") : scene2D->OrigineHaut();

        perspective->defOrigineFace(Perspective3D::Ppoint2D(origineface.x(), -origineface.y()));
        perspective->defOrigineCote(Perspective3D::Ppoint2D(originecote.x(), -originecote.y()));
        perspective->defOrigineHaut(Perspective3D::Ppoint2D(originehaut.x(), -originehaut.y()));

        Th_Perspective *th_perspective = new Th_Perspective(perspective);
        th_perspective->start();

        if (th_perspective->ErreurExec())
        {
            QMessageBox::critical(this, tr("Erreur"), tr("La création du fil d'exécution semble avoir échoué. Peut être n'avez vous plus de ressources ?"));
            th_perspective->exit(-1);
            delete th_perspective;
            delete perspective;
            return false;
        }

        NouvelleVue3D(th_perspective, false, supprime_vues);
    }
    else if (score == 1)
    {
        const QGraphicsRectItem *rectextrusion_ptr = scene2D->RectVueConst(VUE_EXTRUSION);
        const QGraphicsRectItem *rectrevolution_ptr = scene2D->RectVueConst(VUE_REVOLUTION);

        if (scene2D->ValideElement(rectextrusion_ptr)) // Extrusion simple.
        {
            QPointF origine;
            double ht_extrusion = 0.;

            if (param1 > 0.) /* Le paramètre est prédéfini, en principe par le biais d'un script. */
            {
                if (automate.AutomateConst().AnimationScript() && parametres->R_AutoriseAnimationScripts()) /* Si on autorise les animations, on affiche la boite de dialogue, qui sera fermée par l'animation. */
                {
                    DialogDouble question_double(tr("Extrusion"), tr("Hauteur d'extrusion ?"), param1, 0., 100000., &animation, true, true, this);
                    if (!question_double.Accepte())
                    {
                        return false;
                    }
                    ht_extrusion = question_double.Reponse();
                }
                else /* Si pas d'animation, on valide directement. */
                {
                    ht_extrusion = param1;
                }
            }
            else if (parametres->R_ExtrusionFixe())
            {
                ht_extrusion = parametres->R_HauteurExtrusion();
            }
            else
            {
                DialogDouble question_double(tr("Extrusion"), tr("Hauteur d'extrusion ?"), parametres->R_HauteurExtrusion(), 0., 100000., &animation, false, true, this);
                if (!question_double.Accepte())
                {
                    return false;
                }
                ht_extrusion = question_double.Reponse();
                parametres->defHauteurExtrusion(ht_extrusion);
            }

            Perspective3D::Perspective *extrude = Perspective3D::Perspective::Construct(parametres_p3d);

            int compteur_ents = EnvoiElementsPerspective3D(extrude, VUE_EXTRUSION, rectextrusion_ptr);
            if (compteur_ents == 0)
            {
                AjoutConsole(tr("Erreur") + " : " + tr("Aucun éléments dans les vues."));
                return false;
            }

            origine = plan_ouvert ? parametres->R_PointOrigine(plan_courant, "oface") : scene2D->OrigineFace();
            extrude->defOrigineVue(Perspective3D::Ppoint2D(origine.x(), -origine.y()), VUE_EXTRUSION);

            Th_Extrudeur *th_extrude = new Th_Extrudeur(extrude, ht_extrusion);
            th_extrude->start();

            if (th_extrude->ErreurExec())
            {
                QMessageBox::critical(this, tr("Erreur"), tr("La création du fil d'exécution semble avoir échoué. Peut être n'avez vous plus de ressources ?"));
                th_extrude->exit(-1);
                delete th_extrude;
                delete extrude;
                return false;
            }

            NouvelleVue3D(th_extrude, false, supprime_vues);
        }
        else if (scene2D->ValideElement(rectrevolution_ptr)) // Révolution
        {
            QPointF origine;
            double angle_revol = 0.;

            if (param1 > 0.)
            {
                if (automate.AutomateConst().AnimationScript() && parametres->R_AutoriseAnimationScripts()) /* Si on autorise les animations, on affiche la boite de dialogue, qui sera fermée par l'animation. */
                {
                    DialogDouble question_double(tr("Révolution"), tr("Angle de révolution ?"), param1, 0., 360., &animation, true, true, this);
                    if (!question_double.Accepte())
                    {
                        return false;
                    }
                    angle_revol = question_double.Reponse();
                }
                else /* Si pas d'animation, on valide directement. */
                {
                    angle_revol  = param1;
                }
            }
            else if (parametres->R_RevolutionFixe())
            {
                angle_revol = parametres->R_AngleRevolution();
            }
            else
            {
                DialogDouble question_double(tr("Révolution"), tr("Angle de révolution ?"), parametres->R_AngleRevolution(), 0., 360., &animation, false, true, this);
                if (!question_double.Accepte())
                {
                    return false;
                }
                angle_revol = question_double.Reponse();
                parametres->defAngleRevolution(angle_revol);
            }

            Perspective3D::Perspective *revol = Perspective3D::Perspective::Construct(parametres_p3d);

            int compteur_ents = EnvoiElementsPerspective3D(revol, VUE_REVOLUTION, rectrevolution_ptr);
            if (compteur_ents == 0)
            {
                AjoutConsole(tr("Erreur") + " : " + tr("Aucun éléments dans les vues."));
                return false;
            }

            origine = plan_ouvert ? parametres->R_PointOrigine(plan_courant, "ocote") : scene2D->OrigineCote();
            revol->defOrigineVue(Perspective3D::Ppoint2D(origine.x(), -origine.y()), VUE_REVOLUTION);

            Th_Revolution *th_revol = new Th_Revolution(revol, angle_revol);
            th_revol->start();

            if (th_revol->ErreurExec())
            {
                QMessageBox::critical(this, tr("Erreur"), tr("La création du fil d'exécution semble avoir échoué. Peut être n'avez vous plus de ressources ?"));
                th_revol->exit(-1);
                delete th_revol;
                delete revol;
                return false;
            }

            NouvelleVue3D(th_revol, false, supprime_vues);
        }
        else
        {
            AjoutConsole(tr("Erreur") + " : " + tr("Une seule vue est définieet ne permet pas de générer un solide."));
            MesgDialog(this, tr("Erreur"), tr("Une seule vue est définie et ne permet pas de générer un solide."), QMessageBox::Information);
            return false;
        }
    }
    else // 0 ou 2 vues définies
    {
        AjoutConsole(tr("Erreur") + " : " + tr("Il faut définir les vues sur le plan 2D avant de générer la représentation 3D."));
        MesgDialog(this, tr("Erreur"), tr("Il faut définir les vues sur le plan 2D avant de générer la représentation 3D."), QMessageBox::Information);
        return false;
    }

    AjoutConsole(tr("Génération d'un nouveau modèle..."));
    if (OngletFixe)
    {
        ui->tabWidget->setCurrentIndex(OngletFixe);
    }
    return true;
}

void MainWindow::EnregistreScript(const ProprietesPlan_t &proprietes_plan, const Perspective3D::Perspective &perspective)
{
    if (proprietes_plan.ignore)
    {
        return;
    }
    if (proprietes_plan.fichier_plan.isEmpty())
    {
        return;
    }

    QDir chemin_script = proprietes_plan.fichier_plan;
    chemin_script.cdUp();

    QString fichier_plan = FichierChemin(proprietes_plan.fichier_plan);

    QString ext_fichier_script = fichier_plan;
    ext_fichier_script.replace(QString(".dxf"), QString(".dxf." ExtensionScriptsAutomate));

    bool ecriture_fichier = false;
    bool mode_ajout = false;

    QString chemin_script_f; /* Contiendra le chemin vers le fichier */

//    while (true)
//    {
        DialogQString mesg(tr("Enregistrement d'un script"), tr("Entrez un nom de fichier pour le script.\nNotez que le script sera enregistré dans le répertoire du fichier de plan auquel il est attaché."), parametres->R_NomDernierScript(), this);

        if (mesg.Accepte())
        {
            chemin_script_f = chemin_script.absolutePath() + QDir::separator() + mesg.Reponse() + "." + ext_fichier_script;
            if (!mesg.Reponse().isEmpty())
            {
                parametres->defNomDernierScript(mesg.Reponse());
            }
        }
        else
        {
//            ecriture_fichier = false;
//            break;
            return;
        }

        if (QFile::exists(chemin_script_f))
        {
            int reponse = MesgDialog(this, tr("Enregistrement d'un script"), tr("Le fichier de script \"%1\" est déjà existant.\nVoulez-vous le conserver et y ajouter le contenu du nouveau script ?\nSi vous refusez, l'ancien script sera écrasé.").arg(chemin_script_f), QMessageBox::Question, true);
            if (reponse > 0) /* Oui */
            {
                ecriture_fichier = true;
                mode_ajout = true;
//                break;
            }
            else if (reponse == 0) /* Non */
            {
                ecriture_fichier = true;
                mode_ajout = false;
//                break;
            }
            else /* Annule */
            {
                ecriture_fichier = false;
                mode_ajout = false;
//                break;
            }
        }
        else
        {
            ecriture_fichier = true;
            mode_ajout = false;
//            break;
        }
//    }

    if (ecriture_fichier)
    {

        const int langue_script = ConvLangueP3DAutomate(Perspective3D::i18n::Langue());
        ScriptAutomate script(chemin_script_f, langue_script, false, mode_ajout);

#ifdef DEBUG
        qDebug() << "Enregistre le script :: " << chemin_script_f << ". Valide=" << script.Valide();
#endif

//        if (!mode_ajout)
//        {
//            script.AjoutInclusionFichier("stdlib.z6s");
//        }

        QString fct_script = QString("gen") + QString::number(script.IdRevisionScript());

//        script.AjoutBloc(QString("conf") + id_script);
//        script.AjoutConfig(params_p3d);
//        script.AjoutClotureBloc();

        if (!script.AjoutBloc(fct_script))
        {
#ifdef DEBUG
            qDebug() << "Erreur lors de l'ajout du bloc !";
#endif // DEBUG
        }
        else
        {
#ifdef DEBUG
//            qDebug() << "Ajout du bloc : " << fct_script;
#endif // DEBUG
        }

        script.AjoutOuvrirFichier(fichier_plan);

        script.AjoutAffichageOnglet2D();

        if (!mode_ajout) /* On ne réinitialise globalement qu'au début. */
        {
            script.AjoutReinitGlobal();
        }
        else
        {
            script.AjoutReinit2D();
        }

        if (parametres->R_ExportConfig3DScripts())
        {
            Perspective3D::ParametresPerspective params_p3d = perspective.Parametres();
            params_p3d.Id_ral = proprietes_plan.id_ral;
            script.AjoutConfig(params_p3d);
        }
        else
        {
            script.AjoutChargeConfig();
        }

        bool valide_vues = false;

        if (proprietes_plan.vue_reference == Perspective3D::vues2D_t::VUEMULT)
        {
            if (!proprietes_plan.rectCote.isNull())
            {
                script.AjoutVue2D(QPointF(proprietes_plan.rectCote.x(), -proprietes_plan.rectCote.y()),
                                  QPointF(proprietes_plan.rectCote.width(), -proprietes_plan.rectCote.height()),
                                  Perspective3D::vues2D_t::VUECOTE, false);
            }
            if (!proprietes_plan.rectFace.isNull())
            {
                script.AjoutVue2D(QPointF(proprietes_plan.rectFace.x(), -proprietes_plan.rectFace.y()),
                                  QPointF(proprietes_plan.rectFace.width(), -proprietes_plan.rectFace.height()),
                                  Perspective3D::vues2D_t::VUEFACE, false);
            }
            if (!proprietes_plan.rectHaut.isNull())
            {
                script.AjoutVue2D(QPointF(proprietes_plan.rectHaut.x(), -proprietes_plan.rectHaut.y()),
                                  QPointF(proprietes_plan.rectHaut.width(), -proprietes_plan.rectHaut.height()),
                                  Perspective3D::vues2D_t::VUEHAUT, false);
            }

            if (!proprietes_plan.origineCote.isNull())
            {
                script.AjoutOrigine(QPointF(proprietes_plan.origineCote.x(), -proprietes_plan.origineCote.y()), Perspective3D::vues2D_t::VUECOTE);
            }
            if (!proprietes_plan.origineFace.isNull())
            {
                script.AjoutOrigine(QPointF(proprietes_plan.origineFace.x(), -proprietes_plan.origineFace.y()), Perspective3D::vues2D_t::VUEFACE);
            }
            if (!proprietes_plan.origineHaut.isNull())
            {
                script.AjoutOrigine(QPointF(proprietes_plan.origineHaut.x(), -proprietes_plan.origineHaut.y()), Perspective3D::vues2D_t::VUEHAUT);
            }

            valide_vues = true;
        }
        else
        {
            if (proprietes_plan.vue_reference == VUE_EXTRUSION)
            {
                if (VUE_EXTRUSION == Perspective3D::vues2D_t::VUEFACE)
                {
                    if (!proprietes_plan.rectFace.isNull())
                    {
                        script.AjoutVueExtrusion(QPointF(proprietes_plan.rectFace.x(), -proprietes_plan.rectFace.y()),
                                                 QPointF(proprietes_plan.rectFace.width(), -proprietes_plan.rectFace.height()),
                                                 false);
                        valide_vues = true;
                    }
                    if (!proprietes_plan.origineFace.isNull())
                    {
                        script.AjoutOrigineExtrusion(QPointF(proprietes_plan.origineFace.x(), -proprietes_plan.origineFace.y()));
                    }
                }
                else if (VUE_EXTRUSION == Perspective3D::vues2D_t::VUECOTE)
                {
                    if (!proprietes_plan.rectCote.isNull())
                    {
                        script.AjoutVueExtrusion(QPointF(proprietes_plan.rectCote.x(), -proprietes_plan.rectCote.y()),
                                                 QPointF(proprietes_plan.rectCote.width(), -proprietes_plan.rectCote.height()),
                                                 false);
                        valide_vues = true;
                    }
                    if (!proprietes_plan.origineCote.isNull())
                    {
                        script.AjoutOrigineExtrusion(QPointF(proprietes_plan.origineCote.x(), -proprietes_plan.origineCote.y()));
                    }
                }
                else if (VUE_EXTRUSION == Perspective3D::vues2D_t::VUEHAUT)
                {
                    if (!proprietes_plan.rectHaut.isNull())
                    {
                        script.AjoutVueExtrusion(QPointF(proprietes_plan.rectHaut.x(), -proprietes_plan.rectHaut.y()),
                                                 QPointF(proprietes_plan.rectHaut.width(), -proprietes_plan.rectHaut.height()),
                                                 false);
                        valide_vues = true;
                    }
                    if (!proprietes_plan.origineHaut.isNull())
                    {
                        script.AjoutOrigineExtrusion(QPointF(proprietes_plan.origineHaut.x(), -proprietes_plan.origineHaut.y()));
                    }
                }
            }
            else if (proprietes_plan.vue_reference == VUE_REVOLUTION)
            {
                if (VUE_REVOLUTION == Perspective3D::vues2D_t::VUEFACE)
                {
                    if (!proprietes_plan.rectFace.isNull())
                    {
                        script.AjoutVueRevolution(QPointF(proprietes_plan.rectFace.x(), -proprietes_plan.rectFace.y()),
                                                 QPointF(proprietes_plan.rectFace.width(), -proprietes_plan.rectFace.height()),
                                                 false);
                        valide_vues = true;
                    }
                    if (!proprietes_plan.origineFace.isNull())
                    {
                        script.AjoutOrigineRevolution(QPointF(proprietes_plan.origineFace.x(), -proprietes_plan.origineFace.y()));
                    }
                }
                else if (VUE_REVOLUTION == Perspective3D::vues2D_t::VUECOTE)
                {
                    if (!proprietes_plan.rectCote.isNull())
                    {
                        script.AjoutVueRevolution(QPointF(proprietes_plan.rectCote.x(), -proprietes_plan.rectCote.y()),
                                                 QPointF(proprietes_plan.rectCote.width(), -proprietes_plan.rectCote.height()),
                                                 false);
                        valide_vues = true;
                    }
                    if (!proprietes_plan.origineCote.isNull())
                    {
                        script.AjoutOrigineRevolution(QPointF(proprietes_plan.origineCote.x(), -proprietes_plan.origineCote.y()));
                    }
                }
                else if (VUE_REVOLUTION == Perspective3D::vues2D_t::VUEHAUT)
                {
                    if (!proprietes_plan.rectHaut.isNull())
                    {
                        script.AjoutVueRevolution(QPointF(proprietes_plan.rectHaut.x(), -proprietes_plan.rectHaut.y()),
                                                 QPointF(proprietes_plan.rectHaut.width(), -proprietes_plan.rectHaut.height()),
                                                 false);
                        valide_vues = true;
                    }
                    if (!proprietes_plan.origineHaut.isNull())
                    {
                        script.AjoutOrigineRevolution(QPointF(proprietes_plan.origineHaut.x(), -proprietes_plan.origineHaut.y()));
                    }
                }
            }
        }

        if (!valide_vues)
        {
            MesgDialog(this, tr("Information"), tr("Erreur lors de la récupération des vues pour l'export du script") + " " + chemin_script_f, QMessageBox::Information);
        }

        script.AjoutGen3D(proprietes_plan.param_gen1, false);
        script.AjoutClotureBloc();

//        script.AjoutExecBloc("conf0");
        script.AjoutExecBloc(fct_script);

        if (!script.Valide())
        {
            MesgDialog(this, tr("Information"), tr("Erreur lors de l'enregistrement du script") + " " + chemin_script_f, QMessageBox::Information);
        }
    }
    else
    {
#ifdef DEBUG
        qDebug() << "Enregistre le script (sans ajout) :: " << chemin_script_f;
#endif
    }
}

bool MainWindow::ExportScriptPlan(const QString &chemin, const Perspective3D::PScene2D &scene)
/* Enregistre le plan 2D dans un fichier de script. */
{
    const int langue_script = ConvLangueP3DAutomate(Perspective3D::i18n::Langue());
    ScriptAutomate script(chemin, langue_script, false, false);

//    script.AjoutReinit2D();

    script.AjoutAffichageOnglet2D();
    script.AjoutReinit2D();
    script.AjoutFermerPlan();

    bool valide = script.AjoutScene2D(scene);

    if (!valide || !script.Valide())
    {
        MesgDialog(this, tr("Information"), tr("Erreur lors de l'enregistrement du fichier") + " " + chemin, QMessageBox::Information);
        return false;
    }
    return true;
}

void MainWindow::FinGeneration(int id_onglet)
{
    if ((id_onglet-1) >= int(ListeVues3D->size()))
    {
        return;
    }

    const Interface3d *interface3d = ListeVues3D->at(id_onglet-NombreVues2D);
    if (interface3d)
    {
        if (interface3d->Perspective())
        {
            const int taille_vignette = parametres->R_InterfacePetitEcran() ? 64 : 128;
            defIconeOnglet(id_onglet, interface3d->SceneP3DConst()->GenerationVignette(taille_vignette));
        }
        else if (interface3d->SceneP3D())
        {
            const int taille_vignette = parametres->R_InterfacePetitEcran() ? 64 : 128;
            defIconeOnglet(id_onglet, interface3d->SceneP3DConst()->GenerationVignette(taille_vignette));
        }
    }

    GereOutilsBarreOnglets();
}

#ifdef SUPPORT_VISION2D
void MainWindow::Annuler()
{
    if (scene2D && persp_scene_2D && Contexte2D())
    {
#ifdef DEBUG
        qDebug() << "MainWindow::Annuler() :: " << persp_scene_2D->Groupes().size() << " & " << scene2D->NombreGroupes();
#endif // DEBUG
        if (persp_scene_2D->SupprimeDernierGroupe())
        {
            if (scene2D->SupprimeDernierGroupe())
            {
            }
        }
    }
}
// SUPPORT_VISION2D...
void MainWindow::Restaurer()
{
    if (scene2D && persp_scene_2D && Contexte2D())
    {
#ifdef DEBUG
    qDebug() << "MainWindow::Restaurer() :: " << persp_scene_2D->Groupes().size() << " & " << scene2D->NombreGroupes();
#endif // DEBUG
        if (persp_scene_2D->RestaureDernierGroupe())
        {
            if (scene2D->ImportGroupeEntitesPerspective(persp_scene_2D->DernierGroupe(), persp_scene_2D->Groupes().size()-1))
            {
            }
        }
    }
}
#endif // SUPPORT_VISION2D

void MainWindow::defIconeOnglet(int id_onglet, const Perspective3D::PImage &image)
/* Assigne une icone sur l'onglet dont l'id est donné en argument. */
{
    QByteArray tampon_img;

    QPixmap pix_mod = ConversionPImageQPixmap(image);
    QBuffer buffer(&tampon_img);
    pix_mod.save(&buffer, "PNG");

    ui->tabWidget->setTabToolTip(id_onglet, QString("<img src=\"data:image/png;base64,") + tampon_img.toBase64() + "\"/>");
}

bool MainWindow::ValideBarreOutils2D() const
{
    return barreoutils2d != 0;
}

bool MainWindow::ExecGenerationModele(bool supprime_vues)
{
    if (Contexte2D())
    {
        Perspective3D::ParametresPerspective parametres_p3d;
        parametres->ParamsPerspective(parametres_p3d);
        if (Generation3D(parametres_p3d, -1, supprime_vues))
        {
            return true;
        }
    }
    return false;
}

void MainWindow::on_actionGenerer_triggered()
/* L'utilisateur demande la génération du modèle... */
{
    ExecGenerationModele(false);
}

void MainWindow::OuvrirGenerique()
{
    if (Contexte2D())
    {
        QString n = DialogOuvrir();
        if (!n.isEmpty())
        {
            Ouvrir(n);
        }
    }
}

void MainWindow::InverseModeFilaire()
{
    on_actionModele_filaire_triggered(!ui->actionModele_filaire->isChecked());
}

void MainWindow::on_actionModele_filaire_triggered(bool checked)
{
    if (checked != ui->actionModele_filaire->isChecked())
    {
        ui->actionModele_filaire->setChecked(checked);
    }
    parametres->defFilaireUniquement(checked);
    AjoutConsole(tr("Mode filaire: ") + QString::number(checked));
}

void MainWindow::on_actionParametres_triggered()
{
    if (GestionScripts->isVisible())
    {
        GestionScripts->close();
    }
    ArretAnimationsAutomate(); /* Arrêt de l'automate ! */

    if (!parametres->isVisible())
    {
        ui->actionParametres->setChecked(true);
        parametres->show();
        AjoutConsole(tr("Paramètres..."));
    }
    else
    {
        ui->actionParametres->setChecked(false);
        parametres->close();
    }
}

void MainWindow::cmb_tolerance_currentIndexChanged(int index)
{
    if (index && ValideBarreOutils2D())
    {
        parametres->defNiveauTolerance(index, cmb_tolerance->count());
        AjoutConsole(tr("Niveau tolérance: ") + QString::number(index));
    }
}

void MainWindow::on_actionAide_triggered()
{
    if (parametres->isVisible())
    {
        parametres->AfficheAide();
    }
    else
    {
        if (!Aide->isVisible())
        {
            //Aide->OuvrePage("index.html");
            Aide->setFloating(parametres->R_InterfacePetitEcran());
            if (Contexte2D())
            {
                Aide->show();
            }
            else
            {
                Aide->OuvrePage("barreoutils_3d.html");
            }
        }
        else
        {
            Aide->hide();
        }
    }
}

void MainWindow::on_actionAfficheConsole_triggered()
{
    if (Contexte2D())
    {
        if (widget_console->isVisible())
        {
            MasqueConsole();
        }
        else
        {
            AfficheConsole(false);
        }
    }
    else
    {
        Interface3d *interface_3d = Interface3DCourante();
        if (!interface_3d) { return; }
        interface_3d->GereProprietes();
    }
}

void MainWindow::on_actionEnregistrer_le_mod_le_triggered()
{
    if (Contexte2D()) { return; }
    EnregistreModele();
}

void MainWindow::on_actionImprimer_triggered()
{
    if (Contexte2D()) { return; }
    ImprimeModele();
}

void MainWindow::on_actionEnregistrer_le_script_triggered()
{
    Interface3d *interface_3d = Interface3DCourante();
    if (interface_3d && interface_3d->Perspective())
    {
        EnregistreScript(interface_3d->ProprietesPlan(), *interface_3d->Perspective());
    }
}

void MainWindow::on_actionArreter_le_script_triggered()
{
    ArretAnimationsAutomate();
}

void MainWindow::on_actionScripts_triggered()
{
    if (!GestionScripts->isVisible())
    {
        GestionScripts->AfficheScripts(parametres->R_DernierDossier_Ouverture(), plan_courant);
        if (!ui->actionScripts->isChecked())
        {
            ui->actionScripts->setChecked(true);
        }
    }
    else
    {
        GestionScripts->close();
        if (ui->actionScripts->isChecked())
        {
            ui->actionScripts->setChecked(false);
        }
    }
}

void MainWindow::on_actionEnregistrer_image_triggered()
{
    if (Contexte2D()) { return; }
    ExportImageModele();
}

void MainWindow::on_actionDebloquer_le_logiciel_triggered()
{
    animation.InitAnimations();
    CliqueAction(ui->actionParametres);
    parametres->AnimeLicence();
    animation.ArretAnimations();
}

#ifdef SUPPORT_VISION2D
void MainWindow::ForceModeEdition(bool etat)
{
//    qDebug() << "MainWindow::ForceModeEdition :: " << etat;
    if (scene2D && Contexte2D())
    {
        if (etat == scene2D->ModeEdition())
        {
            return;
        }

//        vue2D->ReinitMatriceAffichage();

        if (!scene2D->ModeEdition())
        {
            scene2D->AnnuleDessinRectCourant();
        }

        if (etat)
        {
#if 0
            if (parametres->R_AutoriseAnimationScripts())
            {
                animation.InitAnimations();
//                vue2D->CentreSurAnime(QPointF(0.0, 0.0));
                vue2D->ReinitEchelleAnime();
                animation.ArretAnimations();
            }
            else
            {
                vue2D->ReinitEchelle();
            }
#else
            vue2D->ReinitEchelle();
#endif // 0

//            vue2D->setCursor(QCursor(Qt::CrossCursor));
            QGuiApplication::setOverrideCursor(QCursor(Qt::CrossCursor));
            AfficheConsole();
            widget_edition_2d->show();
        }
        else
        {
//            vue2D->setCursor(QCursor(Qt::ArrowCursor));
            QGuiApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
            widget_edition_2d->hide();
            MasqueConsole();
        }

        scene2D->ForceModeEdition(etat);
//        scene2D->PermutationModeEdition();
        MAJ_IconeOnglet2D();
    }
}
// SUPPORT_VISION2D...
void MainWindow::PermutteModeEdition2D()
{
    if (vue2D)
    {
        ForceModeEdition(!vue2D->ModeEdition());
    }
}
// SUPPORT_VISION2D...
void MainWindow::DoubleClicScene2D(bool mode)
{
    ForceModeEdition(mode);
}
// SUPPORT_VISION2D...
void MainWindow::NouveauTraceVision2D(const QList<QPoint> &ls, const QPointF &scene_pos_dernier_point)
/* Exécute la conversion de la liste des points du dessin à main levée avec le module Vision2D. */
{
#ifdef DEBUG
//    qDebug() << "Nouveau tracé Vision 2D : " << ls;
#endif
    const int n_ls = ls.size();
    if (!n_ls)
    {
        return;
    }

    const qreal echelle = vue2D->EchelleActuelle();
    const qreal m_echelle = 1.0 / echelle;

    const int taille_max = parametres->R_ZoneDessin();

    /* Recherche du point minmum qui servira d'origine. */
    QPoint point_min(taille_max, taille_max);

    Perspective3D::PStdVectPointsPixels trajet_points_pixels(n_ls);

    for(int i=0; i<n_ls; ++i)
    {
        const QPoint &p = ls.at(i);
        trajet_points_pixels[i].defXY(p.x(), p.y());
        if (p.x() < point_min.x())
        {
            point_min.setX(p.x());
        }
        if (p.y() < point_min.y())
        {
            point_min.setY(p.y());
        }
    }

    if (point_min.x() == taille_max || point_min.y() == taille_max)
    {
        return;
    }

//    qDebug() << "Point minimum : " << point_min;

    /* Création de la PScene2D si elle n'existe pas déjà: */
    if (!persp_scene_2D)
    {
        persp_scene_2D = new Perspective3D::PScene2D;
    }

    /* Génération des entités: */
    Perspective3D::ParametresVision parametres_v2d;
    if (!parametres->AssigneParametresVision2D(parametres_v2d, ConfigVision::DOMAINE_MAIN_LEVEE))
    {
        Ferme_plan();
        return;
    }

    const QPoint decalage_coords = scene_pos_dernier_point.toPoint() - ls.back();

    QPoint origine = (point_min + decalage_coords);
    origine.setX(int((qreal(origine.x())*echelle)+0.5));
    origine.setY(int((qreal(origine.y())*echelle)+0.5));

#ifdef DEBUG_API_P3D
//    {
//        const int marge_img = 16;
//        QPoint origine2 = ((point_min + decalage_coords) - QPoint(marge_img, marge_img));
//        origine2.setX(int((qreal(origine2.x())*echelle)+0.5));
//        origine2.setY(int((qreal(origine2.y())*echelle)+0.5));
//        Perspective3D::PImage image_trajet(trajet_points_pixels, m_echelle, marge_img);
//        QPixmap q_image_trajet = ConversionPImageQPixmap(image_trajet);
//        QGraphicsPixmapItem * pix = scene2D->addPixmap(q_image_trajet);
//        pix->setPos(origine2.x()*m_echelle, origine2.y()*m_echelle);
//        qDebug() << "Image convertie : " << q_image_trajet.width() << " : " << q_image_trajet.height();
//        qDebug() << "  Echelle actuelle : " << echelle;
//        return;
//    }

    if (vision_2d != nullptr)
    {
        delete vision_2d;
        vision_2d = nullptr;
    }
#endif // DEBUG_API_P3D

#ifdef DEBUG_API_P3D
    const Perspective3D::Ppoint2D origine_finale(origine.x()-3, origine.y()+3);
    vision_2d = Perspective3D::Vision::Construct(trajet_points_pixels, m_echelle, parametres_v2d, origine_finale, persp_scene_2D, true);
#else
    Perspective3D::Vision *vision_2d = Perspective3D::Vision::Construct(trajet_points_pixels, m_echelle, parametres_v2d, Perspective3D::Ppoint2D(origine.x(), origine.y()), persp_scene_2D, true);
#endif // DEBUG_API_P3D
    const Perspective3D::resultat_vision2d_t resultat_v2d = vision_2d->Genere();

    if ( (resultat_v2d == Perspective3D::resultat_vision2d_t::V2D_OK) ||
         (resultat_v2d == Perspective3D::resultat_vision2d_t::V2D_ASSEMBLAGE)  )
    {
        scene2D->Nettoyage();

        if (parametres->R_AffichageMatriceVision() && vision_2d->MatriceActive())
        {
#ifdef DEBUG_API_P3D
//            const int mult_pos_y = 1; // 1
//            scene2D->AjoutImage(vision_2d->MatriceDebug(0), Perspective3D::Ppoint2D(origine.x()*m_echelle, (origine.y()*m_echelle)*mult_pos_y));
            scene2D->AjoutImage(vision_2d->MatriceDebug(0), Perspective3D::Ppoint2D((origine.x()-6)*m_echelle, ((origine.y()-3)*m_echelle)));
#else
            scene2D->AjoutImage(vision_2d->Matrice(), Perspective3D::Ppoint2D((origine.x()-6)*m_echelle, ((origine.y()-3)*m_echelle)));
#endif // DEBUG_API_P3D
        }

        if (!scene2D->ImportScenePerspective(persp_scene_2D, false))
        {
            delete persp_scene_2D;
            persp_scene_2D = nullptr;
            return;
        }
    }

#ifndef DEBUG_API_P3D
    delete vision_2d;
#endif // DEBUG_API_P3D

//    scene2D->ReinitRect(Perspective3D::vues2D_t::VUEFACE | Perspective3D::vues2D_t::VUECOTE | Perspective3D::vues2D_t::VUEHAUT);

    if (MesgErreurVision2D(resultat_v2d))
    {
#ifdef DEBUG
        qDebug() << "NouveauTraceVision2D :: Erreur lors de la conversion du tracé ?! : " << int(resultat_v2d);
#endif // DEBUG
    }

    GereIconesOutils2D();
}
// SUPPORT_VISION2D...
bool MainWindow::MesgErreurVision2D(const Perspective3D::resultat_vision2d_t resultat_v2d)
{
    if (resultat_v2d == Perspective3D::resultat_vision2d_t::V2D_OK)
    {
        return false;
    }
    if (resultat_v2d == Perspective3D::resultat_vision2d_t::V2D_ASSEMBLAGE)
    {
        AfficheMesgStatus(tr("Rien à ajouter (mais il y a eu fusion avec des entités précédentes)."));
        return true;
    }

    if (scene2D && scene2D->ModeEdition2()) /* En principe, cela veut dire que l'utilisateur vient de double-cliquer sur la vue 2D. */
    {
        if ( (resultat_v2d & Perspective3D::resultat_vision2d_t::V2D_DEPASSEMENT_CAPACITE) ||
             (resultat_v2d & Perspective3D::resultat_vision2d_t::V2D_MATRICE_INVALIDE) )
        {
            ForceModeEdition(false);
            return false;
        }
    }

    QString mesg;
    int cpt_erreurs = 0;

    if (resultat_v2d & Perspective3D::resultat_vision2d_t::V2D_MATRICE_INVALIDE)
    {
        AfficheMesgStatus(tr("Impossible de convertir le plan, la matrice est trop petite."));
        return true; /* On affiche rien dans ce cas là... */
    }

    if ( (resultat_v2d & Perspective3D::resultat_vision2d_t::V2D_TRACE_NUL)) /* Pas une erreur en soit pour l'utilisateur... */
    {
//        mesg += " - " + tr("Tracé nul.") + "\n";
//        ++cpt_erreurs;

        AfficheMesgStatus(tr("Rien à ajouter"));
        return true; /* On affiche rien dans ce cas là... */
    }

    ForceModeEdition(false);

    if (resultat_v2d & Perspective3D::resultat_vision2d_t::V2D_MAX_ENTITES_LICENCE)
    {
        mesg += " - " + tr("Nombre maximum d'entités atteint. Il est possible de débloquer le logiciel pour pouvoir tracer un nombre illimité d'entités.") + "\n";
        ++cpt_erreurs;
    }

    if (resultat_v2d & Perspective3D::resultat_vision2d_t::V2D_ERR_GROUPE)
    {
        mesg += " - " + tr("Groupe 2D invalide.") + "\n";
        ++cpt_erreurs;
    }

    if (resultat_v2d & Perspective3D::resultat_vision2d_t::V2D_DEPASSEMENT_CAPACITE)
    {
        mesg += " - " + tr("Dépassement de capacité. Le contenu de l'image est probablement trop dense.") + "\n";
        ++cpt_erreurs;
    }

    if ((!cpt_erreurs) || (resultat_v2d & Perspective3D::resultat_vision2d_t::V2D_ERR_INCONNUE))
    {
        mesg += " - " + tr("Erreur inconnue lors du traitement 2D.") + "\n";
    }

    mesg = tr("Erreur lors de la génération Vision2D :") + "\n\n" + mesg;
    AjoutConsole(mesg, false);
    QMessageBox dial(QMessageBox::Critical, tr("Erreur"), mesg, QMessageBox::Ok, this);
    dial.exec();
    return true;
}
// SUPPORT_VISION2D...
void MainWindow::EnregistrePlan2D()
{
    if (!Contexte2D())
    {
        return;
    }

    if (!persp_scene_2D || !persp_scene_2D->ValideScene())
    {
        AjoutConsole(tr("Export :: Il n'y a rien à enregistrer..."));
        MesgDialog(this, tr("Erreur"), tr("Erreur lors de l'enregistrement, le plan est vide."), QMessageBox::Information);
        return;
    }

    QString type_fichier;

    static const QString tr_dxf = tr("Plan DXF") + " (*.dxf)";
    static const QString tr_z6s = tr("Script Zetta6") + " (*." ExtensionScriptsAutomate ")";

    QString nfichier = DialogEnregistrer(parametres->R_CheminExport() + QDir::separator() + tr("sans_nom") + ".dxf", tr_dxf + ";;" + tr_z6s, &type_fichier);

    if (!nfichier.isEmpty())
    {
        if (nfichier.endsWith(".dxf", Qt::CaseInsensitive))
        {
            if (!persp_scene_2D->ExportDXF(nfichier.toStdString()))
            {
                AjoutConsole(tr("Erreur lors de la création du fichier '%1' (droits d'accès ?).").arg(nfichier));
                MesgDialog(this, tr("Erreur"), tr("Erreur lors de l'enregistrement du fichier %1\n\nPeut être n'y a t'il rien à exporter ?\nOu peut être n'avez-vous pas les droits en écriture ?").arg(nfichier), QMessageBox::Information);
                return;
            }
            AjoutConsole(tr("Fichier %1 enregistré.").arg(nfichier));
        }
        else if (nfichier.endsWith("." ExtensionScriptsAutomate , Qt::CaseInsensitive))
        {
            if (ExportScriptPlan(nfichier, *persp_scene_2D))
            {
                AjoutConsole(tr("Fichier %1 enregistré.").arg(nfichier));
            }
            else
            {
                AjoutConsole(tr("Erreur lors de la création du fichier '%1' (droits d'accès ?).").arg(nfichier));
            }
        }
        else
        {
            AjoutConsole(tr("Erreur lors de la création du fichier '%1' (type inconnu).").arg(nfichier));
        }
    }
}
// SUPPORT_VISION2D...
#ifdef DEBUG_API_P3D
void MainWindow::EnregistrerMatriceVision2D_mousePress()
{
    if (vision_2d != nullptr)
    {
        if (vision_2d->MatriceActive())
        {
            ForceModeEdition(false); /* Désactive le tracé */

            QString type_fichier;
            QString nfichier = DialogEnregistrer(parametres->R_CheminExport() + QDir::separator() + tr("sans_nom") + ".png", tr("Image") + " (*.png)", &type_fichier);

            if (!type_fichier.isEmpty() && !nfichier.isEmpty())
            {
                QPixmap q_image_matrice_v2d = ConversionPImageQPixmap(vision_2d->MatriceDebug(0));
                q_image_matrice_v2d.save(nfichier);
            }
        }
        else
        {
            MesgDialog(this, tr("Zetta6"), tr("Impossible d'exporter la matrice, elle n'est pas active..."), QMessageBox::Information);
        }
    }
}
#endif // DEBUG_API_P3D
// SUPPORT_VISION2D...
void MainWindow::EnregistrerScene2D_mousePress()
{
    ForceModeEdition(false); /* Désactive le tracé */
    EnregistrePlan2D();
}

void MainWindow::AnnulerScene2D_mousePress()
{
    Annuler();
}
// SUPPORT_VISION2D...
void MainWindow::RetablirScene2D_mousePress()
{
    Restaurer();
}

#endif // SUPPORT_VISION2D

#ifdef SUPPORT_GIF
void MainWindow::on_actionEnregistrer_animation_triggered()
{
    if (Contexte2D()) { return; }
    EnregistreAnimationModele();
}
#endif

void MainWindow::on_actionQuitter_triggered()
{
    this->close();
}

void MainWindow::on_actionVue_de_face_3d_triggered()
{
    Interface3d *interface_3d = Interface3DCourante();
    if (interface_3d)
    {
        interface_3d->ForceVue(Perspective3D::vues2D_t::VUEFACE);
    }
}

void MainWindow::on_actionVue_de_cote_3d_triggered()
{
    Interface3d *interface_3d = Interface3DCourante();
    if (interface_3d)
    {
        interface_3d->ForceVue(Perspective3D::vues2D_t::VUECOTE);
    }
}

void MainWindow::on_actionVue_de_dessus_3d_triggered()
{
    Interface3d *interface_3d = Interface3DCourante();
    if (interface_3d)
    {
        interface_3d->ForceVue(Perspective3D::vues2D_t::VUEHAUT);
    }
}

void MainWindow::on_actionVue_oblique_3d_triggered()
{
    Interface3d *interface_3d = Interface3DCourante();
    if (interface_3d)
    {
        interface_3d->ForceVue(Perspective3D::vues2D_t::VUEMULT);
    }
}

void MainWindow::on_action_Permutte_mode_plein_ecran_triggered()
{
    parametres->defPleinEcran(!parametres->R_PleinEcran());
    GereFenetre();
}

void MainWindow::on_actionPermutte_ecran_triggered()
{
    int id = DeplaceEcranSuivantApp(this);
    if (id)
    {
        if (id<0)
        {
            ui->actionPermutte_ecran->setIcon(QIcon(":/icones/permutte_ecran_g.png"));
        }
        else
        {
            ui->actionPermutte_ecran->setIcon(QIcon(":/icones/permutte_ecran_d.png"));
        }
    }
}

void MainWindow::on_actionAffichage_proprietes3D_triggered()
{
    Interface3d *interface_3d = Interface3DCourante();
    if (interface_3d)
    {
        interface_3d->GereProprietes();
    }
}

void MainWindow::on_actionConversion_2D_triggered()
{
    if (Contexte2D()) { return; }
    EnregistreModele_2D();
}
