mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2025-09-14 20:33:05 +02:00
1621 lines
55 KiB
C++
1621 lines
55 KiB
C++
/*
|
|
Copyright 2006-2019 The QElectroTech Team
|
|
This file is part of QElectroTech.
|
|
|
|
QElectroTech is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
QElectroTech is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "qetelementeditor.h"
|
|
#include "qet.h"
|
|
#include "qetapp.h"
|
|
#include "elementscene.h"
|
|
#include "elementview.h"
|
|
#include "customelementpart.h"
|
|
#include "newelementwizard.h"
|
|
#include "elementitemeditor.h"
|
|
#include "elementdialog.h"
|
|
#include "recentfiles.h"
|
|
#include "qeticons.h"
|
|
#include "qetmessagebox.h"
|
|
#include "editorcommands.h"
|
|
|
|
// editeurs de primitives
|
|
#include "arceditor.h"
|
|
#include "ellipseeditor.h"
|
|
#include "lineeditor.h"
|
|
#include "polygoneditor.h"
|
|
#include "rectangleeditor.h"
|
|
#include "terminaleditor.h"
|
|
#include "texteditor.h"
|
|
#include "partterminal.h"
|
|
#include "styleeditor.h"
|
|
#include "dynamictextfieldeditor.h"
|
|
|
|
#include "eseventaddline.h"
|
|
#include "eseventaddrect.h"
|
|
#include "eseventaddellipse.h"
|
|
#include "eseventaddpolygon.h"
|
|
#include "eseventaddarc.h"
|
|
#include "eseventaddtext.h"
|
|
#include "eseventaddterminal.h"
|
|
#include "eseventadddynamictextfield.h"
|
|
|
|
#include <QMessageBox>
|
|
#include <QTextStream>
|
|
#include <QFileDialog>
|
|
#include <QFile>
|
|
#include <QModelIndex>
|
|
#include <utility>
|
|
|
|
/*
|
|
Nombre maximum de primitives affichees par la "liste des parties"
|
|
Au-dela, un petit message est affiche, indiquant que ce nombre a ete depasse
|
|
et que la liste ne sera donc pas mise a jour.
|
|
*/
|
|
#define QET_MAX_PARTS_IN_ELEMENT_EDITOR_LIST 200
|
|
|
|
/**
|
|
Constructeur
|
|
@param parent QWidget parent
|
|
*/
|
|
QETElementEditor::QETElementEditor(QWidget *parent) :
|
|
QETMainWindow(parent),
|
|
read_only(false),
|
|
min_title(tr("QElectroTech - Éditeur d'élément", "window title")),
|
|
opened_from_file(false)
|
|
{
|
|
setWindowTitle(min_title);
|
|
setWindowIcon(QET::Icons::QETLogo);
|
|
|
|
setupInterface();
|
|
setupActions();
|
|
setupMenus();
|
|
|
|
// la fenetre est maximisee par defaut
|
|
setMinimumSize(QSize(500, 350));
|
|
setWindowState(Qt::WindowMaximized);
|
|
|
|
// lecture des parametres
|
|
readSettings();
|
|
slot_updateMenus();
|
|
|
|
// affichage
|
|
show();
|
|
}
|
|
|
|
/// Destructeur
|
|
QETElementEditor::~QETElementEditor() {
|
|
/*
|
|
retire le widget d'edition de primitives affiche par le dock
|
|
cela evite qu'il ne soit supprime par son widget parent
|
|
*/
|
|
clearToolsDock();
|
|
|
|
// supprime les editeurs de primitives
|
|
qDeleteAll(m_editors.begin(), m_editors.end());
|
|
m_editors.clear();
|
|
}
|
|
|
|
/**
|
|
* @brief QETElementEditor::setLocation
|
|
* The new location to edit
|
|
* @param el
|
|
*/
|
|
void QETElementEditor::setLocation(const ElementsLocation &el)
|
|
{
|
|
location_ = el;
|
|
opened_from_file = false;
|
|
setReadOnly(!location_.isWritable());
|
|
slot_updateTitle();
|
|
}
|
|
|
|
/**
|
|
@param fn Le nouveau nom de fichier de l'element edite
|
|
*/
|
|
void QETElementEditor::setFileName(const QString &fn) {
|
|
filename_ = fn;
|
|
opened_from_file = true;
|
|
// modifie le mode lecture seule si besoin
|
|
bool must_be_read_only = !QFileInfo(filename_).isWritable();
|
|
if (isReadOnly() != must_be_read_only) {
|
|
setReadOnly(must_be_read_only);
|
|
}
|
|
slot_updateTitle();
|
|
}
|
|
|
|
/**
|
|
* @brief QETElementEditor::setupActions
|
|
* Create action used in Element editor
|
|
*/
|
|
void QETElementEditor::setupActions() {
|
|
new_element = new QAction(QET::Icons::DocumentNew, tr("&Nouveau"), this);
|
|
open = new QAction(QET::Icons::FolderOpen, tr("&Ouvrir"), this);
|
|
open_file = new QAction(QET::Icons::FolderOpen, tr("&Ouvrir depuis un fichier"), this);
|
|
open_dxf = new QAction(QET::Icons::RunDxf, tr("&Lancer le plugin convertisseur DXF"), this);
|
|
save = new QAction(QET::Icons::DocumentSave, tr("&Enregistrer"), this);
|
|
save_as = new QAction(QET::Icons::DocumentSaveAs, tr("Enregistrer sous"), this);
|
|
save_as_file = new QAction(QET::Icons::DocumentSaveAs, tr("Enregistrer dans un fichier"), this);
|
|
reload = new QAction(QET::Icons::ViewRefresh, tr("Recharger"), this);
|
|
quit = new QAction(QET::Icons::ApplicationExit, tr("&Quitter"), this);
|
|
selectall = new QAction(QET::Icons::EditSelectAll, tr("Tout sélectionner"), this);
|
|
deselectall = new QAction(QET::Icons::EditSelectNone, tr("Désélectionner tout"), this);
|
|
cut = new QAction(QET::Icons::EditCut, tr("Co&uper"), this);
|
|
copy = new QAction(QET::Icons::EditCopy, tr("Cop&ier"), this);
|
|
paste = new QAction(QET::Icons::EditPaste, tr("C&oller"), this);
|
|
paste_in_area = new QAction(QET::Icons::EditPaste, tr("C&oller dans la zone..."), this);
|
|
paste_from_file = new QAction(QET::Icons::XmlTextFile, tr("un fichier"), this);
|
|
paste_from_elmt = new QAction(QET::Icons::Element, tr("un élément"), this);
|
|
inv_select = new QAction(QET::Icons::EditSelectInvert, tr("Inverser la sélection"), this);
|
|
edit_delete = new QAction(QET::Icons::EditDelete, tr("&Supprimer"), this);
|
|
edit_names = new QAction(QET::Icons::Names, tr("Éditer le nom et les traductions de l'élément"), this);
|
|
edit_author = new QAction(QET::Icons::UserInformations, tr("Éditer les informations sur l'auteur"), this);
|
|
m_edit_properties = new QAction(QET::Icons::ElementEdit, tr("Éditer les propriétés de l'élément"), this);
|
|
|
|
#if defined(Q_OS_WIN32) || defined(Q_OS_WIN64)
|
|
open_dxf -> setStatusTip(tr("To install the plugin DXFtoQET\nVisit https://download.tuxfamily.org/qet/builds/dxf_to_elmt/\n"
|
|
"\n"
|
|
">> Install on Windows\n"
|
|
"Put DXFtoQET.exe binary on C:\\Users\\user_name\\AppData\\Roaming\\qet\\ directory \n"
|
|
));
|
|
#elif defined(Q_OS_MAC)
|
|
open_dxf -> setStatusTip(tr("To install the plugin DXFtoQET\nVisit https://download.tuxfamily.org/qet/builds/dxf_to_elmt/\n"
|
|
"\n"
|
|
">> Install on macOSX\n"
|
|
"Put DXFtoQET.app binary on /Users/user_name/.qet/ directory \n"
|
|
));
|
|
#else
|
|
open_dxf -> setStatusTip(tr("To install the plugin DXFtoQET\nVisit https://download.tuxfamily.org/qet/builds/dxf_to_elmt/\n"
|
|
"\n"
|
|
">> Install on Linux\n"
|
|
"Put DXFtoQET binary on your /home/user_name/.qet/ directory\n"
|
|
"make it executable : chmod +x ./DXFtoQET\n"
|
|
));
|
|
#endif
|
|
|
|
open_dxf -> setWhatsThis (tr("To install the plugin DXFtoQET\nVisit https://download.tuxfamily.org/qet/builds/dxf_to_elmt/\n"
|
|
"\n"
|
|
">> Install on Linux\n"
|
|
"Put DXFtoQET binary on your /home/user_name/.qet/ directory\n"
|
|
"make it executable : chmod +x ./DXFtoQET\n"
|
|
">> Install on Windows\n"
|
|
"Put DXFtoQET.exe binary on C:\\Users\\user_name\\AppData\\Roaming\\qet\\ directory \n"
|
|
"\n"
|
|
">> Install on macOSX\n"
|
|
"Put DXFtoQET.app binary on /Users/user_name/.qet/ directory \n"
|
|
));
|
|
|
|
undo = m_elmt_scene -> undoStack().createUndoAction(this, tr("Annuler"));
|
|
redo = m_elmt_scene -> undoStack().createRedoAction(this, tr("Refaire"));
|
|
undo -> setIcon(QET::Icons::EditUndo);
|
|
redo -> setIcon(QET::Icons::EditRedo);
|
|
undo -> setShortcuts(QKeySequence::Undo);
|
|
redo -> setShortcuts(QKeySequence::Redo);
|
|
|
|
new_element -> setShortcut(QKeySequence::New);
|
|
open -> setShortcut(QKeySequence::Open);
|
|
open_file -> setShortcut(tr("Ctrl+Shift+O"));
|
|
save -> setShortcut(QKeySequence::Save);
|
|
save_as_file -> setShortcut(tr("Ctrl+Shift+S"));
|
|
quit -> setShortcut(QKeySequence(tr("Ctrl+Q")));
|
|
selectall -> setShortcut(QKeySequence::SelectAll);
|
|
deselectall -> setShortcut(QKeySequence(tr("Ctrl+Shift+A")));
|
|
inv_select -> setShortcut(QKeySequence(tr("Ctrl+I")));
|
|
cut -> setShortcut(QKeySequence::Cut);
|
|
copy -> setShortcut(QKeySequence::Copy);
|
|
paste -> setShortcut(QKeySequence::Paste);
|
|
paste_in_area -> setShortcut(tr("Ctrl+Shift+V"));
|
|
#ifndef Q_OS_MAC
|
|
edit_delete -> setShortcut(QKeySequence(Qt::Key_Delete));
|
|
#else
|
|
edit_delete -> setShortcut(QKeySequence(tr("Backspace")));
|
|
#endif
|
|
|
|
edit_names -> setShortcut(QKeySequence(tr("Ctrl+E")));
|
|
edit_author -> setShortcut(tr("Ctrl+Y"));
|
|
|
|
connect(new_element, SIGNAL(triggered()), this, SLOT(slot_new()));
|
|
connect(open, SIGNAL(triggered()), this, SLOT(slot_open()));
|
|
connect(open_dxf, SIGNAL(triggered()), this, SLOT(slot_openDxf()));
|
|
connect(open_file, SIGNAL(triggered()), this, SLOT(slot_openFile()));
|
|
connect(save, SIGNAL(triggered()), this, SLOT(slot_save()));
|
|
connect(save_as, SIGNAL(triggered()), this, SLOT(slot_saveAs()));
|
|
connect(save_as_file, SIGNAL(triggered()), this, SLOT(slot_saveAsFile()));
|
|
connect(reload, SIGNAL(triggered()), this, SLOT(slot_reload()));
|
|
connect(quit, SIGNAL(triggered()), this, SLOT(close()));
|
|
connect(selectall, SIGNAL(triggered()), m_elmt_scene, SLOT(slot_selectAll()));
|
|
connect(deselectall, SIGNAL(triggered()), m_elmt_scene, SLOT(slot_deselectAll()));
|
|
connect(inv_select, SIGNAL(triggered()), m_elmt_scene, SLOT(slot_invertSelection()));
|
|
connect(cut, SIGNAL(triggered()), m_view, SLOT(cut()));
|
|
connect(copy, SIGNAL(triggered()), m_view, SLOT(copy()));
|
|
connect(paste, SIGNAL(triggered()), m_view, SLOT(paste()));
|
|
connect(paste_in_area, SIGNAL(triggered()), m_view, SLOT(pasteInArea()));
|
|
connect(paste_from_file, SIGNAL(triggered()), this, SLOT(pasteFromFile()));
|
|
connect(paste_from_elmt, SIGNAL(triggered()), this, SLOT(pasteFromElement()));
|
|
connect(edit_delete, SIGNAL(triggered()), m_elmt_scene, SLOT(slot_delete()));
|
|
connect(edit_names, SIGNAL(triggered()), m_elmt_scene, SLOT(slot_editNames()));
|
|
connect(edit_author, SIGNAL(triggered()), m_elmt_scene, SLOT(slot_editAuthorInformations()));
|
|
connect(m_edit_properties, SIGNAL(triggered()), m_elmt_scene, SLOT(slot_editProperties()));
|
|
|
|
//Action related to change depth of primitive
|
|
m_depth_action_group = QET::depthActionGroup(this);
|
|
|
|
connect(m_depth_action_group, &QActionGroup::triggered, [this](QAction *action) {
|
|
this->elementScene()->undoStack().push(new ChangeZValueCommand(this->elementScene(), action->data().value<QET::DepthOption>()));
|
|
emit(this->elementScene()->partsZValueChanged());
|
|
});
|
|
|
|
depth_toolbar = addToolBar(tr("Profondeur", "toolbar title"));
|
|
depth_toolbar -> setObjectName("depth_toolbar");
|
|
depth_toolbar -> addActions(m_depth_action_group -> actions());
|
|
addToolBar(Qt::TopToolBarArea, depth_toolbar);
|
|
|
|
|
|
/*
|
|
* Action related to zoom
|
|
*/
|
|
m_zoom_ag = new QActionGroup(this);
|
|
|
|
QAction *zoom_in = new QAction(QET::Icons::ZoomIn, tr("Zoom avant"), m_zoom_ag);
|
|
QAction *zoom_out = new QAction(QET::Icons::ZoomOut, tr("Zoom arrière"), m_zoom_ag);
|
|
QAction *zoom_fit = new QAction(QET::Icons::ZoomFitBest, tr("Zoom adapté"), m_zoom_ag);
|
|
QAction *zoom_reset = new QAction(QET::Icons::ZoomOriginal, tr("Pas de zoom"), m_zoom_ag);
|
|
|
|
zoom_in -> setShortcut(QKeySequence::ZoomIn);
|
|
zoom_out -> setShortcut(QKeySequence::ZoomOut);
|
|
zoom_fit -> setShortcut(QKeySequence(tr("Ctrl+9")));
|
|
zoom_reset -> setShortcut(QKeySequence(tr("Ctrl+0")));
|
|
|
|
connect(zoom_in, SIGNAL(triggered()), m_view, SLOT(zoomIn() ));
|
|
connect(zoom_out, SIGNAL(triggered()), m_view, SLOT(zoomOut() ));
|
|
connect(zoom_fit, SIGNAL(triggered()), m_view, SLOT(zoomFit() ));
|
|
connect(zoom_reset, SIGNAL(triggered()), m_view, SLOT(zoomReset() ));
|
|
|
|
|
|
/*
|
|
* Action related to primitive creation
|
|
*/
|
|
connect (m_elmt_scene, SIGNAL(partsAdded()), this, SLOT(UncheckAddPrimitive()));
|
|
parts = new QActionGroup(this);
|
|
|
|
QAction *add_line = new QAction(QET::Icons::PartLine, tr("Ajouter une ligne"), parts);
|
|
QAction *add_rectangle = new QAction(QET::Icons::PartRectangle, tr("Ajouter un rectangle"), parts);
|
|
QAction *add_ellipse = new QAction(QET::Icons::PartEllipse, tr("Ajouter une ellipse"), parts);
|
|
QAction *add_polygon = new QAction(QET::Icons::PartPolygon, tr("Ajouter un polygone"), parts);
|
|
QAction *add_text = new QAction(QET::Icons::PartText, tr("Ajouter du texte"), parts);
|
|
QAction *add_arc = new QAction(QET::Icons::PartArc, tr("Ajouter un arc de cercle"), parts);
|
|
QAction *add_terminal = new QAction(QET::Icons::Terminal, tr("Ajouter une borne"), parts);
|
|
QAction *add_dynamic_text_field = new QAction(QET::Icons::PartTextField, tr("Ajouter un champ texte dynamique"), parts);
|
|
|
|
foreach (QAction *action, parts -> actions()) action -> setCheckable(true);
|
|
|
|
connect(add_line, SIGNAL(triggered()), this, SLOT(addLine() ));
|
|
connect(add_rectangle, SIGNAL(triggered()), this, SLOT(addRect() ));
|
|
connect(add_ellipse, SIGNAL(triggered()), this, SLOT(addEllipse() ));
|
|
connect(add_polygon, SIGNAL(triggered()), this, SLOT(addPolygon() ));
|
|
connect(add_text, SIGNAL(triggered()), this, SLOT(addText() ));
|
|
connect(add_arc, SIGNAL(triggered()), this, SLOT(addArc() ));
|
|
connect(add_terminal, SIGNAL(triggered()), this, SLOT(addTerminal() ));
|
|
connect(add_dynamic_text_field, &QAction::triggered, this, &QETElementEditor::addDynamicTextField);
|
|
|
|
add_polygon ->setStatusTip(tr("Double-click pour terminer la forme, Click droit pour annuler le dernier point"));
|
|
add_text ->setStatusTip(tr("Ajouter un texte d'élément non éditable dans les schémas"));
|
|
add_dynamic_text_field ->setStatusTip(tr("Ajouter un texte d'élément pouvant être édité dans les schémas"));
|
|
|
|
parts_toolbar = addToolBar(tr("Parties", "toolbar title"));
|
|
parts_toolbar -> setAllowedAreas(Qt::AllToolBarAreas);
|
|
parts_toolbar -> setObjectName("parts");
|
|
parts_toolbar -> addActions(parts -> actions());
|
|
addToolBar(Qt::LeftToolBarArea, parts_toolbar);
|
|
|
|
|
|
main_toolbar = new QToolBar(tr("Outils", "toolbar title"), this);
|
|
main_toolbar -> setObjectName("main_toolbar");
|
|
view_toolbar = new QToolBar(tr("Affichage", "toolbar title"), this);
|
|
view_toolbar -> setObjectName("display");
|
|
element_toolbar = new QToolBar(tr("Élément", "toolbar title"), this);
|
|
element_toolbar -> setObjectName("element_toolbar");
|
|
|
|
main_toolbar -> addAction(new_element);
|
|
main_toolbar -> addAction(open);
|
|
main_toolbar -> addAction(save);
|
|
main_toolbar -> addAction(save_as);
|
|
main_toolbar -> addAction(reload);
|
|
main_toolbar -> addSeparator();
|
|
main_toolbar -> addAction(undo);
|
|
main_toolbar -> addAction(redo);
|
|
main_toolbar -> addSeparator();
|
|
main_toolbar -> addAction(edit_delete);
|
|
|
|
view_toolbar -> addAction(zoom_fit);
|
|
view_toolbar -> addAction(zoom_reset);
|
|
|
|
element_toolbar -> addAction(edit_names);
|
|
element_toolbar -> addAction(m_edit_properties);
|
|
|
|
addToolBar(Qt::TopToolBarArea, main_toolbar);
|
|
addToolBar(Qt::TopToolBarArea, view_toolbar);
|
|
addToolBar(Qt::TopToolBarArea, element_toolbar);
|
|
|
|
connect(m_elmt_scene, SIGNAL(selectionChanged()), this, SLOT(slot_updateInformations()), Qt::QueuedConnection);
|
|
connect(m_elmt_scene, SIGNAL(selectionChanged()), this, SLOT(slot_updateMenus()));
|
|
connect(QApplication::clipboard(), SIGNAL(dataChanged()), this, SLOT(slot_updateMenus()));
|
|
connect(&(m_elmt_scene -> undoStack()), SIGNAL(cleanChanged(bool)), this, SLOT(slot_updateMenus()));
|
|
connect(&(m_elmt_scene -> undoStack()), SIGNAL(cleanChanged(bool)), this, SLOT(slot_updateTitle()));
|
|
|
|
// Annuler ou refaire une action met a jour la liste des primitives ; cela sert notamment pour les
|
|
// ajouts et suppressions de primitives ainsi que pour les actions entrainant un change
|
|
connect(&(m_elmt_scene -> undoStack()), SIGNAL(indexChanged(int)), this, SLOT(slot_updatePartsList()));
|
|
|
|
// Annuler ou refaire une action met a jour les informations affichees sur les primitives selectionnees,
|
|
// celles-ci etant potentiellement impactees
|
|
connect(&(m_elmt_scene -> undoStack()), SIGNAL(indexChanged(int)), this, SLOT(slot_updateInformations()));
|
|
}
|
|
|
|
/**
|
|
* @brief QETElementEditor::setupMenus
|
|
*/
|
|
void QETElementEditor::setupMenus() {
|
|
file_menu = new QMenu(tr("&Fichier"), this);
|
|
edit_menu = new QMenu(tr("&Édition"), this);
|
|
display_menu = new QMenu(tr("Afficha&ge"), this);
|
|
tools_menu = new QMenu(tr("O&utils"), this);
|
|
|
|
file_menu -> setTearOffEnabled(true);
|
|
edit_menu -> setTearOffEnabled(true);
|
|
display_menu -> setTearOffEnabled(true);
|
|
tools_menu -> setTearOffEnabled(true);
|
|
|
|
file_menu -> addAction(new_element);
|
|
file_menu -> addAction(open);
|
|
file_menu -> addAction(open_file);
|
|
file_menu -> addAction(open_dxf);
|
|
QMenu *recentfile = file_menu -> addMenu(QET::Icons::DocumentOpenRecent, tr("&Récemment ouverts"));
|
|
recentfile->addActions(QETApp::elementsRecentFiles()->menu()->actions());
|
|
connect(QETApp::elementsRecentFiles(), SIGNAL(fileOpeningRequested(const QString &)), this, SLOT(openRecentFile(const QString &)));
|
|
file_menu -> addAction(save);
|
|
file_menu -> addAction(save_as);
|
|
file_menu -> addAction(save_as_file);
|
|
file_menu -> addSeparator();
|
|
file_menu -> addAction(reload);
|
|
file_menu -> addSeparator();
|
|
file_menu -> addAction(quit);
|
|
|
|
paste_from_menu = new QMenu(tr("Coller depuis..."));
|
|
paste_from_menu -> setIcon(QET::Icons::EditPaste);
|
|
paste_from_menu -> addAction(paste_from_file);
|
|
paste_from_menu -> addAction(paste_from_elmt);
|
|
|
|
edit_menu -> addAction(undo);
|
|
edit_menu -> addAction(redo);
|
|
edit_menu -> addSeparator();
|
|
edit_menu -> addAction(selectall);
|
|
edit_menu -> addAction(deselectall);
|
|
edit_menu -> addAction(inv_select);
|
|
edit_menu -> addSeparator();
|
|
edit_menu -> addAction(cut);
|
|
edit_menu -> addAction(copy);
|
|
edit_menu -> addAction(paste);
|
|
edit_menu -> addAction(paste_in_area);
|
|
edit_menu -> addMenu(paste_from_menu);
|
|
edit_menu -> addSeparator();
|
|
edit_menu -> addAction(edit_delete);
|
|
edit_menu -> addSeparator();
|
|
edit_menu -> addAction(edit_names);
|
|
edit_menu -> addAction(edit_author);
|
|
edit_menu -> addAction(m_edit_properties);
|
|
edit_menu -> addSeparator();
|
|
edit_menu -> addActions(m_depth_action_group -> actions());
|
|
|
|
display_menu -> addActions(m_zoom_ag -> actions());
|
|
|
|
insertMenu(settings_menu_, file_menu);
|
|
insertMenu(settings_menu_, edit_menu);
|
|
insertMenu(settings_menu_, display_menu);
|
|
}
|
|
|
|
/**
|
|
* @brief QETElementEditor::contextMenu
|
|
* Display a context menu, with all available action.
|
|
* @param p, the pos of the menu, in screen coordinate
|
|
* @param actions, a list of actions who can be prepended to the context menu.
|
|
*/
|
|
void QETElementEditor::contextMenu(QPoint p, QList<QAction *> actions)
|
|
{
|
|
QMenu menu(this);
|
|
menu.addActions(std::move(actions));
|
|
menu.addSeparator();
|
|
menu.addAction(undo);
|
|
menu.addAction(redo);
|
|
menu.addAction(selectall);
|
|
menu.addAction(deselectall);
|
|
menu.addAction(inv_select);
|
|
menu.addSeparator();
|
|
menu.addAction(edit_delete);
|
|
menu.addAction(cut);
|
|
menu.addAction(copy);
|
|
menu.addSeparator();
|
|
menu.addAction(paste);
|
|
menu.addAction(paste_in_area);
|
|
menu.addMenu(paste_from_menu);
|
|
menu.addSeparator();
|
|
menu.addActions(m_depth_action_group -> actions());
|
|
|
|
//Remove from the context menu the actions which are disabled.
|
|
const QList<QAction *>menu_actions = menu.actions();
|
|
for(QAction *action : menu_actions)
|
|
{
|
|
if(!action->isEnabled())
|
|
menu.removeAction(action);
|
|
}
|
|
menu.exec(p);
|
|
}
|
|
|
|
|
|
/**
|
|
Met a jour les menus
|
|
*/
|
|
void QETElementEditor::slot_updateMenus() {
|
|
bool selected_items = !read_only && !m_elmt_scene -> selectedItems().isEmpty();
|
|
bool clipboard_elmt = !read_only && ElementScene::clipboardMayContainElement();
|
|
|
|
// actions dependant seulement de l'etat "lecture seule" de l'editeur
|
|
foreach (QAction *action, parts -> actions()) {
|
|
action -> setEnabled(!read_only);
|
|
}
|
|
selectall -> setEnabled(!read_only);
|
|
inv_select -> setEnabled(!read_only);
|
|
paste_from_file -> setEnabled(!read_only);
|
|
paste_from_elmt -> setEnabled(!read_only);
|
|
m_parts_list -> setEnabled(!read_only);
|
|
|
|
// Action enabled if primitive selected
|
|
deselectall -> setEnabled(selected_items);
|
|
cut -> setEnabled(selected_items);
|
|
copy -> setEnabled(selected_items);
|
|
edit_delete -> setEnabled(selected_items);
|
|
foreach (QAction *action, m_depth_action_group -> actions())
|
|
action->setEnabled(selected_items);
|
|
|
|
// actions dependant du contenu du presse-papiers
|
|
paste -> setEnabled(clipboard_elmt);
|
|
paste_in_area -> setEnabled(clipboard_elmt);
|
|
|
|
// actions dependant de l'etat de la pile d'annulation
|
|
save -> setEnabled(!read_only && !m_elmt_scene -> undoStack().isClean());
|
|
undo -> setEnabled(!read_only && m_elmt_scene -> undoStack().canUndo());
|
|
redo -> setEnabled(!read_only && m_elmt_scene -> undoStack().canRedo());
|
|
}
|
|
|
|
/**
|
|
Met a jour le titre de la fenetre
|
|
*/
|
|
void QETElementEditor::slot_updateTitle() {
|
|
QString title = min_title;
|
|
title += " - " + m_elmt_scene -> names().name() + " ";
|
|
if (!filename_.isEmpty() || !location_.isNull()) {
|
|
if (!m_elmt_scene -> undoStack().isClean()) title += tr("[Modifié]", "window title tag");
|
|
}
|
|
if (isReadOnly()) title += tr(" [lecture seule]", "window title tag");
|
|
setWindowTitle(title);
|
|
}
|
|
|
|
/**
|
|
* @brief QETElementEditor::setupInterface
|
|
*/
|
|
void QETElementEditor::setupInterface()
|
|
{
|
|
// editeur
|
|
m_elmt_scene = new ElementScene(this, this);
|
|
m_view = new ElementView(m_elmt_scene, this);
|
|
slot_setRubberBandToView();
|
|
setCentralWidget(m_view);
|
|
|
|
// widget par defaut dans le QDockWidget
|
|
m_default_informations = new QLabel();
|
|
|
|
// ScrollArea pour accueillir un widget d'edition (change a la volee)
|
|
// m_tools_dock_scroll_area = new QScrollArea();
|
|
// m_tools_dock_scroll_area -> setFrameStyle(QFrame::NoFrame);
|
|
// m_tools_dock_scroll_area -> setAlignment(Qt::AlignHCenter|Qt::AlignTop);
|
|
|
|
// Pile de widgets pour accueillir les deux widgets precedents
|
|
m_tools_dock_stack = new QStackedWidget();
|
|
m_tools_dock_stack -> insertWidget(0, m_default_informations);
|
|
// m_tools_dock_stack -> insertWidget(1, m_tools_dock_scroll_area);
|
|
|
|
// widgets d'editions pour les parties
|
|
m_editors["arc"] = new ArcEditor(this);
|
|
m_editors["ellipse"] = new EllipseEditor(this);
|
|
m_editors["line"] = new LineEditor(this);
|
|
m_editors["polygon"] = new PolygonEditor(this);
|
|
m_editors["rect"] = new RectangleEditor(this);
|
|
m_editors["terminal"] = new TerminalEditor(this);
|
|
m_editors["text"] = new TextEditor(this);
|
|
m_editors["style"] = new StyleEditor(this);
|
|
m_editors["dynamic_text"] = new DynamicTextFieldEditor(this);
|
|
|
|
// panel sur le cote pour editer les parties
|
|
m_tools_dock = new QDockWidget(tr("Informations", "dock title"), this);
|
|
m_tools_dock -> setObjectName("informations");
|
|
m_tools_dock -> setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
|
|
m_tools_dock -> setFeatures(QDockWidget::AllDockWidgetFeatures);
|
|
//m_tools_dock -> setMinimumWidth(380);
|
|
addDockWidget(Qt::RightDockWidgetArea, m_tools_dock);
|
|
m_tools_dock -> setWidget(m_tools_dock_stack);
|
|
|
|
// panel sur le cote pour les annulations
|
|
m_undo_dock = new QDockWidget(tr("Annulations", "dock title"), this);
|
|
m_undo_dock -> setObjectName("undo");
|
|
m_undo_dock -> setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
|
|
m_undo_dock -> setFeatures(QDockWidget::AllDockWidgetFeatures);
|
|
m_undo_dock -> setMinimumWidth(290);
|
|
addDockWidget(Qt::RightDockWidgetArea, m_undo_dock);
|
|
QUndoView* undo_view = new QUndoView(&(m_elmt_scene -> undoStack()), this);
|
|
undo_view -> setEmptyLabel(tr("Aucune modification"));
|
|
m_undo_dock -> setWidget(undo_view);
|
|
|
|
// panel sur le cote pour la liste des parties
|
|
m_parts_list = new QListWidget(this);
|
|
m_parts_list -> setSelectionMode(QAbstractItemView::ExtendedSelection);
|
|
connect(m_elmt_scene, SIGNAL(partsAdded()), this, SLOT(slot_createPartsList()));
|
|
connect(m_elmt_scene, SIGNAL(partsRemoved()), this, SLOT(slot_createPartsList()));
|
|
connect(m_elmt_scene, SIGNAL(partsZValueChanged()), this, SLOT(slot_createPartsList()));
|
|
connect(m_elmt_scene, SIGNAL(selectionChanged()), this, SLOT(slot_updatePartsList()));
|
|
connect(m_parts_list, SIGNAL(itemSelectionChanged()), this, SLOT(slot_updateSelectionFromPartsList()));
|
|
m_parts_dock = new QDockWidget(tr("Parties", "dock title"), this);
|
|
m_parts_dock -> setObjectName("parts_list");
|
|
m_parts_dock -> setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
|
|
m_parts_dock -> setFeatures(QDockWidget::AllDockWidgetFeatures);
|
|
m_parts_dock -> setMinimumWidth(290);
|
|
tabifyDockWidget(m_undo_dock, m_parts_dock);
|
|
m_parts_dock -> setWidget(m_parts_list);
|
|
|
|
slot_updateInformations();
|
|
slot_createPartsList();
|
|
|
|
// barre d'etat
|
|
statusBar() -> showMessage(tr("Éditeur d'éléments", "status bar message"));
|
|
}
|
|
|
|
/**
|
|
Passe l'editeur d'element en mode selection : le pointeur deplace les
|
|
elements selectionnes et il est possible d'utiliser un rectangle de selection.
|
|
*/
|
|
void QETElementEditor::slot_setRubberBandToView() {
|
|
m_view -> setDragMode(QGraphicsView::RubberBandDrag);
|
|
}
|
|
|
|
/**
|
|
Passe l'editeur d'element en mode immobile (utilise pour la lecture seule)
|
|
*/
|
|
void QETElementEditor::slot_setNoDragToView() {
|
|
m_view -> setDragMode(QGraphicsView::NoDrag);
|
|
}
|
|
|
|
/**
|
|
Met a jour la zone d'information et d'edition des primitives.
|
|
Si plusieurs primitives sont selectionnees, seule leur quantite est
|
|
affichee. Sinon, un widget d'edition approprie est mis en place.
|
|
*/
|
|
void QETElementEditor::slot_updateInformations()
|
|
{
|
|
QList<QGraphicsItem *> selected_qgis = m_elmt_scene -> selectedItems();
|
|
if (selected_qgis.isEmpty()) {
|
|
clearToolsDock();
|
|
m_default_informations -> setText(tr("%n partie(s) sélectionnée(s).",
|
|
"",
|
|
selected_qgis.size()));
|
|
m_default_informations -> setAlignment(Qt::AlignHCenter | Qt::AlignTop);
|
|
m_tools_dock_stack -> setCurrentIndex(0);
|
|
return;
|
|
}
|
|
|
|
QList<CustomElementPart *> cep_list;
|
|
|
|
CustomElementPart* part = dynamic_cast<CustomElementPart *>(selected_qgis.first());
|
|
QString selection_xml_name = part->xmlName();
|
|
bool same_xml_name = true;
|
|
bool style_editable = true;
|
|
for (QGraphicsItem *qgi: selected_qgis) {
|
|
if (CustomElementPart *cep = dynamic_cast<CustomElementPart *>(qgi)) {
|
|
cep_list << cep;
|
|
if (cep->xmlName() != selection_xml_name)
|
|
same_xml_name = false;
|
|
} else {
|
|
style_editable = false;
|
|
same_xml_name = false;
|
|
}
|
|
}
|
|
if (style_editable)
|
|
style_editable = StyleEditor::isStyleEditable(cep_list);
|
|
|
|
if (same_xml_name) {
|
|
if (selection_xml_name == "terminal" ||
|
|
selection_xml_name == "text" ||
|
|
selection_xml_name == "dynamic_text") {
|
|
clearToolsDock();
|
|
//We add the editor widget
|
|
ElementItemEditor *editor = static_cast<ElementItemEditor*>(m_editors[selection_xml_name]);
|
|
|
|
// TODO: Check if it takes longer than setting the parts again to the editor.
|
|
bool equal = true;
|
|
QList<CustomElementPart*> parts = editor->currentParts();
|
|
if (parts.length() == cep_list.length()) {
|
|
for (auto cep: cep_list) {
|
|
bool part_found = false;
|
|
for (auto part: parts) {
|
|
if (part == cep) {
|
|
part_found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!part_found) {
|
|
equal = false;
|
|
break;
|
|
}
|
|
}
|
|
} else
|
|
equal = false;
|
|
|
|
if (editor)
|
|
{
|
|
bool success = true;
|
|
if (equal == false) {
|
|
success = editor->setParts(cep_list);
|
|
}
|
|
if (success)
|
|
{
|
|
m_tools_dock_stack->insertWidget(1, editor);
|
|
m_tools_dock_stack -> setCurrentIndex(1);
|
|
}
|
|
else
|
|
{
|
|
qDebug() << "Editor refused part.";
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
//There's several parts selecteds and all can be edited by style editor.
|
|
if (style_editable)
|
|
{
|
|
clearToolsDock();
|
|
ElementItemEditor *selection_editor = m_editors["style"];
|
|
if (selection_editor)
|
|
{
|
|
if (selection_editor -> setParts(cep_list))
|
|
{
|
|
m_tools_dock_stack->insertWidget(1, selection_editor);
|
|
m_tools_dock_stack -> setCurrentIndex(1);
|
|
}
|
|
else
|
|
{
|
|
qDebug() << "Editor refused part.";
|
|
}
|
|
}
|
|
}
|
|
//Else we only display the number of selected items
|
|
else
|
|
{
|
|
clearToolsDock();
|
|
m_default_informations -> setText(tr("%n partie(s) sélectionnée(s).",
|
|
"",
|
|
selected_qgis.size()));
|
|
m_default_informations -> setAlignment(Qt::AlignHCenter | Qt::AlignTop);
|
|
m_tools_dock_stack -> setCurrentIndex(0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief QETElementEditor::checkElement
|
|
* Do several check about element.
|
|
* If error is occurred return false
|
|
*/
|
|
bool QETElementEditor::checkElement()
|
|
{
|
|
//List of warning and error
|
|
typedef QPair<QString, QString> QETWarning;
|
|
QList<QETWarning> warnings;
|
|
QList<QETWarning> errors;
|
|
|
|
/// Warning #1: Element haven't got terminal
|
|
/// (except for report, because report must have one terminal and this checking is do below)
|
|
if (!m_elmt_scene -> containsTerminals() && !m_elmt_scene -> elementType().contains("report"))
|
|
{
|
|
warnings << qMakePair(
|
|
tr("Absence de borne", "warning title"),
|
|
tr(
|
|
"<br>En l'absence de borne, l'élément ne pourra être"
|
|
" relié à d'autres éléments par l'intermédiaire de conducteurs.",
|
|
"warning description"
|
|
)
|
|
);
|
|
}
|
|
|
|
/// Check folio report element
|
|
if (m_elmt_scene -> elementType().contains("report"))
|
|
{
|
|
int terminal =0;
|
|
|
|
foreach(QGraphicsItem *qgi, m_elmt_scene->items())
|
|
if (qgraphicsitem_cast<PartTerminal *>(qgi))
|
|
terminal ++;
|
|
|
|
///Error folio report must have only one terminal
|
|
if (terminal != 1)
|
|
{
|
|
errors << qMakePair (tr("Absence de borne"),
|
|
tr("<br><b>Erreur</b> :"
|
|
"<br>Les reports de folio doivent posséder une seul borne."
|
|
"<br><b>Solution</b> :"
|
|
"<br>Verifier que l'élément ne possède qu'une seul borne"));
|
|
}
|
|
}
|
|
|
|
if (!errors.count() && !warnings.count()) return(true);
|
|
|
|
// Display warnings
|
|
QString dialog_message = tr("La vérification de cet élément a généré", "message box content");
|
|
|
|
if (errors.size())
|
|
dialog_message += QString(tr(" %n erreur(s)", "errors", errors.size()));
|
|
|
|
if (warnings.size())
|
|
{
|
|
if (errors.size())
|
|
dialog_message += QString (tr(" et"));
|
|
|
|
dialog_message += QString (tr(" %n avertissement(s)", "warnings", warnings.size()));
|
|
}
|
|
dialog_message += " :";
|
|
|
|
dialog_message += "<ol>";
|
|
QList<QETWarning> total = warnings << errors;
|
|
foreach(QETWarning warning, total) {
|
|
dialog_message += "<li>";
|
|
dialog_message += QString(
|
|
tr("<b>%1</b> : %2", "warning title: warning description")
|
|
).arg(warning.first).arg(warning.second);
|
|
dialog_message += "</li>";
|
|
}
|
|
dialog_message += "</ol>";
|
|
|
|
if (errors.size())
|
|
QMessageBox::critical(this, tr("Erreurs"), dialog_message);
|
|
else
|
|
QMessageBox::warning(this, tr("Avertissements"), dialog_message);
|
|
|
|
//if error == 0 that means they are only warning, we return true.
|
|
if (errors.count() == 0) return(true);
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
Charge un fichier
|
|
@param filepath Chemin du fichier a charger
|
|
*/
|
|
void QETElementEditor::fromFile(const QString &filepath) {
|
|
bool state = true;
|
|
QString error_message;
|
|
|
|
// le fichier doit exister
|
|
QFileInfo infos_file(filepath);
|
|
if (!infos_file.exists() || !infos_file.isFile()) {
|
|
state = false;
|
|
error_message = QString(tr("Le fichier %1 n'existe pas.", "message box content")).arg(filepath);
|
|
}
|
|
|
|
// le fichier doit etre lisible
|
|
QFile file(filepath);
|
|
if (state) {
|
|
if (!file.open(QIODevice::ReadOnly)) {
|
|
state = false;
|
|
error_message = QString(tr("Impossible d'ouvrir le fichier %1.", "message box content")).arg(filepath);
|
|
}
|
|
}
|
|
|
|
// le fichier doit etre un document XML
|
|
QDomDocument document_xml;
|
|
if (state) {
|
|
if (!document_xml.setContent(&file)) {
|
|
state = false;
|
|
error_message = tr("Ce fichier n'est pas un document XML valide", "message box content");
|
|
}
|
|
file.close();
|
|
}
|
|
|
|
if (!state) {
|
|
QET::QetMessageBox::critical(this, tr("Erreur", "toolbar title"), error_message);
|
|
return;
|
|
}
|
|
|
|
// chargement de l'element
|
|
m_elmt_scene -> fromXml(document_xml);
|
|
slot_createPartsList();
|
|
|
|
// gestion de la lecture seule
|
|
if (!infos_file.isWritable()) {
|
|
QET::QetMessageBox::warning(
|
|
this,
|
|
tr("Édition en lecture seule", "message box title"),
|
|
tr("Vous n'avez pas les privilèges nécessaires pour modifier cet élement. Il sera donc ouvert en lecture seule.", "message box content")
|
|
);
|
|
setReadOnly(true);
|
|
} else {
|
|
setReadOnly(false);
|
|
}
|
|
|
|
// memorise le fichier
|
|
setFileName(filepath);
|
|
QETApp::elementsRecentFiles() -> fileWasOpened(filepath);
|
|
slot_updateMenus();
|
|
}
|
|
|
|
/**
|
|
* @brief QETElementEditor::toFile
|
|
* Save to file the drawed element.
|
|
* @param fn : path of the file
|
|
* @return : true if succesfully save.
|
|
*/
|
|
bool QETElementEditor::toFile(const QString &fn)
|
|
{
|
|
m_elmt_scene->clearEventInterface();
|
|
m_elmt_scene->clearSelection();
|
|
UncheckAddPrimitive();
|
|
|
|
QDomDocument element_xml = m_elmt_scene->toXml();
|
|
bool writing = QET::writeXmlFile(element_xml, fn);
|
|
if (!writing) {
|
|
QET::QetMessageBox::warning(
|
|
this,
|
|
tr("Erreur", "message box title"),
|
|
tr("Impossible d'écrire dans ce fichier", "message box content")
|
|
);
|
|
}
|
|
return(writing);
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief QETElementEditor::toLocation
|
|
* Save the element to Location
|
|
* @param location : location where we must save the current element
|
|
* @return true if succesfully saved
|
|
*/
|
|
bool QETElementEditor::toLocation(const ElementsLocation &location)
|
|
{
|
|
m_elmt_scene->clearEventInterface();
|
|
m_elmt_scene->clearSelection();
|
|
UncheckAddPrimitive();
|
|
|
|
if (!location.setXml(m_elmt_scene->toXml()))
|
|
{
|
|
QET::QetMessageBox::critical(this,
|
|
tr("Erreur", "message box title"),
|
|
tr("Impossible d'enregistrer l'élément", "message box content"));
|
|
return(false);
|
|
}
|
|
return(true);
|
|
}
|
|
|
|
/**
|
|
@param provided_location Emplacement d'un element
|
|
@return true si cet editeur est en train d'editer l'element dont
|
|
l'emplacement est location, false sinon
|
|
*/
|
|
bool QETElementEditor::isEditing(const ElementsLocation &provided_location) {
|
|
if (opened_from_file) {
|
|
return(
|
|
QET::compareCanonicalFilePaths(
|
|
filename_,
|
|
QETApp::realPath(provided_location.toString())
|
|
)
|
|
);
|
|
} else {
|
|
return(provided_location == location_);
|
|
}
|
|
}
|
|
|
|
/**
|
|
@param provided_filepath Chemin d'un element sur un filesystem
|
|
@return true si cet editeur est en train d'editer l'element dont
|
|
le chemin est filepath, false sinon
|
|
*/
|
|
bool QETElementEditor::isEditing(const QString &provided_filepath) {
|
|
// determine le chemin canonique de l'element actuelle edite, si applicable
|
|
QString current_filepath;
|
|
if (opened_from_file) {
|
|
current_filepath = filename_;
|
|
} else {
|
|
current_filepath = QETApp::realPath(location_.toString());
|
|
}
|
|
|
|
return(
|
|
QET::compareCanonicalFilePaths(
|
|
current_filepath,
|
|
provided_filepath
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
specifie si l'editeur d'element doit etre en mode lecture seule
|
|
@param ro true pour activer le mode lecture seule, false pour le desactiver
|
|
*/
|
|
void QETElementEditor::setReadOnly(bool ro) {
|
|
read_only = ro;
|
|
|
|
// active / desactive les interactions avec la scene
|
|
m_view -> setInteractive(!ro);
|
|
|
|
slot_updateMenus();
|
|
}
|
|
|
|
/**
|
|
@return true si l'editeur d'element est en mode lecture seule
|
|
*/
|
|
bool QETElementEditor::isReadOnly() const {
|
|
return(read_only);
|
|
}
|
|
|
|
/**
|
|
* @brief QETElementEditor::addLine
|
|
* Set line creation interface to scene
|
|
*/
|
|
void QETElementEditor::addLine() {
|
|
m_elmt_scene -> setEventInterface(new ESEventAddLine(m_elmt_scene));
|
|
}
|
|
|
|
/**
|
|
* @brief QETElementEditor::addRect
|
|
* Set rectangle creation interface to scene
|
|
*/
|
|
void QETElementEditor::addRect() {
|
|
m_elmt_scene -> setEventInterface(new ESEventAddRect(m_elmt_scene));
|
|
}
|
|
|
|
/**
|
|
* @brief QETElementEditor::addEllipse
|
|
* Set ellipse creation interface to scene
|
|
*/
|
|
void QETElementEditor::addEllipse() {
|
|
m_elmt_scene -> setEventInterface(new ESEventAddEllipse(m_elmt_scene));
|
|
}
|
|
|
|
/**
|
|
* @brief QETElementEditor::addPolygon
|
|
* Set polygon creation interface to scene
|
|
*/
|
|
void QETElementEditor::addPolygon() {
|
|
m_elmt_scene -> setEventInterface(new ESEventAddPolygon(m_elmt_scene));
|
|
}
|
|
|
|
/**
|
|
* @brief QETElementEditor::addArc
|
|
* Set arc creation interface to scene
|
|
*/
|
|
void QETElementEditor::addArc() {
|
|
m_elmt_scene -> setEventInterface(new ESEventAddArc(m_elmt_scene));
|
|
}
|
|
|
|
/**
|
|
* @brief QETElementEditor::addText
|
|
* Set text creation interface to scene
|
|
*/
|
|
void QETElementEditor::addText() {
|
|
m_elmt_scene -> setEventInterface(new ESEventAddText(m_elmt_scene));
|
|
}
|
|
|
|
/**
|
|
* @brief QETElementEditor::addTerminal
|
|
* Set terminal creation interface to scene
|
|
*/
|
|
void QETElementEditor::addTerminal() {
|
|
m_elmt_scene -> setEventInterface(new ESEventAddTerminal(m_elmt_scene));
|
|
}
|
|
|
|
/**
|
|
* @brief QETElementEditor::addDynamicTextField
|
|
* Set dynamic text field creation interface to scene
|
|
*/
|
|
void QETElementEditor::addDynamicTextField() {
|
|
m_elmt_scene->setEventInterface(new ESEventAddDynamicTextField(m_elmt_scene));
|
|
}
|
|
|
|
/**
|
|
* @brief QETElementEditor::UncheckAddPrimitive
|
|
* Uncheck all action related to primitive
|
|
*/
|
|
void QETElementEditor::UncheckAddPrimitive() {
|
|
foreach(QAction *action, parts->actions()) action -> setChecked(false);
|
|
}
|
|
|
|
/**
|
|
Lance l'assistant de creation d'un nouvel element.
|
|
*/
|
|
void QETElementEditor::slot_new() {
|
|
NewElementWizard new_element_wizard(this);
|
|
new_element_wizard.exec();
|
|
}
|
|
|
|
/**
|
|
Ouvre un element
|
|
*/
|
|
void QETElementEditor::slot_open() {
|
|
// demande le chemin virtuel de l'element a ouvrir a l'utilisateur
|
|
ElementsLocation location = ElementDialog::getOpenElementLocation(this);
|
|
if (location.isNull()) return;
|
|
QETApp::instance() -> openElementLocations(QList<ElementsLocation>() << location);
|
|
}
|
|
|
|
/**
|
|
Ouvre un fichier
|
|
Demande un fichier a l'utilisateur et ouvre ce fichier
|
|
*/
|
|
void QETElementEditor::slot_openFile() {
|
|
// repertoire a afficher initialement dans le dialogue
|
|
QString open_dir = filename_.isEmpty() ? QETApp::customElementsDir() : QDir(filename_).absolutePath();
|
|
|
|
// demande un nom de fichier a ouvrir a l'utilisateur
|
|
QString user_filename = QETElementEditor::getOpenElementFileName(this, open_dir);
|
|
|
|
// ouvre l'element
|
|
openElement(user_filename);
|
|
}
|
|
|
|
/**
|
|
Slot utilise pour ouvrir un fichier recent.
|
|
Transfere filepath au slot openElement seulement si cet editeur est actif
|
|
@param filepath Fichier a ouvrir
|
|
@see openElement
|
|
*/
|
|
void QETElementEditor::openRecentFile(const QString &filepath) {
|
|
// small hack to prevent all element editors from trying to topen the required
|
|
// recent file at the same time
|
|
if (qApp -> activeWindow() != this) return;
|
|
openElement(filepath);
|
|
}
|
|
|
|
/**
|
|
* @brief QETElementEditor::slot_openDxf
|
|
*/
|
|
void QETElementEditor::slot_openDxf (){
|
|
|
|
#if defined(Q_OS_WIN32) || defined(Q_OS_WIN64)
|
|
QString program = (QDir::homePath() + "/Application Data/qet/DXFtoQET.exe");
|
|
#elif defined(Q_OS_MAC)
|
|
QString program = (QDir::homePath() + "/.qet/DXFtoQET.app");
|
|
#else
|
|
QString program = (QDir::homePath() + "/.qet/DXFtoQET");
|
|
#endif
|
|
QStringList arguments;
|
|
QProcess *DXF = new QProcess(qApp);
|
|
DXF->start(program,arguments);
|
|
|
|
}
|
|
|
|
/**
|
|
Ouvre un fichier element dans un nouvel editeur
|
|
Cette methode ne controle pas si le fichier est deja ouvert
|
|
@param filepath Fichier a ouvrir
|
|
@see fromFile
|
|
@see QETApp::openElementFiles
|
|
*/
|
|
void QETElementEditor::openElement(const QString &filepath) {
|
|
if (filepath.isEmpty()) return;
|
|
// we have to test the file existence here because QETApp::openElementFiles()
|
|
// will discard non-existent files through QFileInfo::canonicalFilePath()
|
|
if (!QFile::exists(filepath)) {
|
|
QET::QetMessageBox::critical(
|
|
this,
|
|
tr("Impossible d'ouvrir le fichier", "message box title"),
|
|
QString(
|
|
tr("Il semblerait que le fichier %1 que vous essayez d'ouvrir"
|
|
" n'existe pas ou plus.")
|
|
).arg(filepath)
|
|
);
|
|
}
|
|
QETApp::instance() -> openElementFiles(QStringList() << filepath);
|
|
}
|
|
|
|
/**
|
|
* @brief QETElementEditor::slot_reload
|
|
* Reload the element from the file or location
|
|
*/
|
|
void QETElementEditor::slot_reload()
|
|
{
|
|
//If user already edit the element, ask confirmation to reload
|
|
if (!m_elmt_scene -> undoStack().isClean())
|
|
{
|
|
QMessageBox::StandardButton answer = QET::QetMessageBox::question(this,
|
|
tr("Recharger l'élément", "dialog title"),
|
|
tr("Vous avez efffectué des modifications sur cet élément. Si vous le rechargez, ces modifications seront perdues. Voulez-vous vraiment recharger l'élément ?", "dialog content"),
|
|
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
|
|
QMessageBox::Cancel);
|
|
if (answer != QMessageBox::Yes) return;
|
|
}
|
|
|
|
//Reload the element
|
|
m_elmt_scene -> reset();
|
|
if (opened_from_file)
|
|
fromFile(filename_);
|
|
else
|
|
fromLocation(location_);
|
|
}
|
|
|
|
/**
|
|
* @brief QETElementEditor::slot_save
|
|
* Save the current editing element.
|
|
* If the filepath or location is unknown, use save_as instead
|
|
* @return true if save with success
|
|
*/
|
|
bool QETElementEditor::slot_save()
|
|
{
|
|
// Check element befor writing
|
|
if (checkElement())
|
|
{
|
|
//If we don't know the name of the current file, use save as instead
|
|
if (opened_from_file)
|
|
{
|
|
if (filename_.isEmpty())
|
|
return(slot_saveAsFile());
|
|
|
|
//Else wa save to the file at filename_ path
|
|
bool result_save = toFile(filename_);
|
|
if (result_save) m_elmt_scene -> undoStack().setClean();
|
|
return(result_save);
|
|
}
|
|
else
|
|
{
|
|
if (location_.isNull())
|
|
return(slot_saveAs());
|
|
|
|
//Else save to the known location
|
|
bool result_save = toLocation(location_);
|
|
if (result_save) {
|
|
m_elmt_scene -> undoStack().setClean();
|
|
emit saveToLocation(location_);
|
|
}
|
|
return(result_save);
|
|
}
|
|
}
|
|
|
|
QMessageBox::critical(this, tr("Echec de l'enregistrement"), tr("L'enregistrement à échoué,\nles conditions requises ne sont pas valides"));
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @brief QETElementEditor::slot_saveAs
|
|
* Ask a location to user and save the current edited element
|
|
* to this location
|
|
* @return true if save with success
|
|
*/
|
|
bool QETElementEditor::slot_saveAs()
|
|
{
|
|
// Check element befor writing
|
|
if (checkElement())
|
|
{
|
|
//Ask a location to user
|
|
ElementsLocation location = ElementDialog::getSaveElementLocation(this);
|
|
if (location.isNull())
|
|
return(false);
|
|
|
|
bool result_save = toLocation(location);
|
|
if (result_save)
|
|
{
|
|
setLocation(location);
|
|
m_elmt_scene->undoStack().setClean();
|
|
emit saveToLocation(location);
|
|
}
|
|
|
|
return(result_save);
|
|
}
|
|
QMessageBox::critical(this, tr("Echec de l'enregistrement"), tr("L'enregistrement à échoué,\nles conditions requises ne sont pas valides"));
|
|
return (false);
|
|
}
|
|
|
|
/**
|
|
* @brief QETElementEditor::slot_saveAsFile
|
|
* Ask a file to user and save the current edited element to this file
|
|
* @return true if save with success
|
|
*/
|
|
bool QETElementEditor::slot_saveAsFile()
|
|
{
|
|
// Check element befor writing
|
|
if (checkElement())
|
|
{
|
|
//Ask a filename to user, for save the element
|
|
QString fn = QFileDialog::getSaveFileName(
|
|
this,
|
|
tr("Enregistrer sous", "dialog title"),
|
|
filename_.isEmpty() ? QETApp::customElementsDir() : QDir(filename_).absolutePath(),
|
|
tr(
|
|
"Éléments QElectroTech (*.elmt)",
|
|
"filetypes allowed when saving an element file"
|
|
)
|
|
);
|
|
|
|
if (fn.isEmpty())
|
|
return(false);
|
|
|
|
//If the name doesn't end by .elmt, we add it
|
|
if (!fn.endsWith(".elmt", Qt::CaseInsensitive))
|
|
fn += ".elmt";
|
|
|
|
bool result_save = toFile(fn);
|
|
//If the save success, the filename is keep
|
|
if (result_save)
|
|
{
|
|
setFileName(fn);
|
|
QETApp::elementsRecentFiles() -> fileWasOpened(fn);
|
|
m_elmt_scene -> undoStack().setClean();
|
|
}
|
|
|
|
return(result_save);
|
|
}
|
|
QMessageBox::critical(this, tr("Echec de l'enregistrement"), tr("L'enregistrement à échoué,\nles conditions requises ne sont pas valides"));
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
@return true si l'element peut etre ferme.
|
|
Un element peut etre ferme s'il ne comporte aucune modification.
|
|
Si l'element comporte des modifications, la question est posee a
|
|
l'utilisateur.
|
|
*/
|
|
bool QETElementEditor::canClose() {
|
|
if (m_elmt_scene -> undoStack().isClean()) return(true);
|
|
// demande d'abord a l'utilisateur s'il veut enregistrer l'element en cours
|
|
QMessageBox::StandardButton answer = QET::QetMessageBox::question(
|
|
this,
|
|
tr("Enregistrer l'élément en cours ?", "dialog title"),
|
|
QString(
|
|
tr(
|
|
"Voulez-vous enregistrer l'élément %1 ?",
|
|
"dialog content - %1 is an element name"
|
|
)
|
|
).arg(m_elmt_scene -> names().name()),
|
|
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
|
|
QMessageBox::Cancel
|
|
);
|
|
bool result;
|
|
switch(answer) {
|
|
case QMessageBox::Cancel: result = false; break; // l'utilisateur annule : echec de la fermeture
|
|
case QMessageBox::Yes: result = slot_save(); break; // l'utilisateur dit oui : la reussite depend de l'enregistrement
|
|
default: result = true; // l'utilisateur dit non ou ferme le dialogue: c'est reussi
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
/**
|
|
Enleve et cache le widget affiche par le dock permettant d'editer les
|
|
parties.
|
|
@return le widget enleve, ou 0 s'il n'y avait pas de widget a enlever
|
|
*/
|
|
QWidget *QETElementEditor::clearToolsDock() {
|
|
if (QWidget *previous_widget = m_tools_dock_stack->widget(1))
|
|
{
|
|
m_tools_dock_stack->removeWidget(previous_widget);
|
|
previous_widget -> setParent(nullptr);
|
|
previous_widget -> hide();
|
|
return(previous_widget);
|
|
}
|
|
return(nullptr);
|
|
}
|
|
|
|
/**
|
|
Exporte le document XML xml_document vers le presse-papier puis declenche
|
|
son collage dans l'editeur courant, avec selection de la zone de collage
|
|
@param xml_document Document XML a copier/coller
|
|
@see ElementView::pasteInArea
|
|
*/
|
|
void QETElementEditor::copyAndPasteXml(const QDomDocument &xml_document) {
|
|
// accede au presse-papier
|
|
QClipboard *clipboard = QApplication::clipboard();
|
|
|
|
// genere la description XML de la selection
|
|
QString clipboard_content = xml_document.toString(4);
|
|
|
|
// met la description XML dans le presse-papier
|
|
if (clipboard -> supportsSelection()) {
|
|
clipboard -> setText(clipboard_content, QClipboard::Selection);
|
|
}
|
|
clipboard -> setText(clipboard_content);
|
|
|
|
m_view -> pasteInArea();
|
|
}
|
|
|
|
/**
|
|
Permet de quitter l'editeur lors de la fermeture de la fenetre principale
|
|
@param qce Le QCloseEvent correspondant a l'evenement de fermeture
|
|
*/
|
|
void QETElementEditor::closeEvent(QCloseEvent *qce) {
|
|
if (canClose()) {
|
|
writeSettings();
|
|
setAttribute(Qt::WA_DeleteOnClose);
|
|
m_elmt_scene -> reset();
|
|
qce -> accept();
|
|
} else qce -> ignore();
|
|
}
|
|
|
|
/**
|
|
Executed the first time the window editor is displayed.
|
|
*/
|
|
void QETElementEditor::firstActivation(QEvent *event) {
|
|
Q_UNUSED(event)
|
|
QTimer::singleShot(250, m_view, SLOT(zoomFit()));
|
|
}
|
|
|
|
/**
|
|
Remplit la liste des parties
|
|
*/
|
|
void QETElementEditor::slot_createPartsList() {
|
|
m_parts_list -> blockSignals(true);
|
|
m_parts_list -> clear();
|
|
QList<QGraphicsItem *> qgis = m_elmt_scene -> zItems();
|
|
|
|
// on ne construit plus la liste a partir de 200 primitives
|
|
// c'est ingerable : la maj de la liste prend trop de temps et le resultat
|
|
// est inexploitable
|
|
if (qgis.count() <= QET_MAX_PARTS_IN_ELEMENT_EDITOR_LIST) {
|
|
for (int j = qgis.count() - 1 ; j >= 0 ; -- j) {
|
|
QGraphicsItem *qgi = qgis[j];
|
|
if (CustomElementPart *cep = dynamic_cast<CustomElementPart *>(qgi)) {
|
|
QString part_desc = cep -> name();
|
|
QListWidgetItem *qlwi = new QListWidgetItem(part_desc);
|
|
QVariant v;
|
|
v.setValue<QGraphicsItem *>(qgi);
|
|
qlwi -> setData(42, v);
|
|
m_parts_list -> addItem(qlwi);
|
|
qlwi -> setSelected(qgi -> isSelected());
|
|
}
|
|
}
|
|
} else {
|
|
m_parts_list -> addItem(new QListWidgetItem(tr("Trop de primitives, liste non générée.")));
|
|
}
|
|
m_parts_list -> blockSignals(false);
|
|
}
|
|
|
|
/**
|
|
Met a jour la selection dans la liste des parties
|
|
*/
|
|
void QETElementEditor::slot_updatePartsList() {
|
|
int items_count = m_elmt_scene -> items().count();
|
|
if (m_parts_list -> count() != items_count) {
|
|
slot_createPartsList();
|
|
} else if (items_count <= QET_MAX_PARTS_IN_ELEMENT_EDITOR_LIST) {
|
|
m_parts_list -> blockSignals(true);
|
|
int i = 0;
|
|
QList<QGraphicsItem *> items = m_elmt_scene -> zItems();
|
|
for (int j = items.count() - 1 ; j >= 0 ; -- j) {
|
|
QGraphicsItem *qgi = items[j];
|
|
QListWidgetItem *qlwi = m_parts_list -> item(i);
|
|
if (qlwi) qlwi -> setSelected(qgi -> isSelected());
|
|
++ i;
|
|
}
|
|
m_parts_list -> blockSignals(false);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Met a jour la selection des parties de l'element a partir de la liste des
|
|
parties
|
|
*/
|
|
void QETElementEditor::slot_updateSelectionFromPartsList() {
|
|
m_elmt_scene -> blockSignals(true);
|
|
m_parts_list -> blockSignals(true);
|
|
for (int i = 0 ; i < m_parts_list -> count() ; ++ i) {
|
|
QListWidgetItem *qlwi = m_parts_list -> item(i);
|
|
QGraphicsItem *qgi = qlwi -> data(42).value<QGraphicsItem *>();
|
|
if (qgi) {
|
|
qgi -> setSelected(qlwi -> isSelected());
|
|
}
|
|
}
|
|
m_parts_list -> blockSignals(false);
|
|
m_elmt_scene -> blockSignals(false);
|
|
slot_updateInformations();
|
|
slot_updateMenus();
|
|
}
|
|
|
|
/**
|
|
* @brief QETElementEditor::readSettings
|
|
* Read settings
|
|
*/
|
|
void QETElementEditor::readSettings()
|
|
{
|
|
QSettings settings;
|
|
|
|
// dimensions et position de la fenetre
|
|
QVariant geometry = settings.value("elementeditor/geometry");
|
|
if (geometry.isValid()) restoreGeometry(geometry.toByteArray());
|
|
|
|
// etat de la fenetre (barres d'outils, docks...)
|
|
QVariant state = settings.value("elementeditor/state");
|
|
if (state.isValid()) restoreState(state.toByteArray());
|
|
|
|
// informations complementaires de l'element : valeur par defaut
|
|
m_elmt_scene -> setInformations(settings.value("elementeditor/default-informations", "").toString());
|
|
}
|
|
|
|
/**
|
|
* @brief QETElementEditor::writeSettings
|
|
* Write the settings
|
|
*/
|
|
void QETElementEditor::writeSettings()
|
|
{
|
|
QSettings settings;
|
|
settings.setValue("elementeditor/geometry", saveGeometry());
|
|
settings.setValue("elementeditor/state", saveState());
|
|
}
|
|
|
|
/**
|
|
@return les decalages horizontaux et verticaux (sous la forme d'un point) a
|
|
utiliser lors d'un copier/coller avec decalage.
|
|
*/
|
|
QPointF QETElementEditor::pasteOffset() {
|
|
QPointF paste_offset(5.0, 0.0);
|
|
return(paste_offset);
|
|
}
|
|
|
|
/**
|
|
Demande a l'utilisateur d'ouvrir un fichier sense etre un element.
|
|
@param parent QWidget parent du dialogue d'ouverture de fichier
|
|
@param initial_dir Repertoire a afficher initialement - si une chaine vide
|
|
est fournie, QETApp::customElementsDir() sera utilise.
|
|
@return Le chemin du fichier choisi ou une chaine vide si l'utilisateur a
|
|
clique sur le bouton "Annuler".
|
|
@see QETApp::customElementsDir()
|
|
*/
|
|
QString QETElementEditor::getOpenElementFileName(QWidget *parent, const QString &initial_dir) {
|
|
// demande un nom de fichier a ouvrir a l'utilisateur
|
|
QString user_filename = QFileDialog::getOpenFileName(
|
|
parent,
|
|
tr("Ouvrir un fichier", "dialog title"),
|
|
initial_dir.isEmpty() ? QETApp::customElementsDir() : initial_dir,
|
|
tr(
|
|
"Éléments QElectroTech (*.elmt);;"
|
|
"Fichiers XML (*.xml);;"
|
|
"Tous les fichiers (*)",
|
|
"filetypes allowed when opening an element file"
|
|
)
|
|
);
|
|
return(user_filename);
|
|
}
|
|
|
|
/**
|
|
* @brief QETElementEditor::fromLocation
|
|
* Location of the element to edit
|
|
* @param location
|
|
*/
|
|
void QETElementEditor::fromLocation(const ElementsLocation &location)
|
|
{
|
|
if (!location.isElement())
|
|
{
|
|
QET::QetMessageBox::critical(this,
|
|
tr("Élément inexistant.", "message box title"),
|
|
tr("Le chemin virtuel choisi ne correspond pas à un élément.", "message box content"));
|
|
return;
|
|
}
|
|
if (!location.exist())
|
|
{
|
|
QET::QetMessageBox::critical(this,
|
|
tr("Élément inexistant.", "message box title"),
|
|
tr("L'élément n'existe pas.", "message box content"));
|
|
return;
|
|
}
|
|
|
|
//The file must be an xml document
|
|
QDomDocument document_xml;
|
|
QDomNode node = document_xml.importNode(location.xml(), true);
|
|
document_xml.appendChild(node);
|
|
|
|
//Load the element
|
|
m_elmt_scene -> fromXml(document_xml);
|
|
slot_createPartsList();
|
|
|
|
//location is read only
|
|
if (!location.isWritable())
|
|
{
|
|
QET::QetMessageBox::warning(this,
|
|
tr("Édition en lecture seule", "message box title"),
|
|
tr("Vous n'avez pas les privilèges nécessaires pour modifier cet élement. Il sera donc ouvert en lecture seule.", "message box content"));
|
|
setReadOnly(true);
|
|
}
|
|
else {
|
|
setReadOnly(false);
|
|
}
|
|
|
|
setLocation(location);
|
|
slot_updateMenus();
|
|
}
|
|
|
|
/**
|
|
Demande un fichier a l'utilisateur, l'ouvre en tant que fichier element,
|
|
met son contenu dans le presse-papiers, et appelle ElementView::PasteInArea
|
|
*/
|
|
void QETElementEditor::pasteFromFile() {
|
|
// demande le chemin du fichier a ouvrir a l'utilisateur
|
|
QString element_file_path = getOpenElementFileName(this);
|
|
if (element_file_path.isEmpty()) return;
|
|
|
|
QString error_message;
|
|
QDomDocument xml_document;
|
|
QFile element_file(element_file_path);
|
|
// le fichier doit etre lisible
|
|
if (!element_file.open(QIODevice::ReadOnly)) {
|
|
error_message = QString(tr("Impossible d'ouvrir le fichier %1.", "message box content")).arg(element_file_path);
|
|
} else {
|
|
// le fichier doit etre un document XML
|
|
if (!xml_document.setContent(&element_file)) {
|
|
error_message = tr("Ce fichier n'est pas un document XML valide", "message box content");
|
|
}
|
|
element_file.close();
|
|
}
|
|
|
|
if (!error_message.isEmpty()) {
|
|
QET::QetMessageBox::critical(this, tr("Erreur", "toolbar title"), error_message);
|
|
}
|
|
copyAndPasteXml(xml_document);
|
|
}
|
|
|
|
/**
|
|
* @brief QETElementEditor::pasteFromElement
|
|
* Ask an element to user, copy the xml definition of the element
|
|
* to the clipboard and call ElementView::PasteInArea
|
|
*/
|
|
void QETElementEditor::pasteFromElement()
|
|
{
|
|
//Ask for a location
|
|
ElementsLocation location = ElementDialog::getOpenElementLocation(this);
|
|
if (location.isNull())
|
|
return;
|
|
|
|
if (!location.isElement())
|
|
{
|
|
QET::QetMessageBox::critical(this,
|
|
tr("Élément inexistant.", "message box title"),
|
|
tr("Le chemin virtuel choisi ne correspond pas à un élément.", "message box content"));
|
|
return;
|
|
}
|
|
if (!location.exist())
|
|
{
|
|
QET::QetMessageBox::critical(this,
|
|
tr("Élément inexistant.", "message box title"),
|
|
tr("L'élément n'existe pas.", "message box content"));
|
|
return;
|
|
}
|
|
|
|
//Create an xml document from the location xml
|
|
QDomDocument document_xml;
|
|
QDomNode node = document_xml.importNode(location.xml(), true);
|
|
document_xml.appendChild(node);
|
|
|
|
copyAndPasteXml(document_xml);
|
|
}
|
|
|
|
/**
|
|
Met a jour l'editeur de primitive actuellement visible.
|
|
Si aucun editeur de primitive n'est visible, ce slot ne fait rien.
|
|
*/
|
|
void QETElementEditor::updateCurrentPartEditor() {
|
|
// si aucun widget d'edition n'est affiche, on ne fait rien
|
|
if (!m_tools_dock_stack -> currentIndex()) return;
|
|
|
|
// s'il y a un widget d'edition affiche, on le met a jour
|
|
if (ElementItemEditor *current_editor = dynamic_cast<ElementItemEditor *>(m_tools_dock_stack->widget(1))) {
|
|
current_editor -> updateForm();
|
|
}
|
|
}
|