mirror of https://github.com/CGAL/cgal
3758 lines
120 KiB
C++
3758 lines
120 KiB
C++
|
|
#ifdef CGAL_USE_SSH
|
|
# include "CGAL/Use_ssh.h"
|
|
#endif
|
|
#include <cmath>
|
|
|
|
#include "config.h"
|
|
#include "MainWindow.h"
|
|
#include "Scene.h"
|
|
#include <CGAL/Three/Scene_item.h>
|
|
#include <CGAL/Three/TextRenderer.h>
|
|
#include <CGAL/Three/exceptions.h>
|
|
#include <CGAL/Three/Three.h>
|
|
#include <CGAL/Qt/debug.h>
|
|
#include <CGAL/double.h>
|
|
|
|
#include <QJsonArray>
|
|
#include <QtDebug>
|
|
#include <QFileDialog>
|
|
#include <QFileInfo>
|
|
#include <QHeaderView>
|
|
#include <QMenu>
|
|
#include <QMenuBar>
|
|
#include <QChar>
|
|
#include <QAction>
|
|
#include <QShortcut>
|
|
#include <QKeySequence>
|
|
#include <QLibrary>
|
|
#include <QPluginLoader>
|
|
#include <QMessageBox>
|
|
#include <QScrollBar>
|
|
#include <QColor>
|
|
#include <QColorDialog>
|
|
#include <QClipboard>
|
|
#include <QCloseEvent>
|
|
#include <QInputDialog>
|
|
#include <QTreeView>
|
|
#include <QSortFilterProxyModel>
|
|
#include <QStandardItemModel>
|
|
#include <QStandardItem>
|
|
#include <QTreeWidgetItem>
|
|
#include <QTreeWidget>
|
|
#include <QDockWidget>
|
|
#include <QSpinBox>
|
|
#include <stdexcept>
|
|
#include <fstream>
|
|
#include <QElapsedTimer>
|
|
#include <QWidgetAction>
|
|
#include <QSequentialIterable>
|
|
#include <QDir>
|
|
#include <QJSValue>
|
|
#include <QLoggingCategory>
|
|
|
|
#include <CGAL/Three/Three.h>
|
|
#include <CGAL/Three/CGAL_Lab_plugin_interface.h>
|
|
#include <CGAL/Three/CGAL_Lab_io_plugin_interface.h>
|
|
#include <CGAL/Three/Scene_item_with_properties.h>
|
|
#include "ui_SubViewer.h"
|
|
#include "ui_MainWindow.h"
|
|
#include "ui_Preferences.h"
|
|
#include "ui_Details.h"
|
|
#include "ui_Statistics_on_item_dialog.h"
|
|
#include "ui_SSH_dialog.h"
|
|
#include "Show_point_dialog.h"
|
|
#include "File_loader_dialog.h"
|
|
#include "Viewer.h"
|
|
|
|
#include <CGAL/Qt/manipulatedCameraFrame.h>
|
|
#include <CGAL/Qt/manipulatedFrame.h>
|
|
|
|
#include "Color_map.h"
|
|
|
|
|
|
using namespace CGAL::Three;
|
|
QJSValue
|
|
myScene_itemToScriptValue(QJSEngine *engine,
|
|
CGAL::Three::Scene_item* const &in)
|
|
{
|
|
return engine->newQObject(in);
|
|
}
|
|
|
|
void myScene_itemFromScriptValue(const QJSValue &object,
|
|
CGAL::Three::Scene_item* &out)
|
|
{
|
|
out = qobject_cast<CGAL::Three::Scene_item*>(object.toQObject());
|
|
}
|
|
|
|
void MainWindow::print(QString message)
|
|
{
|
|
this->message(QString("Script message: ") + message, "");
|
|
QTextStream (stdout) << (QString("Script message: ") + message) << "\n";
|
|
}
|
|
|
|
inline
|
|
QKeySequence combine(Qt::Modifier m, Qt::Key k)
|
|
{
|
|
return QKeySequence(QKeyCombination(m, k));
|
|
}
|
|
|
|
MainWindow::~MainWindow()
|
|
{
|
|
searchAction->deleteLater();
|
|
delete ui;
|
|
delete statistics_ui;
|
|
}
|
|
MainWindow::MainWindow(const QStringList &keywords, bool verbose, QWidget* parent)
|
|
: CGAL::Qt::DemosMainWindow(parent),
|
|
accepted_keywords(keywords)
|
|
{
|
|
bbox_need_update = true;
|
|
ui = new Ui::MainWindow;
|
|
ui->setupUi(this);
|
|
menuBar()->setNativeMenuBar(false);
|
|
searchAction = new QWidgetAction(this);
|
|
CGAL::Three::Three::s_mainwindow = this;
|
|
menu_map[ui->menuOperations->title()] = ui->menuOperations;
|
|
this->verbose = verbose;
|
|
is_locked = false;
|
|
|
|
// Save some pointers from ui, for latter use.
|
|
sceneView = ui->sceneView;
|
|
viewer_window = new SubViewer(ui->mdiArea, this, nullptr);
|
|
viewer = viewer_window->viewer;
|
|
CGAL::Three::Three::s_mainviewer = viewer;
|
|
CGAL::Three::Three::s_mutex = &mutex;
|
|
CGAL::Three::Three::s_wait_condition = &wait_condition;
|
|
viewer->setObjectName("mainViewer");
|
|
viewer_window->showMaximized();
|
|
viewer_window->setWindowFlags(
|
|
Qt::SubWindow
|
|
| Qt::CustomizeWindowHint
|
|
| Qt::WindowMaximizeButtonHint
|
|
| Qt::WindowSystemMenuHint
|
|
| Qt::WindowTitleHint
|
|
);
|
|
viewer_window->setWindowTitle("Main Viewer");
|
|
// setup scene
|
|
scene = new Scene(this);
|
|
CGAL::Three::Three::s_scene = scene;
|
|
CGAL::Three::Three::s_connectable_scene = scene;
|
|
{
|
|
QShortcut* shortcut = new QShortcut(combine(Qt::ALT,Qt::Key_Q), this);
|
|
connect(shortcut, SIGNAL(activated()),
|
|
this, SLOT(setFocusToQuickSearch()));
|
|
shortcut = new QShortcut(QKeySequence(Qt::Key_F5), this);
|
|
connect(shortcut, SIGNAL(activated()),
|
|
this, SLOT(reloadItem()));
|
|
shortcut = new QShortcut(QKeySequence(Qt::Key_F11), this);
|
|
connect(shortcut, SIGNAL(activated()),
|
|
this, SLOT(toggleFullScreen()));
|
|
shortcut = new QShortcut(combine(Qt::CTRL,Qt::Key_R), this);
|
|
connect(shortcut, &QShortcut::activated,
|
|
this, &MainWindow::recenterScene);
|
|
shortcut = new QShortcut(combine(Qt::CTRL,Qt::Key_T), this);
|
|
connect(shortcut, &QShortcut::activated,
|
|
this,
|
|
[](){
|
|
Viewer* viewer = qobject_cast<Viewer*>(CGAL::Three::Three::activeViewer());
|
|
bool b = viewer->property("draw_two_sides").toBool();
|
|
viewer->setTwoSides(!b);
|
|
}
|
|
);
|
|
}
|
|
|
|
proxyModel = new QSortFilterProxyModel(this);
|
|
proxyModel->setSourceModel(scene);
|
|
SceneDelegate *delegate = new SceneDelegate(this);
|
|
delegate->setProxy(proxyModel);
|
|
delegate->setScene(scene);
|
|
|
|
|
|
connect(ui->searchEdit, SIGNAL(textChanged(QString)),
|
|
proxyModel, SLOT(setFilterFixedString(QString)));
|
|
sceneView->setModel(proxyModel);
|
|
|
|
// setup the sceneview: delegation and columns sizing...
|
|
sceneView->setItemDelegate(delegate);
|
|
resetHeader();
|
|
|
|
// setup connections
|
|
connect(scene, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex & )),
|
|
this, SLOT(updateInfo()));
|
|
|
|
connect(scene, &Scene::dataChanged,
|
|
this, [this]() { filterOperations(false); });
|
|
|
|
|
|
connect(scene, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex & )),
|
|
this, SLOT(updateDisplayInfo()));
|
|
|
|
connect(viewer, &Viewer::needNewContext,
|
|
[this](){create();});
|
|
|
|
|
|
connect(scene, SIGNAL(updated()),
|
|
this, SLOT(selectionChanged()));
|
|
|
|
connect(scene, SIGNAL(itemAboutToBeDestroyed(CGAL::Three::Scene_item*)),
|
|
this, SLOT(removeManipulatedFrame(CGAL::Three::Scene_item*)));
|
|
|
|
connect(scene, SIGNAL(updated_bbox(bool)),
|
|
this, SLOT(invalidate_bbox(bool)));
|
|
|
|
connect(scene, SIGNAL(selectionChanged(int)),
|
|
this, SLOT(selectSceneItem(int)));
|
|
connect(scene, SIGNAL(selectionChanged(QList<int>)),
|
|
this, SLOT(selectSceneItems(QList<int>)));
|
|
|
|
connect(scene, SIGNAL(itemPicked(const QModelIndex &)),
|
|
this, SLOT(recenterSceneView(const QModelIndex &)));
|
|
|
|
connect(sceneView->selectionModel(),
|
|
SIGNAL(selectionChanged ( const QItemSelection & , const QItemSelection & ) ),
|
|
this, SLOT(updateInfo()));
|
|
|
|
connect(sceneView->selectionModel(),
|
|
SIGNAL(selectionChanged ( const QItemSelection & , const QItemSelection & ) ),
|
|
this, SLOT(updateDisplayInfo()));
|
|
|
|
connect(sceneView->selectionModel(),
|
|
SIGNAL(selectionChanged ( const QItemSelection & , const QItemSelection & ) ),
|
|
this, SLOT(selectionChanged()));
|
|
// setup menu filtering
|
|
|
|
connect(sceneView->selectionModel(),
|
|
QOverload<const QItemSelection & , const QItemSelection &>::of(&QItemSelectionModel::selectionChanged),
|
|
this, [this](){filterOperations(false);});
|
|
|
|
sceneView->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
connect(sceneView, SIGNAL(customContextMenuRequested(const QPoint & )),
|
|
this, SLOT(showSceneContextMenu(const QPoint &)));
|
|
|
|
connect(sceneView, SIGNAL(expanded(QModelIndex)),
|
|
this, SLOT(setExpanded(QModelIndex)));
|
|
|
|
connect(sceneView, SIGNAL(collapsed(QModelIndex)),
|
|
this, SLOT(setCollapsed(QModelIndex)));
|
|
connect(this, SIGNAL(collapsed(QModelIndex)),
|
|
scene, SLOT(setCollapsed(QModelIndex)));
|
|
connect(this, SIGNAL(expanded(QModelIndex)),
|
|
scene, SLOT(setExpanded(QModelIndex)));
|
|
|
|
connect(scene, SIGNAL(restoreCollapsedState()),
|
|
this, SLOT(restoreCollapseState()));
|
|
|
|
setupViewer(viewer, viewer_window);
|
|
|
|
// add the "About CGAL..." and "About demo..." entries
|
|
this->addAboutCGAL();
|
|
this->addAboutDemo(":/cgal/Lab/about.html");
|
|
|
|
// Connect the button "addButton" with actionLoad
|
|
ui->addButton->setDefaultAction(ui->actionLoad);
|
|
// Same with "removeButton" and "duplicateButton"
|
|
ui->removeButton->setDefaultAction(ui->actionErase);
|
|
ui->duplicateButton->setDefaultAction(ui->actionDuplicate);
|
|
|
|
// Connect actionQuit (Ctrl+Q) and qApp->quit()
|
|
connect(ui->actionQuit, SIGNAL(triggered()),
|
|
this, SLOT(quit()));
|
|
// Connect "Select all items"
|
|
connect(ui->actionSelectAllItems, SIGNAL(triggered()),
|
|
this, SLOT(selectAll()));
|
|
|
|
connect(ui->actionColorItems, SIGNAL(triggered()),
|
|
this, SLOT(colorItems()));
|
|
|
|
// Recent files menu
|
|
this->addRecentFiles(ui->menuFile, ui->actionQuit);
|
|
connect(this, SIGNAL(openRecentFile(QString)),
|
|
this, SLOT(open(QString)));
|
|
|
|
// Reset the "Operation menu"
|
|
clearMenu(ui->menuOperations);
|
|
|
|
script_engine = new QJSEngine(this);
|
|
script_engine->installExtensions(QJSEngine::ConsoleExtension);
|
|
|
|
#if 0
|
|
qScriptRegisterMetaType<CGAL::Three::Scene_item*>(script_engine,
|
|
myScene_itemToScriptValue,
|
|
myScene_itemFromScriptValue);
|
|
|
|
|
|
QJSValue fun = script_engine->newFunction(myPrintFunction);
|
|
script_engine->globalObject().setProperty("print", fun);
|
|
#endif
|
|
// evaluate_script("print('hello', 'world', 'from QtScript!')");
|
|
QJSValue mainWindowObjectValue = script_engine->newQObject(this);
|
|
script_engine->globalObject().setProperty("main_window", mainWindowObjectValue);
|
|
|
|
QJSValue sceneObjectValue = script_engine->newQObject(scene);
|
|
mainWindowObjectValue.setProperty("scene", sceneObjectValue);
|
|
script_engine->globalObject().setProperty("scene", sceneObjectValue);
|
|
|
|
QJSValue viewerObjectValue = script_engine->newQObject(viewer);
|
|
mainWindowObjectValue.setProperty("viewer", viewerObjectValue);
|
|
script_engine->globalObject().setProperty("viewer", viewerObjectValue);
|
|
|
|
QJSValue cameraObjectValue = script_engine->newQObject(viewer->camera());
|
|
viewerObjectValue.setProperty("camera", cameraObjectValue);
|
|
script_engine->globalObject().setProperty("camera", cameraObjectValue);
|
|
|
|
evaluate_script("var plugins = new Array();");
|
|
|
|
|
|
readSettings(); // Among other things, the column widths are stored.
|
|
|
|
// Load plugins, and re-enable actions that need it.
|
|
operationSearchBar.setPlaceholderText("Filter...");
|
|
searchAction->setDefaultWidget(&operationSearchBar);
|
|
|
|
connect(&operationSearchBar, &QLineEdit::textChanged,
|
|
this, [this](){filterOperations(true);});
|
|
|
|
loadPlugins();
|
|
accepted_keywords.clear();
|
|
|
|
// Setup the submenu of the View menu that can toggle the dockwidgets
|
|
for(QDockWidget* widget : findChildren<QDockWidget*>()) {
|
|
widget->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetFloatable);
|
|
ui->menuDockWindows->addAction(widget->toggleViewAction());
|
|
}
|
|
ui->menuDockWindows->removeAction(ui->dummyAction);
|
|
|
|
|
|
this->readState("MainWindow", Size|State);
|
|
|
|
//Manages the group_item creation
|
|
actionAddToGroup= new QAction("Add New Group", this);
|
|
|
|
if(actionAddToGroup) {
|
|
connect(actionAddToGroup, SIGNAL(triggered()),
|
|
this, SLOT(makeNewGroup()));
|
|
}
|
|
|
|
QMenu* menuFile = findChild<QMenu*>("menuFile");
|
|
insertActionBeforeLoadPlugin(menuFile, actionAddToGroup);
|
|
statistics_dlg = nullptr;
|
|
statistics_ui = new Ui::Statistics_on_item_dialog();
|
|
|
|
actionResetDefaultLoaders = new QAction("Reset Default Loaders",this);
|
|
|
|
// evaluate_script("print(plugins);");
|
|
for(QAction* action : findChildren<QAction*>()) {
|
|
if(action->objectName() != "") {
|
|
QJSValue objectValue = script_engine->newQObject(action);
|
|
script_engine->globalObject().setProperty(action->objectName(),
|
|
objectValue);
|
|
}
|
|
}
|
|
filterOperations(true);
|
|
}
|
|
|
|
void addActionToMenu(QAction* action, QMenu* menu)
|
|
{
|
|
auto actions = menu->actions();
|
|
auto it = std::lower_bound(actions.begin(), actions.end(),
|
|
action->text().remove("&"),
|
|
[](QAction* a, QString text) {
|
|
return a->text().remove("&").compare(text) < 0;
|
|
});
|
|
if(it == actions.end()) {
|
|
menu->addAction(action);
|
|
}
|
|
else {
|
|
menu->insertAction(*it, action);
|
|
}
|
|
}
|
|
|
|
//Recursive function that do a pass over a menu and its sub-menus(etc.) and hide them when they are empty
|
|
void filterMenuOperations(QMenu* menu, QString filter, bool keep_from_here)
|
|
{
|
|
QList<QAction*> buffer;
|
|
for(QAction* action : menu->actions())
|
|
buffer.append(action);
|
|
|
|
while(!buffer.isEmpty()){
|
|
QList<QAction*> buffer_copy=buffer; // make a copy as we modify buffer in the loop
|
|
for(QAction* action : buffer_copy) {
|
|
if(QMenu* submenu = action->menu())
|
|
{
|
|
bool keep = true;
|
|
if(!keep_from_here){
|
|
keep = submenu->menuAction()->text().contains(filter, Qt::CaseInsensitive);
|
|
if(!keep)
|
|
{
|
|
for(QAction* subaction : submenu->actions())
|
|
{
|
|
submenu->removeAction(subaction);
|
|
buffer.append(subaction);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
addActionToMenu(submenu->menuAction(), menu);
|
|
}
|
|
}
|
|
filterMenuOperations(submenu, filter, keep);
|
|
action->setVisible(!(submenu->isEmpty()));
|
|
|
|
}
|
|
else if(action->text().remove("&").contains(filter, Qt::CaseInsensitive)){
|
|
//menu->addAction(action);
|
|
addActionToMenu(action, menu);
|
|
}
|
|
buffer.removeAll(action);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef Q_OS_WIN
|
|
void MainWindow::filterOperations(bool hide)
|
|
#else
|
|
void MainWindow::filterOperations(bool)
|
|
#endif
|
|
{
|
|
//on some platforms editing an open menu slows everything like hell,
|
|
//so we hide it for the time of the process.
|
|
#ifdef Q_OS_WIN
|
|
if(hide)
|
|
ui->menuOperations->hide();
|
|
#endif
|
|
//return actions to their true menu
|
|
for(QMenu* menu : action_menu_map.values())
|
|
{
|
|
for(QAction* action : menu->actions())
|
|
{
|
|
if(action != searchAction)
|
|
menu->removeAction(action);
|
|
}
|
|
}
|
|
|
|
for(QAction* action : action_menu_map.keys())
|
|
{
|
|
QMenu* menu = action_menu_map[action];
|
|
addActionToMenu(action, menu);
|
|
}
|
|
QString filter=operationSearchBar.text();
|
|
for(const PluginNamePair& p : plugins) {
|
|
for(QAction* action : p.first->actions()) {
|
|
action->setVisible( p.first->applicable(action)
|
|
&& (action->text().remove("&").contains(filter, Qt::CaseInsensitive)
|
|
|| action->property("subMenuName")
|
|
.toString().contains(filter, Qt::CaseInsensitive)));
|
|
}
|
|
}
|
|
// do a pass over all menus in Operations and their sub-menus(etc.) and hide them when they are empty
|
|
filterMenuOperations(ui->menuOperations, filter, false);
|
|
#ifdef Q_OS_WIN
|
|
if(hide)
|
|
ui->menuOperations->show();
|
|
#endif
|
|
operationSearchBar.setFocus();
|
|
}
|
|
|
|
#include <CGAL/Three/exceptions.h>
|
|
|
|
|
|
void MainWindow::evaluate_script(QString script,
|
|
const QString& filename,
|
|
const bool quiet) {
|
|
QJSValue object = script_engine->globalObject();
|
|
QJSValue former_current_filename = object.property("current_filename");;
|
|
object.setProperty("current_filename", filename);
|
|
QStringList error_bt;
|
|
QJSValue value = script_engine->evaluate(script, filename, 1, &error_bt);
|
|
if (!error_bt.isEmpty()) {
|
|
if(! quiet){
|
|
QString err_str;
|
|
QTextStream err(&err_str);
|
|
err << tr("Qt Script exception at %1:%2").arg(value.property("fileName").toString())
|
|
.arg(value.property("lineNumber").toInt())
|
|
<< ":" << value.toString()
|
|
<< "\nBacktrace:\n";
|
|
for(auto line: error_bt) {
|
|
err << " " << line << "\n";
|
|
}
|
|
qWarning().noquote() << err_str;
|
|
}
|
|
throw CGAL::Three::Script_exception(value.toString(), error_bt);
|
|
}
|
|
else if(!quiet && !value.isNull() && !value.isUndefined()) {
|
|
QTextStream(stderr) << "Qt Script evaluated to \""
|
|
<< value.toString() << "\"\n";
|
|
}
|
|
|
|
object.setProperty("current_filename", former_current_filename);
|
|
}
|
|
|
|
void MainWindow::evaluate_script_quiet(QString script,
|
|
const QString& filename)
|
|
{
|
|
evaluate_script(script, filename, true);
|
|
}
|
|
|
|
|
|
|
|
namespace {
|
|
bool actionsByName(QAction* x, QAction* y) {
|
|
return x->text() < y->text();
|
|
}
|
|
}
|
|
|
|
//Recursively creates all subMenus containing an action.
|
|
// In the current implementation, there is a bug if a menu
|
|
// and a submenu have the same name (cf map menu_map).
|
|
void MainWindow::setMenus(QString name, QString parentName, QAction* a )
|
|
{
|
|
QString menuName, subMenuName;
|
|
if (name.isNull())
|
|
return;
|
|
int slash_index = name.indexOf('/');
|
|
|
|
if(slash_index==-1)
|
|
menuName= name; // no extra sub-menu
|
|
else
|
|
{
|
|
int l = name.length();
|
|
menuName=name.mid(0,slash_index);
|
|
subMenuName=name.mid(slash_index+1,l-slash_index-1);
|
|
// recursively create sub-menus
|
|
setMenus(subMenuName, menuName, a);
|
|
}
|
|
|
|
//Create the menu if it does not already exist
|
|
if(!menu_map.contains(menuName))
|
|
menu_map[menuName] = new QMenu(menuName, this);
|
|
|
|
//Create the parent menu if it does not already exist
|
|
if(!menu_map.contains(parentName))
|
|
menu_map[parentName] = new QMenu(parentName, this);
|
|
// add the submenu in the menu
|
|
menu_map[parentName]->addMenu(menu_map[menuName]);
|
|
action_menu_map[menu_map[menuName]->menuAction()] = menu_map[parentName];
|
|
|
|
// only add the action in the last submenu
|
|
if(slash_index==-1)
|
|
{
|
|
ui->menuOperations->removeAction(a);
|
|
menu_map[menuName]->addAction(a);
|
|
action_menu_map[a] = menu_map[menuName];
|
|
}
|
|
}
|
|
|
|
QLoggingCategory qlog_cgallab_plugins("cgal.lab.plugins");
|
|
|
|
bool MainWindow::load_plugin(QString fileName, bool blacklisted)
|
|
{
|
|
if(fileName.contains("plugin") && QLibrary::isLibrary(fileName)) {
|
|
//set plugin name
|
|
QFileInfo fileinfo(fileName);
|
|
//set plugin name
|
|
QString name = fileinfo.fileName();
|
|
name.remove(QRegularExpression("^lib"));
|
|
name.remove(QRegularExpression("\\..*"));
|
|
//do not load it if it is in the blacklist
|
|
if(blacklisted)
|
|
{
|
|
if ( plugin_blacklist.contains(name) ){
|
|
pluginsStatus_map[name] = QString("Blacklisted.");
|
|
ignored_map[name] = true;
|
|
//qDebug("### Ignoring plugin \"%s\".", qPrintable(fileName));
|
|
PathNames_map[name].push_back(fileinfo.absoluteDir().absolutePath());
|
|
return true;
|
|
}
|
|
}
|
|
if(verbose)
|
|
qCDebug(qlog_cgallab_plugins) << "### Loading" << fileName << "... ";
|
|
QPluginLoader loader;
|
|
loader.setFileName(fileinfo.absoluteFilePath());
|
|
QJsonArray keywords = loader.metaData().value("MetaData").toObject().value("Keywords").toArray();
|
|
QString date = loader.metaData().value("MetaData").toObject().value("ConfigDate").toString();
|
|
QStringList s_keywords;
|
|
for(int i = 0; i < keywords.size(); ++i)
|
|
{
|
|
s_keywords.append(keywords[i].toString());
|
|
}
|
|
plugin_metadata_map[name] = qMakePair(s_keywords, date);
|
|
QObject *obj = loader.instance();
|
|
bool do_load = accepted_keywords.empty();
|
|
if(!do_load)
|
|
{
|
|
for(QString k : s_keywords)
|
|
{
|
|
if(accepted_keywords.contains(k))
|
|
{
|
|
do_load = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if(do_load && obj) {
|
|
obj->setObjectName(name);
|
|
bool init1 = initPlugin(obj);
|
|
bool init2 = initIOPlugin(obj);
|
|
if (!init1 && !init2)
|
|
{
|
|
pluginsStatus_map[name] = QString("ERROR: Not for this program.");
|
|
}
|
|
else{
|
|
QJSValue objectValue =
|
|
script_engine->newQObject(obj);
|
|
script_engine->globalObject().setProperty(obj->objectName(), objectValue);
|
|
evaluate_script_quiet(QString("plugins.push(%1);").arg(obj->objectName()));
|
|
pluginsStatus_map[name] = QString("success");
|
|
}
|
|
}
|
|
else if(!do_load)
|
|
{
|
|
pluginsStatus_map[name]="ERROR: Wrong Keywords.";
|
|
ignored_map[name] = true;
|
|
}
|
|
else{
|
|
pluginsStatus_map[name] = "ERROR: " + loader.errorString();
|
|
}
|
|
if(verbose && pluginsStatus_map[name].startsWith("ERROR")) {
|
|
qCDebug(qlog_cgallab_plugins) << " plugin " << name << ": " << pluginsStatus_map[name]
|
|
<< "\n plugin keywords: " << s_keywords
|
|
<< "\n app keywords: " << accepted_keywords;
|
|
}
|
|
PathNames_map[name].push_back(fileinfo.absoluteDir().absolutePath());
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void MainWindow::loadPlugins()
|
|
{
|
|
for(QObject *obj : QPluginLoader::staticInstances())
|
|
{
|
|
initPlugin(obj);
|
|
initIOPlugin(obj);
|
|
}
|
|
|
|
QList<QDir> plugins_directories;
|
|
QString dirPath = qApp->applicationDirPath();
|
|
plugins_directories<<dirPath;
|
|
QDir msvc_dir(dirPath);
|
|
QString build_dir_name = msvc_dir.dirName();//Debug or Release for msvc
|
|
msvc_dir.cdUp();
|
|
|
|
QFileInfoList filist = QDir(dirPath).entryInfoList();
|
|
filist << msvc_dir.entryInfoList();
|
|
|
|
for(QFileInfo fileinfo : filist)
|
|
{
|
|
//checks if the path leads to a directory
|
|
if(fileinfo.baseName().contains("Plugins"))
|
|
{
|
|
QString plugins_dir = fileinfo.absolutePath();
|
|
plugins_dir.append("/").append(fileinfo.baseName());
|
|
|
|
for(QString package_dir :
|
|
QDir(plugins_dir).entryList(QDir::Dirs))
|
|
{
|
|
QString package_dir_path(plugins_dir);
|
|
package_dir_path.append("/").append(package_dir);
|
|
|
|
QString libdir_path(package_dir_path);
|
|
libdir_path.append("/").append(build_dir_name);
|
|
|
|
if (QDir(libdir_path).exists())
|
|
plugins_directories << QDir(libdir_path);
|
|
else
|
|
plugins_directories << QDir(package_dir_path);
|
|
}
|
|
}
|
|
}
|
|
QString env_path = qgetenv("LAB_DEMO_PLUGINS_PATH");
|
|
QChar separator = QDir::listSeparator();
|
|
if(!env_path.isEmpty()) {
|
|
#if defined(_WIN32)
|
|
QString path = qgetenv("PATH");
|
|
QByteArray new_path = path.append(env_path.prepend(separator)).toUtf8();
|
|
qputenv("PATH", new_path);
|
|
#endif
|
|
for (QString pluginsDir :
|
|
env_path.split(separator, CGAL_QT_SKIP_EMPTY_PARTS)) {
|
|
QDir dir(pluginsDir);
|
|
if(dir.isReadable())
|
|
plugins_directories << dir;
|
|
}
|
|
}
|
|
|
|
QSet<QString> loaded;
|
|
for (QDir pluginsDir : plugins_directories) {
|
|
if(verbose)
|
|
qDebug("# Looking for plugins in directory \"%s\"...",
|
|
qPrintable(pluginsDir.absolutePath()));
|
|
for(QString fileName : pluginsDir.entryList(QDir::Files))
|
|
{
|
|
QString abs_name = pluginsDir.absoluteFilePath(fileName);
|
|
if(loaded.find(abs_name) == loaded.end())
|
|
{
|
|
if(load_plugin(abs_name, true))
|
|
{
|
|
loaded.insert(abs_name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
updateMenus();
|
|
}
|
|
//Creates sub-Menus for operations.
|
|
void MainWindow::updateMenus()
|
|
{
|
|
QList<QAction*> as = ui->menuOperations->actions();
|
|
for(QAction* a : as)
|
|
{
|
|
QString menuPath = a->property("subMenuName").toString();
|
|
setMenus(menuPath, ui->menuOperations->title(), a);
|
|
}
|
|
// sort the operations menu by name
|
|
as = ui->menuOperations->actions();
|
|
std::sort(as.begin(), as.end(), actionsByName);
|
|
ui->menuOperations->clear();
|
|
ui->menuOperations->addAction(searchAction);
|
|
ui->menuOperations->addActions(as);
|
|
operationSearchBar.setFocus();
|
|
}
|
|
|
|
bool MainWindow::hasPlugin(const QString& pluginName) const
|
|
{
|
|
for(const PluginNamePair& p : plugins) {
|
|
if(p.second == pluginName) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool MainWindow::initPlugin(QObject* obj)
|
|
{
|
|
QObjectList childs = this->children();
|
|
CGAL::Three::CGAL_Lab_plugin_interface* plugin =
|
|
qobject_cast<CGAL::Three::CGAL_Lab_plugin_interface*>(obj);
|
|
if(plugin) {
|
|
// Call plugin's init() method
|
|
obj->setParent(this);
|
|
plugin->init(this, this->scene, this);
|
|
plugins << qMakePair(plugin, obj->objectName());
|
|
|
|
for(QAction* action : plugin->actions()) {
|
|
// If action does not belong to the menus, add it to "Operations" menu
|
|
if(!childs.contains(action)) {
|
|
ui->menuOperations->addAction(action);
|
|
action_menu_map[action] = ui->menuOperations;
|
|
}
|
|
// Show and enable menu item
|
|
addAction(action);
|
|
}
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
bool MainWindow::initIOPlugin(QObject* obj)
|
|
{
|
|
CGAL::Three::CGAL_Lab_io_plugin_interface* plugin =
|
|
qobject_cast<CGAL::Three::CGAL_Lab_io_plugin_interface*>(obj);
|
|
if(plugin) {
|
|
plugin->init();
|
|
io_plugins << plugin;
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
void MainWindow::clearMenu(QMenu* menu)
|
|
{
|
|
for(QAction* action : menu->actions())
|
|
{
|
|
QMenu* menu = action->menu();
|
|
if(menu) {
|
|
clearMenu(menu);
|
|
}
|
|
action->setVisible(false);
|
|
}
|
|
menu->menuAction()->setEnabled(false);
|
|
}
|
|
|
|
void MainWindow::addAction(QAction* action)
|
|
{
|
|
if(!action) return;
|
|
|
|
action->setVisible(true);
|
|
action->setEnabled(true);
|
|
for(auto object: action->associatedObjects())
|
|
{
|
|
// qDebug() << QString("%1 (%2)\n")
|
|
// .arg(widget->objectName())
|
|
// .arg(widget->metaObject()->className());
|
|
QMenu* menu = qobject_cast<QMenu*>(object);
|
|
if(menu)
|
|
{
|
|
addAction(menu->menuAction());
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::addAction(QString actionName,
|
|
QString actionText,
|
|
QString menuName) {
|
|
QMenu* menu = nullptr;
|
|
for(QAction* action : findChildren<QAction*>()) {
|
|
if(!action->menu()) continue;
|
|
QString menuText = action->menu()->title();
|
|
if(menuText != menuName) continue;
|
|
menu = action->menu();
|
|
}
|
|
if(menu == nullptr) {
|
|
menu = new QMenu(menuName, this);
|
|
menuBar()->insertMenu(ui->menuView->menuAction(), menu);
|
|
}
|
|
QAction* action = new QAction(actionText, this);
|
|
action->setObjectName(actionName);
|
|
menu->addAction(action);
|
|
|
|
QJSValue objectValue = script_engine->newQObject(action);
|
|
script_engine->globalObject().setProperty(action->objectName(),
|
|
objectValue);
|
|
}
|
|
|
|
void MainWindow::viewerShow(float xmin,
|
|
float ymin,
|
|
float zmin,
|
|
float xmax,
|
|
float ymax,
|
|
float zmax)
|
|
{
|
|
CGAL::qglviewer::Vec
|
|
min_(xmin, ymin, zmin),
|
|
max_(xmax, ymax, zmax);
|
|
|
|
if(min_ == max_) return viewerShow(viewer, xmin, ymin, zmin);
|
|
|
|
viewer->camera()->setPivotPoint((min_+max_)*0.5);
|
|
|
|
CGAL::qglviewer::ManipulatedCameraFrame backup_frame(*viewer->camera()->frame());
|
|
viewer->camera()->fitBoundingBox(min_, max_);
|
|
CGAL::qglviewer::ManipulatedCameraFrame new_frame(*viewer->camera()->frame());
|
|
*viewer->camera()->frame() = backup_frame;
|
|
viewer->camera()->interpolateTo(new_frame, 1.f);
|
|
viewer->setVisualHintsMask(1);
|
|
}
|
|
|
|
void MainWindow::viewerShow(Viewer_interface* vi, float x, float y, float z) {
|
|
|
|
CGAL::qglviewer::ManipulatedCameraFrame backup_frame(*vi->camera()->frame());
|
|
vi->camera()->fitSphere(CGAL::qglviewer::Vec(x, y, z),
|
|
vi->camera()->sceneRadius()/100);
|
|
CGAL::qglviewer::ManipulatedCameraFrame new_frame(*vi->camera()->frame());
|
|
*vi->camera()->frame() = backup_frame;
|
|
vi->camera()->interpolateTo(new_frame, 1.f);
|
|
vi->setVisualHintsMask(1);
|
|
|
|
vi->camera()->setPivotPoint(CGAL::qglviewer::Vec(x, y, z));
|
|
}
|
|
|
|
void MainWindow::message(QString message, QString colorName, QString font) {
|
|
if (message.endsWith('\n')) {
|
|
message.remove(message.length()-1, 1);
|
|
}
|
|
statusBar()->showMessage(message, 5000);
|
|
QTimer::singleShot(5000, [this]{this->statusBar()->setStyleSheet("");});
|
|
message = "<font color=\"" + colorName + "\" style=\"font-style: " + font + ";\" >" +
|
|
message + "</font><br>";
|
|
message = "[" + QTime::currentTime().toString() + "] " + message;
|
|
ui->consoleTextEdit->append(message);
|
|
ui->consoleTextEdit->verticalScrollBar()->setValue(ui->consoleTextEdit->verticalScrollBar()->maximum());
|
|
}
|
|
|
|
void MainWindow::message_information(QString text) {
|
|
statusBar()->setStyleSheet("color: blue");
|
|
this->message("INFO: " + text, "blue");
|
|
}
|
|
|
|
void MainWindow::message_warning(QString text) {
|
|
statusBar()->setStyleSheet("color: orange");
|
|
this->message("WARNING: " + text, "orange");
|
|
}
|
|
|
|
void MainWindow::message_error(QString text) {
|
|
statusBar()->setStyleSheet("color: red");
|
|
this->message("ERROR: " + text, "red");
|
|
}
|
|
|
|
void MainWindow::updateViewersBboxes(bool recenter)
|
|
{
|
|
if(bbox_need_update)
|
|
{
|
|
CGAL::qglviewer::Vec min, max;
|
|
computeViewerBBox(min, max);
|
|
for(CGAL::QGLViewer* v : CGAL::QGLViewer::QGLViewerPool())
|
|
{
|
|
if(v == nullptr)
|
|
continue;
|
|
Viewer* vi = static_cast<Viewer*>(v);
|
|
updateViewerBbox(vi, recenter, min, max);
|
|
}
|
|
bbox_need_update = false;
|
|
}
|
|
|
|
}
|
|
|
|
void MainWindow::computeViewerBBox(CGAL::qglviewer::Vec& vmin, CGAL::qglviewer::Vec& vmax)
|
|
{
|
|
const Scene::Bbox bbox = scene->bbox();
|
|
const double xmin = bbox.xmin();
|
|
const double ymin = bbox.ymin();
|
|
const double zmin = bbox.zmin();
|
|
const double xmax = bbox.xmax();
|
|
const double ymax = bbox.ymax();
|
|
const double zmax = bbox.zmax();
|
|
|
|
|
|
vmin = CGAL::qglviewer::Vec(xmin, ymin, zmin);
|
|
vmax= CGAL::qglviewer::Vec(xmax, ymax, zmax);
|
|
|
|
CGAL::qglviewer::Vec bbox_center((xmin+xmax)/2, (ymin+ymax)/2, (zmin+zmax)/2);
|
|
|
|
double bbox_diag = CGAL::approximate_sqrt(
|
|
CGAL::square(xmax - xmin)
|
|
+ CGAL::square(ymax - ymin)
|
|
+ CGAL::square(zmax - zmin));
|
|
|
|
CGAL::qglviewer::Vec offset(0,0,0);
|
|
|
|
double l_dist = (std::max)((std::abs)(bbox_center.x - viewer->offset().x),
|
|
(std::max)((std::abs)(bbox_center.y - viewer->offset().y),
|
|
(std::abs)(bbox_center.z - viewer->offset().z)));
|
|
if((std::log2)(l_dist/bbox_diag) > 11.0 )
|
|
for(int i=0; i<3; ++i)
|
|
{
|
|
offset[i] = -bbox_center[i];
|
|
}
|
|
if(offset != viewer->offset())
|
|
{
|
|
for(CGAL::QGLViewer* v : CGAL::QGLViewer::QGLViewerPool())
|
|
{
|
|
if(v == nullptr)
|
|
continue;
|
|
Viewer* vi = qobject_cast<Viewer*>(v);
|
|
vi->setOffset(offset);
|
|
}
|
|
for(int i=0; i<scene->numberOfEntries(); ++i)
|
|
{
|
|
// scene->item(i)->invalidate(Scene_item::GEOMETRY);
|
|
scene->item(i)->invalidateOpenGLBuffers();
|
|
scene->item(i)->itemChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::reloadItem() {
|
|
|
|
Scene_item* item = nullptr;
|
|
|
|
for(Scene::Item_id id : scene->selectionIndices())
|
|
{
|
|
item = scene->item(id);
|
|
if(!item)//secure items like selection items that get deleted when their "parent" item is reloaded.
|
|
continue;
|
|
QString filename = item->property("source filename").toString();
|
|
QString loader_name = item->property("loader_name").toString();
|
|
if(filename.isEmpty() || loader_name.isEmpty()) {
|
|
this->warning(QString("Cannot reload item %1: "
|
|
"the item has no \"source filename\" or no \"loader_name\" attached\n").arg(item->name()));
|
|
continue;
|
|
}
|
|
|
|
CGAL::Three::CGAL_Lab_io_plugin_interface* fileloader = findLoader(loader_name);
|
|
QFileInfo fileinfo(filename);
|
|
bool ok;
|
|
QList<Scene_item*> new_items = loadItem(fileinfo, fileloader, ok, false);
|
|
if(!ok)
|
|
return;
|
|
QVariant varian = item->property("load_mates");
|
|
if(!varian.isValid()) //typically when a soup is oriented, the soup_item is deleted and thus the varain points to an unexisting item.
|
|
{
|
|
Scene_item* new_item = new_items.front();
|
|
new_item->setName(item->name());
|
|
new_item->setColor(item->color());
|
|
new_item->setRenderingMode(item->renderingMode());
|
|
new_item->setVisible(item->visible());
|
|
Scene_item_with_properties *property_item = dynamic_cast<Scene_item_with_properties*>(new_item);
|
|
scene->replaceItem(scene->item_id(item), new_item, true);
|
|
if(property_item)
|
|
property_item->copyProperties(item);
|
|
new_item->invalidateOpenGLBuffers();
|
|
item->deleteLater();
|
|
return;
|
|
}
|
|
QSequentialIterable iterable = varian.value<QSequentialIterable>();
|
|
|
|
// Can use foreach:
|
|
int mate_id = 0;
|
|
for(const QVariant &v: iterable)
|
|
{
|
|
Scene_item* mate = v.value<Scene_item*>();
|
|
Scene_item* new_item = new_items[mate_id];
|
|
new_item->setName(mate->name());
|
|
new_item->setColor(mate->color());
|
|
new_item->setRenderingMode(mate->renderingMode());
|
|
new_item->setVisible(mate->visible());
|
|
Scene_item_with_properties *property_item = dynamic_cast<Scene_item_with_properties*>(new_item);
|
|
scene->replaceItem(scene->item_id(mate), new_item, true);
|
|
if(property_item)
|
|
property_item->copyProperties(mate);
|
|
new_item->invalidateOpenGLBuffers();
|
|
mate->deleteLater();
|
|
}
|
|
}
|
|
}
|
|
|
|
CGAL::Three::CGAL_Lab_io_plugin_interface* MainWindow::findLoader(const QString& loader_name) const {
|
|
for(CGAL::Three::CGAL_Lab_io_plugin_interface* io_plugin :
|
|
io_plugins) {
|
|
if(io_plugin->name() == loader_name) {
|
|
return io_plugin;
|
|
}
|
|
}
|
|
throw std::invalid_argument(QString("No loader found with the name %1 available")
|
|
.arg(loader_name).toStdString()) ;
|
|
}
|
|
|
|
bool MainWindow::file_matches_filter(const QString& filters,
|
|
const QString& filename )
|
|
{
|
|
QFileInfo fileinfo(filename);
|
|
QString filename_stripped=fileinfo.fileName();
|
|
|
|
//match all filters between ()
|
|
QRegularExpression all_filters_rx("\\((.*)\\)");
|
|
|
|
QStringList split_filters = filters.split(";;");
|
|
for(const QString& filter : split_filters) {
|
|
QRegularExpressionMatch match = all_filters_rx.match(filter);
|
|
if(match.hasMatch()){
|
|
for (const QString& pattern : match.captured(1).split(' ')) {
|
|
QRegularExpressionMatch m = QRegularExpression(QRegularExpression::fromWildcard(pattern)).match(filename_stripped);
|
|
if (m.hasMatch()) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void MainWindow::open(QString filename)
|
|
{
|
|
QFileInfo fileinfo(filename);
|
|
|
|
// Handles the loading of script file from the command line arguments,
|
|
// and the special command line arguments that start with "javascript:"
|
|
// or "qtscript:"
|
|
QString program;
|
|
if(filename.startsWith("javascript:")) {
|
|
program=filename.right(filename.size() - 11);
|
|
}
|
|
if(filename.startsWith("qtscript:")) {
|
|
program=filename.right(filename.size() - 9);
|
|
}
|
|
if(filename.endsWith(".js")) {
|
|
loadScript(fileinfo);
|
|
return;
|
|
}
|
|
if(!program.isEmpty())
|
|
{
|
|
{
|
|
QTextStream(stderr) << "Execution of script \""
|
|
<< filename << "\"\n";
|
|
// << filename << "\", with following content:\n"
|
|
// << program;
|
|
}
|
|
QApplication::setOverrideCursor(Qt::WaitCursor);
|
|
evaluate_script(program, filename);
|
|
QApplication::restoreOverrideCursor();
|
|
return;
|
|
}
|
|
|
|
if ( !fileinfo.exists() ){
|
|
QMessageBox::warning(this,
|
|
tr("Cannot open file"),
|
|
tr("File %1 does not exist.")
|
|
.arg(filename));
|
|
return;
|
|
}
|
|
|
|
|
|
QStringList selected_items;
|
|
QStringList all_items;
|
|
|
|
QMap<QString,QString>::iterator dfs_it =
|
|
default_plugin_selection.find( fileinfo.completeSuffix() );
|
|
|
|
if ( dfs_it==default_plugin_selection.end() )
|
|
{
|
|
// collect all io_plugins and offer them to load if the file extension match one name filter
|
|
// also collect all available plugins in case of a no extension match
|
|
for(CGAL::Three::CGAL_Lab_io_plugin_interface* io_plugin : io_plugins) {
|
|
if ( file_matches_filter(io_plugin->loadNameFilters(), filename.toLower()) )
|
|
{
|
|
if ( !io_plugin->canLoad(fileinfo) ) continue;
|
|
all_items << io_plugin->name();
|
|
if(io_plugin->isDefaultLoader(fileinfo.completeSuffix()))
|
|
selected_items.prepend(io_plugin->name());
|
|
else
|
|
selected_items << io_plugin->name();
|
|
}
|
|
}
|
|
//if no plugin is correct, offer them all.
|
|
for(CGAL::Three::CGAL_Lab_io_plugin_interface* io_plugin : io_plugins) {
|
|
all_items << io_plugin->name();
|
|
}
|
|
}
|
|
else
|
|
selected_items << *dfs_it;
|
|
|
|
bool ok;
|
|
std::pair<QString, bool> load_pair;
|
|
|
|
switch( selected_items.size() )
|
|
{
|
|
case 1:
|
|
load_pair = std::make_pair(selected_items.first(), false);
|
|
ok=true;
|
|
break;
|
|
case 0:
|
|
load_pair = File_loader_dialog::getItem(this, fileinfo.fileName(), all_items, &ok);
|
|
break;
|
|
default:
|
|
load_pair = File_loader_dialog::getItem(this, fileinfo.fileName(), selected_items, &ok);
|
|
}
|
|
//viewer->makeCurrent();
|
|
if(!ok || load_pair.first.isEmpty()) { return; }
|
|
|
|
if (load_pair.second)
|
|
{
|
|
connect(actionResetDefaultLoaders, SIGNAL(triggered()),
|
|
this, SLOT(reset_default_loaders()));
|
|
default_plugin_selection[fileinfo.completeSuffix()]=load_pair.first;
|
|
insertActionBeforeLoadPlugin(ui->menuFile, actionResetDefaultLoaders);
|
|
}
|
|
|
|
|
|
settings.setValue("OFF open directory",
|
|
fileinfo.absoluteDir().absolutePath());
|
|
loadItem(fileinfo, findLoader(load_pair.first), ok);
|
|
|
|
if(!ok)
|
|
return;
|
|
this->addToRecentFiles(fileinfo.absoluteFilePath());
|
|
updateViewersBboxes(true);
|
|
}
|
|
|
|
bool MainWindow::open(QString filename, QString loader_name) {
|
|
QFileInfo fileinfo(filename);
|
|
std::optional<bool> item_opt;
|
|
try {
|
|
item_opt = wrap_a_call_to_cpp
|
|
([this, fileinfo, loader_name]()
|
|
{
|
|
bool ok;
|
|
loadItem(fileinfo, findLoader(loader_name), ok);
|
|
return ok;
|
|
},
|
|
this, __FILE__, __LINE__
|
|
);
|
|
if(!item_opt) return false;
|
|
}
|
|
catch(std::logic_error& e) {
|
|
std::cerr << e.what() << std::endl;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
QList<Scene_item*> MainWindow::loadItem(QFileInfo fileinfo,
|
|
CGAL::Three::CGAL_Lab_io_plugin_interface* loader,
|
|
bool &ok,
|
|
bool add_to_scene) {
|
|
if(!fileinfo.isFile() || !fileinfo.isReadable()) {
|
|
QMessageBox::warning(this, tr("Error"),
|
|
QString("File %1 is not a readable file.")
|
|
.arg(fileinfo.absoluteFilePath()));
|
|
}
|
|
|
|
QCursor tmp_cursor(Qt::WaitCursor);
|
|
CGAL::Three::Three::CursorScopeGuard guard(tmp_cursor);
|
|
QList<Scene_item*> result = loader->load(fileinfo, ok, add_to_scene);
|
|
if(!ok)
|
|
{
|
|
QApplication::restoreOverrideCursor();
|
|
QMessageBox::warning(this, tr("Error"),
|
|
QString("Could not load item from file %1 using plugin %2")
|
|
.arg(fileinfo.absoluteFilePath()).arg(loader->name()));
|
|
return QList<Scene_item*>();
|
|
}
|
|
selectSceneItem(scene->item_id(result.back()));
|
|
for(Scene_item* item : result)
|
|
{
|
|
CGAL::Three::Scene_group_item* group =
|
|
qobject_cast<CGAL::Three::Scene_group_item*>(item);
|
|
if(group)
|
|
scene->redraw_model();
|
|
item->setProperty("source filename", fileinfo.absoluteFilePath());
|
|
item->setProperty("loader_name", loader->name());
|
|
item->setProperty("load_mates",QVariant::fromValue(result));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
void MainWindow::setFocusToQuickSearch()
|
|
{
|
|
ui->searchEdit->setFocus(Qt::ShortcutFocusReason);
|
|
}
|
|
|
|
void MainWindow::selectSceneItem(int i)
|
|
{
|
|
if(i < 0 || i >= scene->numberOfEntries()) {
|
|
sceneView->selectionModel()->clearSelection();
|
|
updateInfo();
|
|
updateDisplayInfo();
|
|
}
|
|
else {
|
|
QItemSelection s =
|
|
proxyModel->mapSelectionFromSource(scene->createSelection(i));
|
|
if(s.empty())
|
|
return;
|
|
QModelIndex mi = proxyModel->mapFromSource(scene->getModelIndexFromId(i).first());
|
|
sceneView->setCurrentIndex(mi);
|
|
sceneView->selectionModel()->select(s,
|
|
QItemSelectionModel::ClearAndSelect);
|
|
sceneView->scrollTo(s.indexes().first());
|
|
sceneView->setCurrentIndex(sceneView->selectionModel()->selectedIndexes().first());
|
|
}
|
|
}
|
|
|
|
void MainWindow::selectSceneItems(QList<int> is)
|
|
{
|
|
if(is.first() < 0 || is.last() >= scene->numberOfEntries()) {
|
|
sceneView->selectionModel()->clearSelection();
|
|
updateInfo();
|
|
updateDisplayInfo();
|
|
}
|
|
else {
|
|
QItemSelection s =
|
|
proxyModel->mapSelectionFromSource(scene->createSelection(is));
|
|
|
|
QModelIndex i = proxyModel->mapFromSource(scene->getModelIndexFromId(is.first()).first());
|
|
sceneView->setCurrentIndex(i);
|
|
sceneView->selectionModel()->select(s,
|
|
QItemSelectionModel::ClearAndSelect);
|
|
if(!s.empty())
|
|
sceneView->scrollTo(s.indexes().first());
|
|
}
|
|
}
|
|
|
|
|
|
void MainWindow::showSelectedPoint(double x, double y, double z)
|
|
{
|
|
static double x_prev = 0;
|
|
static double y_prev = 0;
|
|
static double z_prev = 0;
|
|
double dist = std::sqrt((x-x_prev)*(x-x_prev) + (y-y_prev)*(y-y_prev) + (z-z_prev)*(z-z_prev));
|
|
information(QString("Selected point: (%1, %2, %3) distance to previous: %4").
|
|
arg(x, 0, 'g', 10).
|
|
arg(y, 0, 'g', 10).
|
|
arg(z, 0, 'g', 10).
|
|
arg(dist,0,'g',10));
|
|
x_prev = x;
|
|
y_prev = y;
|
|
z_prev = z;
|
|
}
|
|
|
|
void MainWindow::unSelectSceneItem(int i)
|
|
{
|
|
removeSceneItemFromSelection(i);
|
|
}
|
|
|
|
void MainWindow::addSceneItemInSelection(int i)
|
|
{
|
|
QItemSelection s =
|
|
proxyModel->mapSelectionFromSource(scene->createSelection(i));
|
|
sceneView->selectionModel()->select(s, QItemSelectionModel::Select);
|
|
scene->itemChanged(i);
|
|
}
|
|
|
|
void MainWindow::removeSceneItemFromSelection(int i)
|
|
{
|
|
QItemSelection s =
|
|
proxyModel->mapSelectionFromSource(scene->createSelection(i));
|
|
sceneView->selectionModel()->select(s,
|
|
QItemSelectionModel::Deselect);
|
|
scene->itemChanged(i);
|
|
}
|
|
|
|
void MainWindow::selectAll()
|
|
{
|
|
sceneView->selectAll();
|
|
}
|
|
|
|
int MainWindow::getSelectedSceneItemIndex() const
|
|
{
|
|
QModelIndexList selectedRows = sceneView->selectionModel()->selectedIndexes();
|
|
if(selectedRows.size() == 0)
|
|
return -1;
|
|
else {
|
|
QModelIndex i = proxyModel->mapToSource(selectedRows.first());
|
|
return scene->getIdFromModelIndex(i);
|
|
}
|
|
}
|
|
|
|
QList<int> MainWindow::getSelectedSceneItemIndices() const
|
|
{
|
|
QModelIndexList selectedIndices = sceneView->selectionModel()->selectedIndexes();
|
|
QList<int> result;
|
|
for(QModelIndex index : selectedIndices) {
|
|
int temp = scene->getIdFromModelIndex(proxyModel->mapToSource(index));
|
|
if(!result.contains(temp))
|
|
result<<temp;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void MainWindow::selectionChanged()
|
|
{
|
|
scene->setSelectedItemIndex(getSelectedSceneItemIndex());
|
|
scene->setSelectedItemIndices(getSelectedSceneItemIndices());
|
|
CGAL::Three::Scene_item* item = scene->item(getSelectedSceneItemIndex());
|
|
for(CGAL::QGLViewer* vi : CGAL::QGLViewer::QGLViewerPool())
|
|
{
|
|
if(vi == nullptr)
|
|
continue;
|
|
|
|
if(item != nullptr && item->manipulatable()) {
|
|
vi->setManipulatedFrame(item->manipulatedFrame());
|
|
} else {
|
|
vi->setManipulatedFrame(nullptr);
|
|
}
|
|
if(vi->manipulatedFrame() == nullptr) {
|
|
for(CGAL::Three::Scene_item* item : scene->entries()) {
|
|
if(item->manipulatable() && item->manipulatedFrame() != nullptr) {
|
|
if(vi->manipulatedFrame() != nullptr) {
|
|
// there are at least two possible frames
|
|
vi->setManipulatedFrame(nullptr);
|
|
break;
|
|
} else {
|
|
vi->setManipulatedFrame(item->manipulatedFrame());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(vi->manipulatedFrame() != nullptr) {
|
|
connect(vi->manipulatedFrame(), SIGNAL(modified()),
|
|
this, SLOT(updateInfo()));
|
|
}
|
|
vi->update();
|
|
}
|
|
}
|
|
void MainWindow::contextMenuRequested(const QPoint& global_pos) {
|
|
int index = scene->mainSelectionIndex();
|
|
showSceneContextMenu(index, global_pos);
|
|
}
|
|
|
|
void MainWindow::showSceneContextMenu(int selectedItemIndex,
|
|
const QPoint& global_pos)
|
|
{
|
|
CGAL::Three::Scene_item* item = scene->item(selectedItemIndex);
|
|
if(!item) return;
|
|
|
|
const char* prop_name = "Menu modified by MainWindow.";
|
|
|
|
QMenu* menu = item->contextMenu();
|
|
if(menu) {
|
|
bool menuChanged = menu->property(prop_name).toBool();
|
|
if(!menuChanged) {
|
|
if(item->has_stats())
|
|
{
|
|
QAction* actionStatistics =
|
|
menu->addAction(tr("Statistics..."));
|
|
actionStatistics->setObjectName("actionStatisticsOnPolyhedron");
|
|
connect(actionStatistics, SIGNAL(triggered()),
|
|
this, SLOT(statisticsOnItem()));
|
|
}
|
|
menu->addSeparator();
|
|
if(!item->property("source filename").toString().isEmpty()) {
|
|
QAction* reload = menu->addAction(tr("&Reload Item from File"));
|
|
reload->setProperty("is_groupable", true);
|
|
connect(reload, SIGNAL(triggered()),
|
|
this, SLOT(reloadItem()));
|
|
}
|
|
QAction* saveas = menu->addAction(tr("&Save as..."));
|
|
saveas->setData(QVariant::fromValue((void*)item));
|
|
connect(saveas, SIGNAL(triggered()),
|
|
this, SLOT(on_actionSaveAs_triggered()));
|
|
QAction* showobject = menu->addAction(tr("&Zoom to this Object"));
|
|
showobject->setData(QVariant::fromValue((void*)item));
|
|
connect(showobject, SIGNAL(triggered()),
|
|
this, SLOT(viewerShowObject()));
|
|
|
|
menu->setProperty(prop_name, true);
|
|
}
|
|
}
|
|
menu->addMenu(ui->menuOperations);
|
|
|
|
if(menu)
|
|
menu->exec(global_pos);
|
|
}
|
|
|
|
void MainWindow::showSceneContextMenu(const QPoint& p) {
|
|
QWidget* sender = qobject_cast<QWidget*>(this->sender());
|
|
if(!sender) return;
|
|
if(scene->selectionIndices().isEmpty())return;
|
|
int main_index = scene->selectionIndices().first();
|
|
|
|
if(sender == sceneView) {
|
|
QModelIndex modelIndex = sceneView->indexAt(p);
|
|
if(!modelIndex.isValid())
|
|
{
|
|
const char* prop_name = "Menu modified by MainWindow.";
|
|
|
|
QMenu* menu = ui->menuFile;
|
|
if(menu) {
|
|
bool menuChanged = menu->property(prop_name).toBool();
|
|
if(!menuChanged) {
|
|
menu->setProperty(prop_name, true);
|
|
}
|
|
}
|
|
if(menu)
|
|
menu->exec(sender->mapToGlobal(p));
|
|
return;
|
|
}
|
|
else if(scene->selectionIndices().size() > 1 )
|
|
{
|
|
QMap<QString, QAction*> menu_actions;
|
|
QVector<QMenu*> slider_menus;
|
|
bool has_stats = false;
|
|
bool has_reload = false;
|
|
for(Scene::Item_id id : scene->selectionIndices())
|
|
{
|
|
if(!scene->item(id)->property("source filename").toString().isEmpty())
|
|
{
|
|
has_reload = true;
|
|
break;
|
|
}
|
|
}
|
|
for(QAction* action : scene->item(main_index)->contextMenu()->actions())
|
|
{
|
|
if(action->property("is_groupable").toBool())
|
|
{
|
|
menu_actions[action->text()] = action;
|
|
if(action->text() == QString("Alpha value"))
|
|
{
|
|
menu_actions["alpha slider"] = action->menu()->actions().last();
|
|
}
|
|
else if(action->text() == QString("Points Size"))
|
|
{
|
|
menu_actions["points slider"] = action->menu()->actions().last();
|
|
}
|
|
else if(action->text() == QString("Normals Length"))
|
|
{
|
|
menu_actions["normals slider"] = action->menu()->actions().last();
|
|
}
|
|
else if(action->text() == QString("Line Width"))
|
|
{
|
|
menu_actions["Line width"] = action->menu()->actions().last();
|
|
}
|
|
}
|
|
|
|
}
|
|
for(Scene::Item_id index : scene->selectionIndices())
|
|
{
|
|
if(index == main_index)
|
|
continue;
|
|
|
|
CGAL::Three::Scene_item* item = scene->item(index);
|
|
if(!item)
|
|
continue;
|
|
if(item->has_stats())
|
|
has_stats = true;
|
|
}
|
|
QMenu menu;
|
|
menu.addAction(actionAddToGroup);
|
|
menu.insertSeparator(nullptr);
|
|
for(QString name : menu_actions.keys())
|
|
{
|
|
if(name == QString("alpha slider")
|
|
|| name == QString("points slider")
|
|
|| name == QString("normals slider"))
|
|
continue;
|
|
if(name == QString("Alpha value"))
|
|
{
|
|
QWidgetAction* sliderAction = new QWidgetAction(&menu);
|
|
QSlider* slider = new QSlider(&menu);
|
|
slider->setMinimum(0);
|
|
slider->setMaximum(255);
|
|
slider->setValue(
|
|
qobject_cast<QSlider*>(
|
|
qobject_cast<QWidgetAction*>
|
|
(menu_actions["alpha slider"])->defaultWidget()
|
|
)->value());
|
|
slider->setOrientation(Qt::Horizontal);
|
|
sliderAction->setDefaultWidget(slider);
|
|
|
|
connect(slider, &QSlider::valueChanged, [this, slider]()
|
|
{
|
|
for(Scene::Item_id id : scene->selectionIndices())
|
|
{
|
|
Scene_item* item = scene->item(id);
|
|
for(QAction* action : item->contextMenu()->actions())
|
|
{
|
|
if(action->text() == "Alpha value")
|
|
{
|
|
QWidgetAction* sliderAction = qobject_cast<QWidgetAction*>(action->menu()->actions().last());
|
|
QSlider* ac_slider = qobject_cast<QSlider*>(sliderAction->defaultWidget());
|
|
ac_slider->setValue(slider->value());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
QMenu* new_menu = new QMenu("Alpha value", &menu);
|
|
new_menu->addAction(sliderAction);
|
|
slider_menus.push_back(new_menu);
|
|
}
|
|
else if(name == QString("Points Size"))
|
|
{
|
|
QWidgetAction* sliderAction = new QWidgetAction(&menu);
|
|
QSlider* slider = new QSlider(&menu);
|
|
slider->setMinimum(1);
|
|
slider->setMaximum(25);
|
|
slider->setValue(
|
|
qobject_cast<QSlider*>(
|
|
qobject_cast<QWidgetAction*>
|
|
(menu_actions["points slider"])->defaultWidget()
|
|
)->value());
|
|
slider->setOrientation(Qt::Horizontal);
|
|
sliderAction->setDefaultWidget(slider);
|
|
|
|
connect(slider, &QSlider::valueChanged, [this, slider]()
|
|
{
|
|
for(Scene::Item_id id : scene->selectionIndices())
|
|
{
|
|
Scene_item* item = scene->item(id);
|
|
for(QAction* action : item->contextMenu()->actions())
|
|
{
|
|
if(action->text() == "Points Size")
|
|
{
|
|
QWidgetAction* sliderAction = qobject_cast<QWidgetAction*>(action->menu()->actions().last());
|
|
QSlider* ac_slider = qobject_cast<QSlider*>(sliderAction->defaultWidget());
|
|
ac_slider->setValue(slider->value());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
QMenu* new_menu = new QMenu("Points Size", &menu);
|
|
new_menu->addAction(sliderAction);
|
|
slider_menus.push_back(new_menu);
|
|
}
|
|
else if(name == QString("Normals Length"))
|
|
{
|
|
QWidgetAction* sliderAction = new QWidgetAction(&menu);
|
|
QSlider* slider = new QSlider(&menu);
|
|
slider->setMinimum(0);
|
|
slider->setMaximum(100);
|
|
slider->setValue(
|
|
qobject_cast<QSlider*>(
|
|
qobject_cast<QWidgetAction*>
|
|
(menu_actions["normals slider"])->defaultWidget()
|
|
)->value());
|
|
slider->setOrientation(Qt::Horizontal);
|
|
sliderAction->setDefaultWidget(slider);
|
|
|
|
connect(slider, &QSlider::valueChanged, [this, slider]()
|
|
{
|
|
for(Scene::Item_id id : scene->selectionIndices())
|
|
{
|
|
Scene_item* item = scene->item(id);
|
|
for(QAction* action : item->contextMenu()->actions())
|
|
{
|
|
if(action->text() == "Normals Length")
|
|
{
|
|
QWidgetAction* sliderAction = qobject_cast<QWidgetAction*>(action->menu()->actions().last());
|
|
QSlider* ac_slider = qobject_cast<QSlider*>(sliderAction->defaultWidget());
|
|
ac_slider->setValue(slider->value());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
QMenu* new_menu = new QMenu("Normals Length", &menu);
|
|
new_menu->addAction(sliderAction);
|
|
slider_menus.push_back(new_menu);
|
|
}
|
|
else if(name == QString("Line Width"))
|
|
{
|
|
QWidgetAction* sliderAction = new QWidgetAction(&menu);
|
|
QSlider* slider = new QSlider(&menu);
|
|
slider->setMinimum(1);
|
|
float lineWidth[2];
|
|
if(!viewer->isOpenGL_4_3())
|
|
viewer->glGetFloatv(GL_LINE_WIDTH_RANGE, lineWidth);
|
|
else
|
|
{
|
|
lineWidth[0] = 0;
|
|
lineWidth[1] = 10;
|
|
}
|
|
slider->setMaximum(lineWidth[1]);
|
|
slider->setValue(
|
|
qobject_cast<QSlider*>(
|
|
qobject_cast<QWidgetAction*>
|
|
(menu_actions["Line width"])->defaultWidget()
|
|
)->value());
|
|
slider->setOrientation(Qt::Horizontal);
|
|
sliderAction->setDefaultWidget(slider);
|
|
|
|
connect(slider, &QSlider::valueChanged, [this, slider]()
|
|
{
|
|
for(Scene::Item_id id : scene->selectionIndices())
|
|
{
|
|
Scene_item* item = scene->item(id);
|
|
for(QAction* action : item->contextMenu()->actions())
|
|
{
|
|
if(action->text() == "Line Width")
|
|
{
|
|
QWidgetAction* sliderAction = qobject_cast<QWidgetAction*>(action->menu()->actions().last());
|
|
QSlider* ac_slider = qobject_cast<QSlider*>(sliderAction->defaultWidget());
|
|
ac_slider->setValue(slider->value());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
QMenu* new_menu = new QMenu("Line Width", &menu);
|
|
new_menu->addAction(sliderAction);
|
|
slider_menus.push_back(new_menu);
|
|
}
|
|
else
|
|
{
|
|
QAction* action = menu.addAction(name);
|
|
connect(action, &QAction::triggered, this, &MainWindow::propagate_action);
|
|
}
|
|
}
|
|
if(!slider_menus.empty())
|
|
{
|
|
for(QMenu* m : slider_menus){
|
|
menu.addMenu(m);
|
|
}
|
|
menu.insertSeparator(nullptr);
|
|
}
|
|
if(has_stats)
|
|
{
|
|
QAction* actionStatistics =
|
|
menu.addAction(tr("Statistics..."));
|
|
actionStatistics->setObjectName("actionStatisticsOnPolyhedron");
|
|
connect(actionStatistics, SIGNAL(triggered()),
|
|
this, SLOT(statisticsOnItem()));
|
|
}
|
|
if(has_reload)
|
|
{
|
|
QAction* reload = menu.addAction(tr("&Reload Item from File"));
|
|
reload->setProperty("is_groupable", true);
|
|
connect(reload, SIGNAL(triggered()),
|
|
this, SLOT(reloadItem()));
|
|
}
|
|
QAction* saveas = menu.addAction(tr("&Save as..."));
|
|
connect(saveas, SIGNAL(triggered()),
|
|
this, SLOT(on_actionSaveAs_triggered()));
|
|
menu.addMenu(ui->menuOperations);
|
|
menu.exec(sender->mapToGlobal(p));
|
|
return;
|
|
}
|
|
}
|
|
showSceneContextMenu(main_index, sender->mapToGlobal(p));
|
|
return;
|
|
}
|
|
|
|
void MainWindow::removeManipulatedFrame(CGAL::Three::Scene_item* item)
|
|
{
|
|
if(item->manipulatable() &&
|
|
item->manipulatedFrame() == viewer->manipulatedFrame()) {
|
|
viewer->setManipulatedFrame(nullptr);
|
|
}
|
|
}
|
|
|
|
void MainWindow::updateInfo() {
|
|
CGAL::Three::Scene_item* item = scene->item(getSelectedSceneItemIndex());
|
|
if(item) {
|
|
QString item_text = item->toolTip();
|
|
QString item_filename = item->property("source filename").toString();
|
|
CGAL::Bbox_3 bbox = item->bbox();
|
|
if(bbox !=CGAL::Bbox_3())
|
|
item_text += QString("<div>Bounding box:<br> min (%1, %2, %3),<br> max (%4, %5, %6),<br> dimensions (%7, %8, %9)</div>")
|
|
.arg(bbox.xmin(),0, 'g', 17)
|
|
.arg(bbox.ymin(),0, 'g', 17)
|
|
.arg(bbox.zmin(),0, 'g', 17)
|
|
.arg(bbox.xmax(),0, 'g', 17)
|
|
.arg(bbox.ymax(),0, 'g', 17)
|
|
.arg(bbox.zmax(),0, 'g', 17)
|
|
.arg(bbox.xmax() - bbox.xmin(), 0, 'g', 17)
|
|
.arg(bbox.ymax() - bbox.ymin(), 0, 'g', 17)
|
|
.arg(bbox.zmax() - bbox.zmin(), 0, 'g', 17);
|
|
if(!item_filename.isEmpty()) {
|
|
item_text += QString("<div>File:<i> %1</div>").arg(item_filename);
|
|
}
|
|
ui->infoLabel->setText(item_text);
|
|
}
|
|
else
|
|
ui->infoLabel->clear();
|
|
}
|
|
void MainWindow::updateDisplayInfo() {
|
|
CGAL::Three::Scene_item* item = scene->item(getSelectedSceneItemIndex());
|
|
if(item)
|
|
ui->displayLabel->setPixmap(item->graphicalToolTip());
|
|
else
|
|
ui->displayLabel->clear();
|
|
|
|
}
|
|
|
|
void MainWindow::readSettings()
|
|
{
|
|
viewer->setAntiAliasing(settings.value("antialiasing", false).toBool());
|
|
viewer->setFastDrawing(settings.value("quick_camera_mode", true).toBool());
|
|
scene->enableVisibilityRecentering(settings.value("offset_update", false).toBool());
|
|
viewer->textRenderer()->setMax(settings.value("max_text_items", 10000).toInt());
|
|
int val = settings.value("transparency_pass_number", 4).toInt();
|
|
if (val < 4 ) {
|
|
val = 4;
|
|
settings.setValue("transparency_pass_number", 4);
|
|
}
|
|
viewer->setTotalPass(val);
|
|
CGAL::Three::Three::s_defaultSMRM = CGAL::Three::Three::modeFromName(
|
|
settings.value("default_sm_rm", "flat+edges").toString());
|
|
CGAL::Three::Three::s_defaultPSRM = CGAL::Three::Three::modeFromName(
|
|
settings.value("default_ps_rm", "points").toString());
|
|
// read plugin blacklist
|
|
QStringList blacklist=settings.value("plugin_blacklist",QStringList()).toStringList();
|
|
for(QString name :blacklist){ plugin_blacklist.insert(name); }
|
|
def_save_dir = settings.value("default_saveas_dir", QDir::homePath()).toString();
|
|
this->default_point_size = settings.value("points_size").toInt();
|
|
this->default_normal_length = settings.value("normals_length").toInt();
|
|
this->default_lines_width = settings.value("lines_width").toInt();
|
|
setProperty("ws_url", settings.value("ws_server_url").toString());
|
|
}
|
|
|
|
void MainWindow::writeSettings()
|
|
{
|
|
this->writeState("MainWindow");
|
|
{
|
|
//setting plugin blacklist
|
|
QStringList blacklist;
|
|
for(QString name :plugin_blacklist){ blacklist << name; }
|
|
if ( !blacklist.isEmpty() ) settings.setValue("plugin_blacklist",blacklist);
|
|
else settings.remove("plugin_blacklist");
|
|
}
|
|
std::cerr << "Write setting... done.\n";
|
|
}
|
|
|
|
void MainWindow::quit()
|
|
{
|
|
close();
|
|
}
|
|
|
|
void MainWindow::closeEvent(QCloseEvent *event)
|
|
{
|
|
for(int i=0; i<plugins.size(); i++)
|
|
{
|
|
plugins[i].first->closure();
|
|
}
|
|
writeSettings();
|
|
event->accept();
|
|
}
|
|
|
|
|
|
bool MainWindow::loadScript(QString filename){
|
|
QFileInfo fileinfo(filename);
|
|
std::optional<bool> opt = wrap_a_call_to_cpp
|
|
([this, fileinfo] {
|
|
return loadScript(fileinfo);
|
|
}, this, __FILE__, __LINE__);
|
|
if(!opt) return false;
|
|
else return *opt;
|
|
}
|
|
|
|
bool MainWindow::loadScript(QFileInfo info)
|
|
{
|
|
|
|
QString program;
|
|
QString filename = info.absoluteFilePath();
|
|
QFile script_file(filename);
|
|
bool success = script_file.open(QIODevice::ReadOnly);
|
|
if((! success) || (!script_file.isReadable())) {
|
|
throw std::ios_base::failure(script_file.errorString().toStdString());
|
|
}
|
|
program = script_file.readAll();
|
|
if(!program.isEmpty())
|
|
{
|
|
QTextStream(stderr)
|
|
<< "Execution of script \""
|
|
<< filename << "\"\n";
|
|
evaluate_script(program, filename);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
void MainWindow::throw_exception() {
|
|
wrap_a_call_to_cpp([]() {
|
|
throw std::runtime_error("Exception thrown in "
|
|
"MainWindow::throw_exception()");
|
|
}, this, __FILE__, __LINE__);
|
|
}
|
|
|
|
void MainWindow::on_actionLoadScript_triggered()
|
|
{
|
|
|
|
}
|
|
|
|
void MainWindow::on_actionLoad_triggered()
|
|
{
|
|
QStringList filters;
|
|
// we need to special case our way out of this
|
|
filters << "All Files (*)";
|
|
|
|
|
|
typedef QMap<QString, CGAL::Three::CGAL_Lab_io_plugin_interface*> FilterPluginMap;
|
|
FilterPluginMap filterPluginMap;
|
|
|
|
for(CGAL::Three::CGAL_Lab_io_plugin_interface* plugin : io_plugins) {
|
|
QStringList split_filters = plugin->loadNameFilters().split(";;");
|
|
for(const QString& filter : split_filters) {
|
|
FilterPluginMap::iterator it = filterPluginMap.find(filter);
|
|
if(it != filterPluginMap.end()) {
|
|
if(verbose)
|
|
{
|
|
qDebug() << "Duplicate Filter: " << it.value()->name();
|
|
qDebug() << "This filter will not be available.";
|
|
}
|
|
} else {
|
|
filterPluginMap[filter] = plugin;
|
|
}
|
|
filters << filter;
|
|
}
|
|
}
|
|
|
|
QString directory = settings.value("OFF open directory",
|
|
QDir::current().dirName()).toString();
|
|
|
|
QFileDialog dialog(this);
|
|
dialog.setDirectory(directory);
|
|
dialog.setNameFilters(filters);
|
|
dialog.setFileMode(QFileDialog::ExistingFiles);
|
|
|
|
if(dialog.exec() != QDialog::Accepted) { return; }
|
|
for(auto v : CGAL::QGLViewer::QGLViewerPool())
|
|
v->update();
|
|
FilterPluginMap::iterator it =
|
|
filterPluginMap.find(dialog.selectedNameFilter());
|
|
|
|
CGAL::Three::CGAL_Lab_io_plugin_interface* selectedPlugin = nullptr;
|
|
|
|
if(it != filterPluginMap.end()) {
|
|
selectedPlugin = it.value();
|
|
}
|
|
|
|
std::size_t nb_files = dialog.selectedFiles().size();
|
|
std::vector<QColor> colors_;
|
|
colors_.reserve(nb_files);
|
|
compute_color_map(QColor(100, 100, 255),//Scene_item's default color
|
|
static_cast<unsigned>(nb_files),
|
|
std::back_inserter(colors_));
|
|
std::size_t nb_item = -1;
|
|
|
|
for(const QString& filename : dialog.selectedFiles()) {
|
|
|
|
CGAL::Three::Scene_item* item = nullptr;
|
|
if(selectedPlugin) {
|
|
QFileInfo info(filename);
|
|
bool ok;
|
|
QList<Scene_item*> result = loadItem(info, selectedPlugin, ok);
|
|
if(!ok)
|
|
continue;
|
|
for(Scene_item* item : result)
|
|
{
|
|
if(!item->property("already_colored").toBool())
|
|
{
|
|
++nb_item;
|
|
item->setColor(colors_[nb_item]);
|
|
}
|
|
selectSceneItem(scene->item_id(item));
|
|
CGAL::Three::Scene_group_item* group =
|
|
qobject_cast<CGAL::Three::Scene_group_item*>(item);
|
|
if(group)
|
|
scene->redraw_model();
|
|
}
|
|
this->addToRecentFiles(filename);
|
|
} else {
|
|
int scene_size = scene->numberOfEntries();
|
|
open(filename);
|
|
item = scene->item(scene->numberOfEntries()-1);
|
|
if(scene->numberOfEntries() != scene_size
|
|
&& !item->property("already_colored").toBool())
|
|
item->setColor(colors_[++nb_item]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::on_actionSaveAs_triggered()
|
|
{
|
|
QList<Scene_item*> to_save;
|
|
for(Scene::Item_id id : scene->selectionIndices())
|
|
{
|
|
Scene_item* item = scene->item(id);
|
|
to_save.append(item);
|
|
}
|
|
while(!to_save.empty())
|
|
{
|
|
Scene_item* item = to_save.front();
|
|
QVector<CGAL::Three::CGAL_Lab_io_plugin_interface*> canSavePlugins;
|
|
QStringList filters;
|
|
QString sf;
|
|
for(CGAL::Three::CGAL_Lab_io_plugin_interface* plugin : io_plugins) {
|
|
if(plugin->canSave(item)) {
|
|
canSavePlugins << plugin;
|
|
filters += plugin->saveNameFilters();
|
|
if(plugin->isDefaultLoader(item))
|
|
sf = plugin->saveNameFilters().split(";;").first();
|
|
}
|
|
}
|
|
QRegularExpression extensions("\\(\\*\\..+\\)");
|
|
QStringList filter_exts;
|
|
if(filters.empty())
|
|
{
|
|
QMessageBox::warning(this,
|
|
tr("Cannot save"),
|
|
tr("The selected object %1 cannot be saved.")
|
|
.arg(item->name()));
|
|
return;
|
|
}
|
|
|
|
for(QString string: filters)
|
|
{
|
|
QStringList sl = string.split(";;");
|
|
for(QString s: sl){
|
|
QRegularExpressionMatch match = extensions.match(s);
|
|
if(match.hasMatch())
|
|
filter_exts.append(match.capturedTexts());
|
|
}
|
|
}
|
|
|
|
filters << tr("All files (*)");
|
|
if(canSavePlugins.isEmpty()) {
|
|
QMessageBox::warning(this,
|
|
tr("Cannot save"),
|
|
tr("The selected object %1 cannot be saved.")
|
|
.arg(item->name()));
|
|
continue;
|
|
}
|
|
QString caption = tr("Save %1 to File...").arg(item->name());
|
|
QString dir = item->property("source filename").toString();
|
|
if(dir.isEmpty() &&
|
|
!item->property("defaultSaveDir").toString().isEmpty())
|
|
{
|
|
dir = item->property("defaultSaveDir").toString();
|
|
}
|
|
else if(!last_saved_dir.isEmpty() && dir.isEmpty())
|
|
dir = QString("%1/%2").arg(last_saved_dir).arg(item->defaultSaveName());
|
|
else if(dir.isEmpty())
|
|
dir = QString("%1/%2").arg(def_save_dir).arg(item->name());
|
|
QString filename =
|
|
QFileDialog::getSaveFileName(this,
|
|
caption,
|
|
dir,
|
|
filters.join(";;"),
|
|
&sf);
|
|
|
|
if(filename.isEmpty())
|
|
return;
|
|
last_saved_dir = QFileInfo(filename).absoluteDir().path();
|
|
auto match = extensions.match(sf.split(";;").first());
|
|
QString filter_ext, filename_ext;
|
|
filter_ext = match.captured().split(" ").first();// in case of syntax like (*.a *.b)
|
|
|
|
filter_ext.remove(")");
|
|
filter_ext.remove("(");
|
|
//remove *
|
|
filter_ext=filter_ext.right(filter_ext.size()-1);
|
|
|
|
QStringList filename_split = filename.split(".");
|
|
filename_split.removeFirst();
|
|
filename_ext = filename_split.join(".");
|
|
filename_ext.push_front(".");
|
|
|
|
QStringList final_extensions;
|
|
for(QString string : filter_exts)
|
|
{
|
|
for(QString s : string.split(" ")){// in case of syntax like (*.a *.b)
|
|
s.remove(")");
|
|
s.remove("(");
|
|
//remove *
|
|
s=s.right(s.size()-1);
|
|
final_extensions.append(s);
|
|
}
|
|
}
|
|
bool ok = false;
|
|
while(!ok)
|
|
{
|
|
if(final_extensions.contains(filename_ext))
|
|
{
|
|
ok = true;
|
|
}
|
|
else{
|
|
QStringList shatterd_filename_ext = filename_ext.split(".");
|
|
if(!shatterd_filename_ext.last().isEmpty())
|
|
{
|
|
shatterd_filename_ext.removeFirst();//removes ""
|
|
shatterd_filename_ext.removeFirst();
|
|
filename_ext = shatterd_filename_ext.join(".");
|
|
filename_ext.push_front(".");
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
if(!ok)
|
|
{
|
|
filename = filename.append(filter_ext);
|
|
}
|
|
for(auto v : CGAL::QGLViewer::QGLViewerPool())
|
|
v->update();
|
|
save(filename, to_save);
|
|
}
|
|
}
|
|
|
|
void MainWindow::save(QString filename, QList<CGAL::Three::Scene_item*>& to_save) {
|
|
QFileInfo fileinfo(filename);
|
|
bool saved = false;
|
|
for(CGAL::Three::CGAL_Lab_io_plugin_interface* plugin : io_plugins) {
|
|
if( plugin->canSave(to_save.front()) &&
|
|
file_matches_filter(plugin->saveNameFilters(),filename.toLower()) )
|
|
{
|
|
if(plugin->save(fileinfo, to_save))
|
|
{
|
|
saved = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if(!saved)
|
|
{
|
|
QMessageBox::warning(this,
|
|
tr("Cannot save"),
|
|
tr("The selected object %1 was not saved. (Maybe a wrong extension ?)")
|
|
.arg(to_save.front()->name()));
|
|
to_save.pop_front();
|
|
}
|
|
}
|
|
|
|
void MainWindow::on_actionSaveSnapshot_triggered()
|
|
{
|
|
viewer->saveSnapshot();
|
|
}
|
|
|
|
bool MainWindow::on_actionErase_triggered()
|
|
{
|
|
int next_index = scene->erase(scene->selectionIndices());
|
|
//Secure the case where erase triggers other items deletions
|
|
if(scene->numberOfEntries()< next_index +1 )
|
|
next_index = -1;
|
|
selectSceneItem(next_index);
|
|
return next_index >= 0;
|
|
}
|
|
|
|
void MainWindow::on_actionEraseAll_triggered()
|
|
{
|
|
QList<int> all_ids;
|
|
for(int i = 0; i < scene->numberOfEntries(); ++i)
|
|
all_ids.push_back(i);
|
|
scene->setSelectedItemIndices(all_ids);
|
|
on_actionErase_triggered();
|
|
}
|
|
|
|
void MainWindow::on_actionDuplicate_triggered()
|
|
{
|
|
int index = scene->duplicate(getSelectedSceneItemIndex());
|
|
selectSceneItem(index);
|
|
}
|
|
|
|
void MainWindow::on_actionShowHide_triggered()
|
|
{
|
|
scene->setUpdatesEnabled(false);
|
|
for(QModelIndex index : sceneView->selectionModel()->selectedRows())
|
|
{
|
|
int i = scene->getIdFromModelIndex(proxyModel->mapToSource(index));
|
|
CGAL::Three::Scene_item* item = scene->item(i);
|
|
item->setVisible(!item->visible());
|
|
item->redraw();
|
|
}
|
|
scene->setUpdatesEnabled(true);
|
|
// updateViewersBboxes(false); //Not usable :when the scene changes scale, smaller items disappear.
|
|
}
|
|
|
|
void MainWindow::on_actionSetPolyhedronA_triggered()
|
|
{
|
|
int i = getSelectedSceneItemIndex();
|
|
scene->setItemA(i);
|
|
}
|
|
|
|
void MainWindow::on_actionSetPolyhedronB_triggered()
|
|
{
|
|
int i = getSelectedSceneItemIndex();
|
|
scene->setItemB(i);
|
|
}
|
|
|
|
void MainWindow::on_actionPreferences_triggered()
|
|
{
|
|
QDialog dialog(this);
|
|
Ui::PreferencesDialog prefdiag;
|
|
prefdiag.setupUi(&dialog);
|
|
|
|
float lineWidth[2];
|
|
if(!viewer->isOpenGL_4_3())
|
|
viewer->glGetFloatv(GL_LINE_WIDTH_RANGE, lineWidth);
|
|
else
|
|
{
|
|
lineWidth[0] = 0;
|
|
lineWidth[1] = 10;
|
|
}
|
|
prefdiag.linesHorizontalSlider->setMinimum(lineWidth[0]);
|
|
prefdiag.linesHorizontalSlider->setMaximum(lineWidth[1]);
|
|
|
|
prefdiag.offset_updateCheckBox->setChecked(
|
|
settings.value("offset_update", false).toBool());
|
|
connect(prefdiag.offset_updateCheckBox, SIGNAL(toggled(bool)),
|
|
scene, SLOT(enableVisibilityRecentering(bool)));
|
|
|
|
prefdiag.antialiasingCheckBox->setChecked(settings.value("antialiasing", false).toBool());
|
|
connect(prefdiag.antialiasingCheckBox, SIGNAL(toggled(bool)),
|
|
viewer, SLOT(setAntiAliasing(bool)));
|
|
|
|
prefdiag.quick_cameraCheckBox->setChecked(
|
|
settings.value("quick_camera_mode", true).toBool());
|
|
connect(prefdiag.quick_cameraCheckBox, SIGNAL(toggled(bool)),
|
|
viewer, SLOT(setFastDrawing(bool)));
|
|
prefdiag.max_itemsSpinBox->setValue(viewer->textRenderer()->getMax_textItems());
|
|
|
|
connect(prefdiag.max_itemsSpinBox,static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
|
|
this, [this](int i){
|
|
setMaxTextItemsDisplayed(i);
|
|
});
|
|
prefdiag.transpSpinBox->setValue(viewer->total_pass());
|
|
connect(prefdiag.transpSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
|
|
this, [this](int i)
|
|
{
|
|
setTransparencyPasses(i);
|
|
});
|
|
prefdiag.pointsHorizontalSlider->setValue(this->default_point_size);
|
|
connect(prefdiag.pointsHorizontalSlider, &QSlider::valueChanged,
|
|
this, [this](int i)
|
|
{
|
|
this->default_point_size = i;
|
|
});
|
|
prefdiag.normalsHorizontalSlider->setValue(this->default_normal_length);
|
|
connect(prefdiag.normalsHorizontalSlider, &QSlider::valueChanged,
|
|
this, [this](int i)
|
|
{
|
|
this->default_normal_length = i;
|
|
});
|
|
prefdiag.linesHorizontalSlider->setValue(this->default_lines_width);
|
|
connect(prefdiag.linesHorizontalSlider, &QSlider::valueChanged,
|
|
this, [this](int i)
|
|
{
|
|
this->default_lines_width = i;
|
|
});
|
|
connect(prefdiag.background_colorPushButton, &QPushButton::clicked,
|
|
this, &MainWindow::setBackgroundColor);
|
|
|
|
connect(prefdiag.default_save_asPushButton, &QPushButton::clicked,
|
|
this, &MainWindow::setDefaultSaveDir);
|
|
|
|
connect(prefdiag.lightingPushButton, &QPushButton::clicked,
|
|
this, &MainWindow::setLighting_triggered);
|
|
|
|
prefdiag.surface_meshComboBox->setCurrentText(CGAL::Three::Three::modeName(
|
|
CGAL::Three::Three::s_defaultSMRM));
|
|
connect(prefdiag.surface_meshComboBox, &QComboBox::currentTextChanged,
|
|
this, [this](const QString& text){
|
|
this->s_defaultSMRM = CGAL::Three::Three::modeFromName(text);
|
|
});
|
|
|
|
prefdiag.point_setComboBox->setCurrentText(CGAL::Three::Three::modeName(
|
|
CGAL::Three::Three::s_defaultPSRM));
|
|
connect(prefdiag.point_setComboBox, &QComboBox::currentTextChanged,
|
|
this, [this](const QString& text){
|
|
this->s_defaultPSRM = CGAL::Three::Three::modeFromName(text);
|
|
});
|
|
|
|
connect(prefdiag.backFrontColor_pushButton, &QPushButton::clicked,
|
|
this, [](){
|
|
qobject_cast<Viewer*>(CGAL::Three::Three::activeViewer())->setBackFrontColors();
|
|
});
|
|
|
|
std::vector<QTreeWidgetItem*> items;
|
|
QBrush successBrush(Qt::green),
|
|
errorBrush(Qt::red),
|
|
ignoredBrush(Qt::lightGray);
|
|
|
|
//add blacklisted plugins
|
|
for (QString name : PathNames_map.keys())
|
|
{
|
|
QTreeWidgetItem *item = new QTreeWidgetItem(prefdiag.treeWidget);
|
|
item->setText(1, name);
|
|
if(plugin_blacklist.contains(name)){
|
|
item->setCheckState(0, Qt::Unchecked);
|
|
}
|
|
else{
|
|
item->setCheckState(0, Qt::Checked);
|
|
}
|
|
if(pluginsStatus_map[name] == QString("success"))
|
|
item->setBackground(1, successBrush);
|
|
else if(ignored_map[name]){
|
|
item->setBackground(1, ignoredBrush);
|
|
}
|
|
else{
|
|
item->setBackground(1, errorBrush);
|
|
}
|
|
items.push_back(item);
|
|
}
|
|
connect(prefdiag.detailsPushButton, &QPushButton::clicked,
|
|
this, [this, prefdiag](){
|
|
QStringList titles;
|
|
titles << "Name" << "Keywords" << "ConfigDate";
|
|
QDialog dialog(this);
|
|
Ui::DetailsDialog detdiag;
|
|
detdiag.setupUi(&dialog);
|
|
QTreeWidgetItem *header = new QTreeWidgetItem(titles);
|
|
detdiag.treeWidget->setHeaderItem(header);
|
|
for(QTreeWidgetItem* plugin_item : prefdiag.treeWidget->selectedItems())
|
|
{
|
|
QString name = plugin_item->text(1);
|
|
QString keywords = plugin_metadata_map[name].first.join(", ");
|
|
QString date = plugin_metadata_map[name].second;
|
|
QStringList values;
|
|
values << name << keywords << date;
|
|
new QTreeWidgetItem(detdiag.treeWidget, values);
|
|
}
|
|
for(int i=0; i<3; ++i)
|
|
{
|
|
detdiag.treeWidget->resizeColumnToContents(i);
|
|
}
|
|
connect(detdiag.treeWidget, &QTreeWidget::clicked,
|
|
this, [this, detdiag](){
|
|
if(detdiag.treeWidget->selectedItems().isEmpty())
|
|
detdiag.textBrowser->setText("");
|
|
else {
|
|
QString name = detdiag.treeWidget->selectedItems().first()->text(0);
|
|
QString status = pluginsStatus_map[name];
|
|
QString path = PathNames_map[name];
|
|
detdiag.textBrowser->setText(QString("Path: %1 \nStatus: %2").arg(path).arg(status));
|
|
}
|
|
});
|
|
dialog.exec();
|
|
});
|
|
connect(prefdiag.sshButton, &QPushButton::clicked,
|
|
this, [this](){
|
|
QDialog dialog(this);
|
|
Ui::SSHDialog sshdiag;
|
|
sshdiag.setupUi(&dialog);
|
|
|
|
#ifdef CGAL_USE_SSH
|
|
sshdiag.userBox->setEnabled(true);
|
|
sshdiag.serverBox->setEnabled(true);
|
|
sshdiag.pkBox->setEnabled(true);
|
|
sshdiag.privkBox->setEnabled(true);
|
|
sshdiag.userEdit->setText(settings.value("ssh_user", QString()).toString());
|
|
sshdiag.serverEdit->setText(settings.value("ssh_server", QString()).toString());
|
|
sshdiag.publicEdit->setText(settings.value("ssh_public_key", QString()).toString());
|
|
sshdiag.privkEdit->setText(settings.value("ssh_priv_key", QString()).toString());
|
|
connect(sshdiag.pubButton, &QPushButton::clicked,
|
|
this, [this, sshdiag](){
|
|
QFileDialog diag(this,
|
|
"Public Key",
|
|
"",
|
|
"All Files (*)");
|
|
diag.setFilter(QDir::Hidden|QDir::Files|QDir::Dirs|QDir::NoDotAndDotDot);
|
|
if(!diag.exec())
|
|
return;
|
|
sshdiag.publicEdit->setText(diag.selectedFiles().front());
|
|
});
|
|
connect(sshdiag.privButton, &QPushButton::clicked,
|
|
this, [this, sshdiag](){
|
|
QFileDialog diag(this,
|
|
"Private Key",
|
|
"",
|
|
"All Files (*)");
|
|
diag.setFilter(QDir::Hidden|QDir::Files|QDir::Dirs|QDir::NoDotAndDotDot);
|
|
if(!diag.exec())
|
|
return;
|
|
sshdiag.privkEdit->setText(diag.selectedFiles().front());
|
|
});
|
|
#else
|
|
sshdiag.userBox->setEnabled(false);
|
|
sshdiag.serverBox->setEnabled(false);
|
|
sshdiag.pkBox->setEnabled(false);
|
|
sshdiag.privkBox->setEnabled(false);
|
|
#endif
|
|
sshdiag.wsEdit->setText(settings.value("ws_server_url", QString()).toString());
|
|
|
|
dialog.exec();
|
|
if ( dialog.result() )
|
|
{
|
|
#ifdef CGAL_USE_SSH
|
|
settings.setValue("ssh_user",
|
|
sshdiag.userEdit->text());
|
|
settings.setValue("ssh_server",
|
|
sshdiag.serverEdit->text());
|
|
settings.setValue("ssh_public_key",
|
|
sshdiag.publicEdit->text());
|
|
settings.setValue("ssh_priv_key",
|
|
sshdiag.privkEdit->text());
|
|
#endif
|
|
settings.setValue("ws_server_url",
|
|
sshdiag.wsEdit->text());
|
|
setProperty("ws_url", sshdiag.wsEdit->text());
|
|
}
|
|
});
|
|
|
|
dialog.exec();
|
|
|
|
if ( dialog.result() )
|
|
{
|
|
plugin_blacklist.clear();
|
|
|
|
for (std::size_t k=0; k<items.size(); ++k)
|
|
{
|
|
QTreeWidgetItem* item=items[k];
|
|
if (item->checkState(0)==Qt::Unchecked)
|
|
plugin_blacklist.insert(item->text(1));
|
|
}
|
|
|
|
//write settings
|
|
settings.setValue("antialiasing",
|
|
prefdiag.antialiasingCheckBox->isChecked());
|
|
settings.setValue("offset_update",
|
|
prefdiag.offset_updateCheckBox->isChecked());
|
|
settings.setValue("quick_camera_mode",
|
|
prefdiag.quick_cameraCheckBox->isChecked());
|
|
settings.setValue("transparency_pass_number",
|
|
viewer->total_pass());
|
|
settings.setValue("max_text_items",
|
|
viewer->textRenderer()->getMax_textItems());
|
|
settings.setValue("background_color",viewer->backgroundColor().name());
|
|
settings.setValue("default_sm_rm", CGAL::Three::Three::modeName(
|
|
CGAL::Three::Three::defaultSurfaceMeshRenderingMode()));
|
|
settings.setValue("default_ps_rm", CGAL::Three::Three::modeName(
|
|
CGAL::Three::Three::defaultPointSetRenderingMode()));
|
|
settings.setValue("points_size", this->default_point_size);
|
|
settings.setValue("normals_length", this->default_normal_length);
|
|
settings.setValue("lines_width", this->default_lines_width);
|
|
|
|
}
|
|
else
|
|
{
|
|
readSettings();
|
|
}
|
|
}
|
|
|
|
void MainWindow::setBackgroundColor()
|
|
{
|
|
QColor c = QColorDialog::getColor();
|
|
if(c.isValid()) {
|
|
for(CGAL::QGLViewer* v : CGAL::QGLViewer::QGLViewerPool())
|
|
{
|
|
if(v == nullptr)
|
|
continue;
|
|
v->setBackgroundColor(c);
|
|
v->update();
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::setLighting_triggered()
|
|
{
|
|
qobject_cast<Viewer*>(CGAL::Three::Three::activeViewer())->setLighting();
|
|
}
|
|
|
|
void MainWindow::viewerShowObject()
|
|
{
|
|
Scene_item* item = nullptr;
|
|
QAction* sender_action = qobject_cast<QAction*>(sender());
|
|
if(sender_action && !sender_action->data().isNull()) {
|
|
item = static_cast<Scene_item*>(sender_action->data().value<void*>());
|
|
}
|
|
if(item) {
|
|
const Scene::Bbox bbox = item->bbox();
|
|
CGAL::qglviewer::Vec min{static_cast<float>(bbox.xmin()) + viewer->offset().x,
|
|
static_cast<float>(bbox.ymin()) + viewer->offset().y,
|
|
static_cast<float>(bbox.zmin()) + viewer->offset().z};
|
|
CGAL::qglviewer::Vec max{static_cast<float>(bbox.xmax()) + viewer->offset().x,
|
|
static_cast<float>(bbox.ymax()) + viewer->offset().y,
|
|
static_cast<float>(bbox.zmax()) + viewer->offset().z};
|
|
viewer->setSceneBoundingBox(min, max);
|
|
viewerShow(static_cast<float>(min.x), static_cast<float>(min.y), static_cast<float>(min.z),
|
|
static_cast<float>(max.x), static_cast<float>(max.y), static_cast<float>(max.z));
|
|
}
|
|
}
|
|
/* to check
|
|
QString MainWindow::cameraString() const
|
|
{
|
|
const CGAL::qglviewer::Vec pos = viewer->camera()->position() - viewer->offset();
|
|
const CGAL::qglviewer::Quaternion q = viewer->camera()->orientation();
|
|
|
|
return QString("%1 %2 %3 %4 %5 %6 %7")
|
|
.arg(pos[0])
|
|
.arg(pos[1])
|
|
.arg(pos[2])
|
|
.arg(q[0])
|
|
.arg(q[1])
|
|
.arg(q[2])
|
|
.arg(q[3]);
|
|
}*/
|
|
QString MainWindow::cameraString(CGAL::Three::Viewer_interface* v) const
|
|
{
|
|
return v->dumpCameraCoordinates();
|
|
}
|
|
|
|
void MainWindow::setAddKeyFrameKeyboardModifiers(::Qt::KeyboardModifiers m)
|
|
{
|
|
viewer->setAddKeyFrameKeyboardModifiers(m);
|
|
}
|
|
|
|
void MainWindow::recenterScene()
|
|
{
|
|
//force the recomputation of the bbox
|
|
bbox_need_update = true;
|
|
CGAL::qglviewer::Vec min, max;
|
|
computeViewerBBox(min, max);
|
|
updateViewerBbox(static_cast<Viewer*>(activeViewer()), true, min, max);
|
|
activeViewer()->showEntireScene();
|
|
}
|
|
|
|
void MainWindow::on_actionLoadPlugin_triggered()
|
|
{
|
|
//pop a dialog of path selection, get the path and add it to plugins_directory
|
|
|
|
QString filters("Library files (*.dll *.DLL *.so *.a *.sl *.dylib *.bundle);;"
|
|
"Any files (*)");
|
|
|
|
QStringList paths = QFileDialog::getOpenFileNames(
|
|
this,
|
|
tr("Select the directory containing your plugins:"),
|
|
".",filters);
|
|
for(QString name : paths)
|
|
load_plugin(name, false);
|
|
|
|
updateMenus();
|
|
}
|
|
|
|
void MainWindow::recurseExpand(QModelIndex index)
|
|
{
|
|
int row = index.row();
|
|
if(scene->index(0,0,index).isValid())
|
|
{
|
|
recurseExpand(scene->index(0,0,index));
|
|
}
|
|
CGAL::Three::Scene_group_item* group =
|
|
qobject_cast<CGAL::Three::Scene_group_item*>(scene->item(scene->getIdFromModelIndex(index)));
|
|
if(group && group->isExpanded())
|
|
{
|
|
sceneView->setExpanded(proxyModel->mapFromSource(index), true);
|
|
}
|
|
else if (group && !group->isExpanded()){
|
|
sceneView->setExpanded(proxyModel->mapFromSource(index), false);
|
|
}
|
|
|
|
if( index.sibling(row+1,0).isValid())
|
|
recurseExpand(index.sibling(row+1,0));
|
|
}
|
|
void MainWindow::restoreCollapseState()
|
|
{
|
|
QModelIndex modelIndex = scene->index(0,0,scene->invisibleRootItem()->index());
|
|
if(modelIndex.isValid())
|
|
recurseExpand(modelIndex);
|
|
resetHeader();
|
|
}
|
|
void MainWindow::makeNewGroup()
|
|
{
|
|
Scene_group_item * group = new Scene_group_item();
|
|
scene->addItem(group);
|
|
for(Scene::Item_id id : scene->selectionIndices())
|
|
{
|
|
scene->changeGroup(scene->item(id), group);
|
|
}
|
|
}
|
|
|
|
void MainWindow::on_upButton_pressed()
|
|
{
|
|
scene->moveRowUp();
|
|
}
|
|
|
|
void MainWindow::on_downButton_pressed()
|
|
{
|
|
scene->moveRowDown();
|
|
}
|
|
|
|
void MainWindow::recenterSceneView(const QModelIndex &id)
|
|
{
|
|
if(id.isValid())
|
|
{
|
|
// mapFromSource is necessary to convert the QModelIndex received
|
|
// from the Scene into a valid QModelIndex in the view, because of
|
|
// the proxymodel
|
|
sceneView->scrollTo(proxyModel->mapFromSource(id));
|
|
}
|
|
}
|
|
|
|
void MainWindow::statisticsOnItem()
|
|
{
|
|
QApplication::setOverrideCursor(Qt::WaitCursor);
|
|
|
|
if (statistics_dlg == nullptr)
|
|
{
|
|
statistics_dlg = new QDialog(this);
|
|
statistics_ui->setupUi(statistics_dlg);
|
|
connect(statistics_ui->okButtonBox, SIGNAL(accepted()),
|
|
statistics_dlg, SLOT(accept()));
|
|
connect(statistics_ui->updateButton, SIGNAL(clicked()),
|
|
this, SLOT(statisticsOnItem()));
|
|
connect(statistics_ui->exportButton, &QPushButton::clicked,
|
|
this, &MainWindow::exportStatistics);
|
|
}
|
|
statistics_ui->label_htmltab->setText(get_item_stats());
|
|
|
|
statistics_dlg->show();
|
|
statistics_dlg->raise();
|
|
|
|
QApplication::restoreOverrideCursor();
|
|
}
|
|
|
|
/* Creates a string containing an html table. This string is constructed by appending each parts of each row, so that the data can
|
|
depend on the number of selected items. This String is then returned.*/
|
|
QString MainWindow::get_item_stats()
|
|
{
|
|
//1st step : get all classnames of the selected items
|
|
QList<QString> classnames;
|
|
for(int id : scene->selectionIndices())
|
|
{
|
|
Scene_item* item = scene->item(id);
|
|
QString classname = item->property("classname").toString();
|
|
if(classname.isEmpty())
|
|
classname = item->metaObject()->className();
|
|
if(!classnames.contains(classname))
|
|
classnames << classname;
|
|
}
|
|
//2nd step : separate the selection in lists corresponding to their classname
|
|
QVector< QList<Scene_item*> > items;
|
|
items.resize(classnames.size());
|
|
for(int id : scene->selectionIndices())
|
|
{
|
|
Scene_item* s_item = scene->item(id);
|
|
for(int i=0; i<items.size(); i++)
|
|
{
|
|
Scene_item* item = scene->item(id);
|
|
QString classname = item->property("classname").toString();
|
|
if(classname.isEmpty())
|
|
classname = item->metaObject()->className();
|
|
if(classnames.at(i) == classname)
|
|
{
|
|
items[i] << s_item;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
//last step :: making tables for each type of item
|
|
QString str;
|
|
for(int i=0; i< classnames.size(); i++)
|
|
{
|
|
CGAL::Three::Scene_item::Header_data data = items[i].at(0)->header();
|
|
int title = 0;
|
|
int titles_limit =0;
|
|
if(data.titles.size()>0)
|
|
{
|
|
//1st row : item names
|
|
str.append("<html> <table border=1>""<tr><td colspan = 2></td>");
|
|
for(Scene_item* sit : items[i])
|
|
{
|
|
str.append(QString("<td>%1</td>").arg(sit->name()));
|
|
}
|
|
|
|
|
|
|
|
for(int j=0; j<data.categories.size(); j++)
|
|
{
|
|
str.append(QString("<tr><th rowspan=%1> %2 </th>")
|
|
.arg(QString::number(data.categories[j].second))
|
|
.arg(data.categories[j].first));
|
|
titles_limit+=data.categories[j].second;
|
|
str.append(QString("<td> %1 </td>").arg(data.titles.at(title)));
|
|
for(Scene_item* sit : items[i])
|
|
{
|
|
str.append(QString("<td>%1</td>").arg(sit->computeStats(title)));
|
|
}
|
|
title++;
|
|
for(;title<titles_limit; title++)
|
|
{
|
|
str.append(QString("</tr><tr><td> %1 </td>").arg(data.titles.at(title)));
|
|
for(Scene_item* sit : items[i])
|
|
{
|
|
str.append(QString("<td>%1</td>").arg(sit->computeStats(title)));
|
|
}
|
|
}
|
|
|
|
str.append("</tr>");
|
|
}
|
|
|
|
str.append(QString("</tr>""</table></html>"));
|
|
}
|
|
}
|
|
return str;
|
|
}
|
|
|
|
void MainWindow::setCollapsed(QModelIndex index)
|
|
{
|
|
Q_EMIT collapsed(proxyModel->mapToSource(index));
|
|
}
|
|
|
|
void MainWindow::setExpanded(QModelIndex index)
|
|
{
|
|
Q_EMIT expanded(proxyModel->mapToSource(index));
|
|
}
|
|
|
|
|
|
void MainWindow::setMaxTextItemsDisplayed(int val)
|
|
{
|
|
for(auto v : CGAL::QGLViewer::QGLViewerPool())
|
|
qobject_cast<CGAL::Three::Viewer_interface*>(v)->textRenderer()->setMax(val);
|
|
}
|
|
|
|
void MainWindow::resetHeader()
|
|
{
|
|
sceneView->header()->setStretchLastSection(false);
|
|
scene->invisibleRootItem()->setColumnCount(5);
|
|
sceneView->header()->setSectionResizeMode(Scene::NameColumn, QHeaderView::Stretch);
|
|
sceneView->header()->setSectionResizeMode(Scene::ColorColumn, QHeaderView::Fixed);
|
|
sceneView->header()->setSectionResizeMode(Scene::RenderingModeColumn, QHeaderView::ResizeToContents);
|
|
sceneView->header()->setSectionResizeMode(Scene::ABColumn, QHeaderView::Fixed);
|
|
sceneView->header()->setSectionResizeMode(Scene::VisibleColumn, QHeaderView::Fixed);
|
|
sceneView->header()->resizeSection(Scene::ColorColumn, sceneView->header()->fontMetrics().horizontalAdvance("_#_"));
|
|
sceneView->resizeColumnToContents(Scene::RenderingModeColumn);
|
|
sceneView->header()->resizeSection(Scene::ABColumn, sceneView->header()->fontMetrics().horizontalAdvance(QString("_AB_")));
|
|
sceneView->header()->resizeSection(Scene::VisibleColumn, sceneView->header()->fontMetrics().horizontalAdvance(QString("_View_")));
|
|
}
|
|
|
|
void MainWindow::reset_default_loaders()
|
|
{
|
|
default_plugin_selection.clear();
|
|
|
|
const char* prop_name = "Menu modified by MainWindow.";
|
|
QMenu* menu = ui->menuFile;
|
|
if(!menu)
|
|
return;
|
|
bool menuChanged = menu->property(prop_name).toBool();
|
|
if(!menuChanged) {
|
|
menu->setProperty(prop_name, true);
|
|
}
|
|
QList<QAction*> menuActions = menu->actions();
|
|
menu->removeAction(actionResetDefaultLoaders);
|
|
}
|
|
|
|
void MainWindow::insertActionBeforeLoadPlugin(QMenu* menu, QAction* actionToInsert)
|
|
{
|
|
if(menu)
|
|
{
|
|
QList<QAction*> menuActions = menu->actions();
|
|
if(!menuActions.contains(actionToInsert))
|
|
menu->insertAction(ui->actionLoadPlugin, actionToInsert);
|
|
}
|
|
}
|
|
|
|
void MainWindow::colorItems()
|
|
{
|
|
std::size_t nb_files = scene->selectionIndices().size();
|
|
if(nb_files<2)
|
|
return;
|
|
std::vector<QColor> colors_;
|
|
colors_.reserve(nb_files);
|
|
#
|
|
compute_color_map(scene->item(scene->selectionIndices().last())->color(),
|
|
static_cast<unsigned>(nb_files),
|
|
std::back_inserter(colors_));
|
|
|
|
std::size_t nb_item = -1;
|
|
for(int id : scene->selectionIndices())
|
|
{
|
|
scene->item(id)->setColor(colors_[++nb_item]);
|
|
}
|
|
for(auto v : CGAL::QGLViewer::QGLViewerPool())
|
|
v->update();
|
|
}
|
|
|
|
|
|
void MainWindow::exportStatistics()
|
|
{
|
|
std::vector<Scene_item*> items;
|
|
for(int id : getSelectedSceneItemIndices())
|
|
{
|
|
Scene_item* s_item = scene->item(id);
|
|
items.push_back(s_item);
|
|
}
|
|
|
|
QString str;
|
|
for(Scene_item* sit: items)
|
|
{
|
|
CGAL::Three::Scene_item::Header_data data = sit->header();
|
|
if(data.titles.size()>0)
|
|
{
|
|
int titles_limit =0;
|
|
int title = 0;
|
|
str.append(QString("%1: \n").arg(sit->name()));
|
|
for(int j=0; j<data.categories.size(); j++)
|
|
{
|
|
str.append(QString(" %1: \n")
|
|
.arg(data.categories[j].first));
|
|
titles_limit+=data.categories[j].second;
|
|
for(;title<titles_limit; ++title)
|
|
{
|
|
str.append(QString(" %1: ").arg(data.titles.at(title)));
|
|
str.append(QString("%1\n").arg(sit->computeStats(title)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
QString filename =
|
|
QFileDialog::getSaveFileName((QWidget*)sender(),
|
|
"",
|
|
QString("Statistics.txt"),
|
|
"Text Files (*.txt)");
|
|
if(filename.isEmpty())
|
|
return;
|
|
QFile output(filename);
|
|
bool success = output.open(QIODevice::WriteOnly | QIODevice::Text);
|
|
|
|
if((! success) || (!output.isOpen())){
|
|
qDebug() << "- Error, unable to open" << "outputFilename" << "for output";
|
|
}
|
|
QTextStream outStream(&output);
|
|
outStream << str;
|
|
output.close();
|
|
}
|
|
|
|
void MainWindow::propagate_action()
|
|
{
|
|
QAction* sender = qobject_cast<QAction*>(this->sender());
|
|
if(!sender) return;
|
|
QString name = sender->text();
|
|
for(Scene::Item_id id : scene->selectionIndices())
|
|
{
|
|
Scene_item* item = scene->item(id);
|
|
for(QAction* action : item->contextMenu()->actions())
|
|
{
|
|
if(action->text() == name)
|
|
{
|
|
action->trigger();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
QString make_fullpath(const QString& filename, bool duplicate = false)
|
|
{
|
|
QString fullpath = QString("%1/%2").arg(QDir::tempPath()).arg(filename);
|
|
QString tmp_fullpath = fullpath;
|
|
if(duplicate)
|
|
{
|
|
int i=0;
|
|
while(QFileInfo(tmp_fullpath).exists())
|
|
{
|
|
QString basename = QFileInfo(tmp_fullpath).baseName();
|
|
QString dir = QFileInfo(tmp_fullpath).dir().path();
|
|
QString suffix= QFileInfo(fullpath).completeSuffix();
|
|
tmp_fullpath=QString("%1/%2%3.%4").arg(dir).arg(basename).arg(++i).arg(suffix);
|
|
}
|
|
}
|
|
return tmp_fullpath;
|
|
}
|
|
/*
|
|
The two following functions allow to create files from string and strings from files.
|
|
This is used as a workaround of the absence of stream management in our I/O system.
|
|
The whole to/from Base64 is used to avoid problems with binary formats. Everything is written
|
|
as a base64 binary string, and converted back to what it was.
|
|
*/
|
|
QByteArray file_to_string(const char* filename)
|
|
{
|
|
std::ifstream f(filename, std::ifstream::binary);
|
|
// get size of file
|
|
f.seekg (0,f.end);
|
|
long size = f.tellg();
|
|
f.seekg (0);
|
|
std::ostringstream ss;
|
|
// allocate memory for file content
|
|
char* buffer = new char[size];
|
|
|
|
// read content of infile
|
|
f.read(buffer,size);
|
|
|
|
// write to outfile
|
|
ss.write(buffer,size);
|
|
// release dynamically-allocated memory
|
|
delete[] buffer;
|
|
//ss.write( << f.rdbuf(); // reading data
|
|
f.close();
|
|
std::string st = ss.str();
|
|
QByteArray ba(st.c_str(), static_cast<int>(st.size()));
|
|
return ba;
|
|
}
|
|
|
|
QString MainWindow::write_string_to_file(const QString& str, const QString &filename)
|
|
{
|
|
QString fullpath = make_fullpath(filename);
|
|
std::ofstream f(fullpath.toStdString().c_str(), std::ofstream::binary);
|
|
QByteArray compressed_item(str.toStdString().c_str());
|
|
QByteArray item = qUncompress(QByteArray::fromBase64(compressed_item));
|
|
QByteArray bb = item;
|
|
f.write(bb.constData(),bb.toStdString().size());
|
|
f.close();
|
|
return fullpath;
|
|
}
|
|
|
|
void MainWindow::on_actionSa_ve_Scene_as_Script_triggered()
|
|
{
|
|
if(scene->numberOfEntries() == 0)
|
|
return;
|
|
bool do_upload = false;
|
|
#ifdef CGAL_USE_SSH
|
|
QString user = settings.value("ssh_user", QString()).toString();
|
|
if(!user.isEmpty())
|
|
{
|
|
QMessageBox::StandardButton doyou =
|
|
QMessageBox::question(this, tr("Upload ?"), tr("Do you wish to upload the scene"
|
|
" using the SSH preferences ?"));
|
|
do_upload = (doyou == QMessageBox::Yes);
|
|
}
|
|
#endif
|
|
|
|
QString filename;
|
|
|
|
if(do_upload){
|
|
filename = QString("%1/save_scene.js").arg(QDir::tempPath());
|
|
}else{
|
|
filename = QFileDialog::getSaveFileName(this,
|
|
"Save the Scene as a Script File",
|
|
last_saved_dir,
|
|
"Qt Script files (*.js)");
|
|
}
|
|
if(!filename.endsWith(".js"))
|
|
filename.append(".js");
|
|
std::ofstream os(filename.toUtf8(), std::ofstream::binary);
|
|
if(!os)
|
|
return;
|
|
QApplication::setOverrideCursor(Qt::WaitCursor);
|
|
std::vector<std::pair<QString, QString> > names;
|
|
std::vector<std::pair<QString, QString> > loaders;
|
|
std::vector<QColor> colors;
|
|
std::vector<int> rendering_modes;
|
|
QStringList not_saved;
|
|
CGAL_Lab_io_plugin_interface* camera_plugin = nullptr;
|
|
QMap<QString, QVector<QString> > group_children_map;
|
|
for(int i = 0; i < scene->numberOfEntries(); ++i)
|
|
{
|
|
Scene_item* item = scene->item(i);
|
|
QString loader;// = item->property("loader_name").toString();
|
|
QString ext;
|
|
for(CGAL_Lab_io_plugin_interface* iop : io_plugins)
|
|
{
|
|
if(iop->name() == "camera_positions_plugin")
|
|
camera_plugin = iop;
|
|
if(iop->isDefaultLoader(item))
|
|
{
|
|
QString sf = iop->saveNameFilters().split(";;").first();
|
|
//OFF Files (*.off)
|
|
QRegularExpression re("\\(\\*\\.(.*)\\)");
|
|
QRegularExpressionMatch rem = re.match(sf);
|
|
if(!rem.hasMatch())
|
|
continue;
|
|
ext = rem.captured(1);
|
|
//check if it is in a group
|
|
if(item->parentGroup())
|
|
{
|
|
group_children_map[item->parentGroup()->name()].append(item->name());
|
|
;
|
|
}
|
|
QList<Scene_item*>to_save;
|
|
to_save.append(item);
|
|
QString savename(tr("%1.%2").arg(item->name()).arg(ext));
|
|
QString fullpath = make_fullpath(savename, true);
|
|
savename = QFileInfo(fullpath).fileName();
|
|
iop->save(QFileInfo(fullpath), to_save);
|
|
names.push_back(std::make_pair(savename, item->name()));
|
|
loader=iop->name();
|
|
break;
|
|
}
|
|
}
|
|
if(loader.isEmpty())
|
|
{
|
|
QApplication::restoreOverrideCursor();
|
|
QMessageBox::warning(this, "", tr("No plugin found for %1. Not saved.").arg(item->name()));
|
|
QApplication::setOverrideCursor(Qt::WaitCursor);
|
|
continue;
|
|
}
|
|
|
|
loaders.push_back(std::make_pair(loader, ext));
|
|
colors.push_back(item->color());
|
|
rendering_modes.push_back(item->renderingMode());
|
|
}
|
|
bool has_camera_positions = false;
|
|
if(camera_plugin)
|
|
{
|
|
QString fullpath = make_fullpath("camera_tmp.camera.txt");
|
|
QList<Scene_item*> dummy;
|
|
if(camera_plugin->save(QFileInfo(fullpath), dummy))
|
|
{
|
|
QByteArray item = file_to_string(fullpath.toStdString().c_str());
|
|
os << "var camera_positions= [\'";
|
|
os<<qCompress(item, 9).toBase64().toStdString().c_str();
|
|
os << "\']\n" ;
|
|
//delete temp file
|
|
QFile tmp_file(fullpath);
|
|
tmp_file.remove();
|
|
has_camera_positions =true;
|
|
}
|
|
}
|
|
if(loaders.empty())
|
|
return;
|
|
//path
|
|
os << "var camera = \""<<viewer->dumpCameraCoordinates().toStdString()<<"\";\n";
|
|
os << "var items = [";
|
|
for(std::size_t i = 0; i< names.size() -1; ++i)
|
|
{
|
|
QString fullpath = make_fullpath(names[i].first);
|
|
|
|
QByteArray item = file_to_string(fullpath.toStdString().c_str());
|
|
os<<"[\'";
|
|
os<<qCompress(item, 9).toBase64().toStdString().c_str();
|
|
os << "\', \'"<<names[i].second.toStdString().c_str()<<"\']," ;
|
|
//delete temp file
|
|
QFile tmp_file(fullpath);
|
|
tmp_file.remove();
|
|
}
|
|
QString fullpath = make_fullpath(names.back().first);
|
|
QByteArray item = file_to_string(fullpath.toStdString().c_str());
|
|
os<<"[\'";
|
|
os<<qCompress(item, 9).toBase64().toStdString().c_str();
|
|
os << "\', \'"<<names.back().second.toStdString().c_str()<<"\']];\n";
|
|
//delete temp file
|
|
QFile tmp_file(fullpath);
|
|
tmp_file.remove();
|
|
//group relations
|
|
if(!group_children_map.empty())
|
|
{
|
|
os << "var groups = [";
|
|
for(int i = 0; i< group_children_map.size() -1; ++i)
|
|
{
|
|
QString group_name = group_children_map.keys()[i];
|
|
os << "[\'" << group_name.toStdString().c_str()<<"\', [";
|
|
for(int j = 0; j<group_children_map[group_name].size()-1; ++j)
|
|
{
|
|
os << group_children_map[group_name][j].toStdString().c_str()<<", ";
|
|
}
|
|
os << group_children_map[group_name].back().toStdString().c_str()<<"]],";
|
|
}
|
|
QString group_name = group_children_map.keys().back();
|
|
os << "[\'" << group_name.toStdString().c_str()<<"\', [";
|
|
for(int j = 0; j<group_children_map[group_name].size()-1; ++j)
|
|
{
|
|
os << "\'"<<group_children_map[group_name][j].toStdString().c_str()<<"\', ";
|
|
}
|
|
os << "\'"<<group_children_map[group_name].back().toStdString().c_str()<<"\']]];\n";
|
|
}
|
|
//plugin
|
|
os << "var loaders = [";
|
|
for(std::size_t i = 0; i< names.size() -1; ++i)
|
|
{
|
|
os << "[\'" << loaders[i].first.toStdString() << "\', \'"<<loaders[i].second.toStdString()<< "\'],";
|
|
}
|
|
os << "[\'" << loaders.back().first.toStdString() << "\', \'"<<loaders.back().second.toStdString()<< "\']];\n";
|
|
|
|
//color
|
|
os << "var colors = [";
|
|
for(std::size_t i = 0; i< names.size() -1; ++i)
|
|
{
|
|
os << "[" << colors[i].red() <<", "<< colors[i].green() <<", "<< colors[i].blue() <<"], ";
|
|
}
|
|
os<<"[" << colors.back().red() <<", "<< colors.back().green() <<", "<< colors.back().blue() <<"]];\n";
|
|
|
|
//rendering mode
|
|
os << "var rendering_modes = [";
|
|
for(std::size_t i = 0; i< names.size() -1; ++i)
|
|
{
|
|
os << rendering_modes[i] << ", ";
|
|
}
|
|
os << rendering_modes.back()<<"];\n";
|
|
os << "function indexFromName(name) {\n";
|
|
os << " for(var i = 0; i < items.length; i++){\n";
|
|
os << " var itemName=items[i][1];\n";
|
|
os << " if( itemName === name)\n";
|
|
os << " {\n";
|
|
os << " return i;\n";
|
|
os << " }\n";
|
|
os << " };\n";
|
|
os << "}\n";
|
|
os << "items.forEach(function(item, index, array){\n";
|
|
os<<" var path=items[index][1];\n";
|
|
os<<" path+='.';\n";
|
|
os<<" path+=loaders[index][1];\n";
|
|
os<<" var fullpath = main_window.write_string_to_file(item[0], path);\n";
|
|
os<<" main_window.open(fullpath,loaders[index][0]);\n";
|
|
os << " var it = scene.item(scene.numberOfEntries-1);\n";
|
|
os << " var r = colors[index][0];\n";
|
|
os << " var g = colors[index][1];\n";
|
|
os << " var b = colors[index][2];\n";
|
|
os << " it.setRgbColor(r,g,b);\n";
|
|
os << " it.setRenderingMode(rendering_modes[index]);\n";
|
|
os << "});\n";
|
|
if(!group_children_map.empty())
|
|
{
|
|
os << "groups.forEach(function(group, index, array){\n";
|
|
os << " main_window.selectSceneItem(-1);\n";
|
|
os << " var group_name = group[0];\n";
|
|
os << " var item_list = group[1];\n";
|
|
os << " main_window.makeNewGroup();\n";
|
|
os << " var it = scene.item(scene.numberOfEntries-1);\n";
|
|
os << " it.setName(group_name);\n";
|
|
os << " item_list.forEach(function(child, index, array){\n";
|
|
os << " scene.changeGroup(scene.item(indexFromName(child)), it);\n";
|
|
os << " });\n";
|
|
os << "});\n";
|
|
}
|
|
os << "viewer.moveCameraToCoordinates(camera, 0.05);\n";
|
|
if(has_camera_positions)
|
|
{
|
|
os<<" var path=\"cams.camera.txt\";\n";
|
|
os<<" var fullpath = main_window.write_string_to_file(camera_positions, path);\n";
|
|
os<<" main_window.open(fullpath,\'camera_positions_plugin\');\n";
|
|
}
|
|
os.close();
|
|
if(!not_saved.empty()){
|
|
QApplication::restoreOverrideCursor();
|
|
QMessageBox::warning(this,
|
|
"Items Not Saved",
|
|
QString("The following items could not be saved: %1").arg(
|
|
not_saved.join(", ")));
|
|
QApplication::setOverrideCursor(Qt::WaitCursor);
|
|
}
|
|
#ifdef CGAL_USE_SSH
|
|
using namespace CGAL::ssh_internal;
|
|
if(do_upload)
|
|
{
|
|
QString server = settings.value("ssh_server", QString()).toString();
|
|
QString pk = settings.value("ssh_public_key", QString()).toString();
|
|
QString privK = settings.value("ssh_priv_key", QString()).toString();
|
|
user = user.trimmed();
|
|
server = server.trimmed();
|
|
pk = pk.trimmed();
|
|
privK=privK.trimmed();
|
|
if(user.isEmpty()){
|
|
return;
|
|
}
|
|
QString path;
|
|
path = QInputDialog::getText(this,
|
|
"",
|
|
tr("Enter the name of your scene file."));
|
|
if(path.isEmpty())
|
|
return;
|
|
if(!path.contains("/tmp/CGAL_Lab_"))
|
|
path.prepend("/tmp/CGAL_Lab_");
|
|
try{
|
|
ssh_session session = nullptr;
|
|
bool res = establish_ssh_session_from_agent(session,
|
|
user.toStdString().c_str(),
|
|
server.toStdString().c_str(),
|
|
pk.toStdString().c_str());
|
|
|
|
if(!res)
|
|
{
|
|
bool ok;
|
|
QString pass;
|
|
pass = QInputDialog::getText(this, "SSH Password",
|
|
"Enter ssh key password:",
|
|
QLineEdit::Password,
|
|
tr(""),
|
|
&ok);
|
|
if(!ok)
|
|
{
|
|
ssh_free(session);
|
|
QApplication::restoreOverrideCursor();
|
|
return;
|
|
}
|
|
pass = pass.trimmed();
|
|
res = establish_ssh_session(session,
|
|
user.toStdString().c_str(),
|
|
server.toStdString().c_str(),
|
|
pk.toStdString().c_str(),
|
|
privK.toStdString().c_str(),
|
|
pass.toStdString().c_str());
|
|
}
|
|
|
|
if(!res)
|
|
{
|
|
QMessageBox::warning(this,
|
|
"Error",
|
|
"The SSH session could not be started.");
|
|
ssh_free(session);
|
|
QApplication::restoreOverrideCursor();
|
|
return;
|
|
}
|
|
res = push_file(session,path.toStdString().c_str(), filename.toStdString().c_str());
|
|
if(!res)
|
|
{
|
|
QApplication::restoreOverrideCursor();
|
|
QMessageBox::warning(this,
|
|
"Error",
|
|
"The file could not be uploaded. Check your console for more information.");
|
|
close_connection(session);
|
|
ssh_free(session);
|
|
return;
|
|
}
|
|
close_connection(session);
|
|
ssh_free(session);
|
|
QFile tmp_file(filename);
|
|
tmp_file.remove();
|
|
} catch( ssh::SshException e )
|
|
{
|
|
std::cout << "Error during connection : ";
|
|
std::cout << e.getError() << std::endl;
|
|
}
|
|
}
|
|
#endif
|
|
QApplication::restoreOverrideCursor();
|
|
}
|
|
void MainWindow::setTransparencyPasses(int val)
|
|
{
|
|
viewer->setTotalPass(val);
|
|
viewer->update();
|
|
}
|
|
|
|
void MainWindow::toggleFullScreen()
|
|
{
|
|
QList<QDockWidget *> dockWidgets = findChildren<QDockWidget *>();
|
|
if(visibleDockWidgets.isEmpty())
|
|
{
|
|
for(QDockWidget * dock : dockWidgets)
|
|
{
|
|
if(dock->isVisible())
|
|
{
|
|
visibleDockWidgets.append(dock);
|
|
dock->hide();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(QDockWidget * dock : visibleDockWidgets){
|
|
dock->show();
|
|
}
|
|
visibleDockWidgets.clear();
|
|
|
|
}
|
|
}
|
|
|
|
void MainWindow::setDefaultSaveDir()
|
|
{
|
|
QString dirpath = QFileDialog::getExistingDirectory(this, "Set Default Save as Directory", def_save_dir);
|
|
if(!dirpath.isEmpty())
|
|
def_save_dir = dirpath;
|
|
settings.setValue("default_saveas_dir", def_save_dir);
|
|
}
|
|
|
|
|
|
void MainWindow::setupViewer(Viewer* viewer, SubViewer* subviewer)
|
|
{
|
|
viewer->textRenderer()->setScene(scene);
|
|
viewer->setScene(scene);
|
|
connect(scene, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex & )),
|
|
viewer, SLOT(update()));
|
|
connect(scene, SIGNAL(updated()),
|
|
viewer, SLOT(update()));
|
|
|
|
QAction* action = subviewer->findChild<QAction*>("actionRecenter");
|
|
connect(action, SIGNAL(triggered()),
|
|
viewer, SLOT(update()));
|
|
connect(action, &QAction::triggered,
|
|
subviewer, &SubViewer::recenter);
|
|
action= subviewer->findChild<QAction*>("actionLookat");
|
|
connect(action, SIGNAL(triggered()),
|
|
subviewer, SLOT(lookat()));
|
|
action= subviewer->findChild<QAction*>("actionColor");
|
|
connect(action, &QAction::triggered,
|
|
subviewer, &SubViewer::color);
|
|
action= subviewer->findChild<QAction*>("actionDumpCamera");
|
|
connect(action, &QAction::triggered,
|
|
[this, viewer](){
|
|
information(QString("Camera: %1")
|
|
.arg(cameraString(viewer)));
|
|
});
|
|
action= subviewer->findChild<QAction*>("actionCopyCamera");
|
|
connect(action, &QAction::triggered,
|
|
[this, viewer](){
|
|
qApp->clipboard()->setText(cameraString(viewer));
|
|
});
|
|
action= subviewer->findChild<QAction*>("actionPasteCamera");
|
|
connect(action, &QAction::triggered,
|
|
this, [viewer](){
|
|
QString s = qApp->clipboard()->text();
|
|
viewer->moveCameraToCoordinates(s, 0.5f);
|
|
});
|
|
action= subviewer->findChild<QAction*>("actionAntiAliasing");
|
|
connect(action, SIGNAL(toggled(bool)),
|
|
viewer, SLOT(setAntiAliasing(bool)));
|
|
action= subviewer->findChild<QAction*>("actionDrawTwoSide");
|
|
connect(action, SIGNAL(toggled(bool)),
|
|
viewer, SLOT(setTwoSides(bool)));
|
|
action= subviewer->findChild<QAction*>("actionQuick");
|
|
connect(action, SIGNAL(toggled(bool)),
|
|
viewer, SLOT(setFastDrawing(bool)));
|
|
action= subviewer->findChild<QAction*>("actionOrtho");
|
|
connect(action, SIGNAL(toggled(bool)),
|
|
viewer, SLOT(SetOrthoProjection(bool)));
|
|
action= subviewer->findChild<QAction*>("actionTotalPass");
|
|
connect(action, &QAction::triggered,
|
|
this, [this, viewer]() {
|
|
bool ok;
|
|
int nb = QInputDialog::getInt(this, "Set Maximum Number of Passes",
|
|
"Enter number of transparency passes:",
|
|
4, 4, 99, 1, &ok);
|
|
if (!ok){
|
|
return;
|
|
}
|
|
viewer->setTotalPass(nb);
|
|
});
|
|
|
|
action = subviewer->findChild<QAction*>("actionScaleScene");
|
|
action->setCheckable(true);
|
|
action->setChecked(false);
|
|
connect(action, &QAction::triggered,
|
|
viewer, &Viewer::scaleScene);
|
|
|
|
action= subviewer->findChild<QAction*>("actionBackFrontShading");
|
|
connect(action, SIGNAL(toggled(bool)),
|
|
viewer, SLOT(setBackFrontShading(bool)));
|
|
connect(viewer, SIGNAL(requestContextMenu(QPoint)),
|
|
this, SLOT(contextMenuRequested(QPoint)));
|
|
connect(viewer, SIGNAL(selected(int)),
|
|
this, SLOT(selectSceneItem(int)));
|
|
connect(viewer, SIGNAL(selectedPoint(double, double, double)),
|
|
this, SLOT(showSelectedPoint(double, double, double)));
|
|
|
|
connect(viewer, SIGNAL(selectionRay(double, double, double,
|
|
double, double, double)),
|
|
scene, SIGNAL(selectionRay(double, double, double,
|
|
double, double, double)));
|
|
|
|
connect(viewer, &Viewer::sendMessage,
|
|
this, [](QString s){
|
|
information(s);
|
|
});
|
|
|
|
#ifdef CGAL_USE_WEBSOCKETS
|
|
action= subviewer->viewer->findChild<QAction*>("actionShareCamera");
|
|
connect(action, &QAction::toggled,
|
|
this, [this, viewer](bool b)
|
|
{
|
|
if(!viewer){
|
|
return;
|
|
}
|
|
QString session;
|
|
if(b){
|
|
bool ok;
|
|
session = QInputDialog::getText(
|
|
this,"Session",
|
|
"Please enter the session name.\n"
|
|
"Only the machines that enter the same session name will be connected.\n"
|
|
"Several sessions can run simultaneously on a same server. ",
|
|
QLineEdit::Normal, QString(), &ok);
|
|
if(session.isEmpty() || !ok)
|
|
{
|
|
viewer->setShareCam(false, session);
|
|
return;
|
|
}
|
|
}
|
|
viewer->setShareCam(b, session);
|
|
});
|
|
#endif
|
|
}
|
|
|
|
void MainWindow::on_actionAdd_Viewer_triggered()
|
|
{
|
|
SubViewer* subviewer = new SubViewer(ui->mdiArea, this, viewer);
|
|
Viewer* viewer2 = subviewer->viewer;
|
|
viewer2->setManipulatedFrame(viewer->manipulatedFrame());
|
|
CGAL::qglviewer::Vec min, max;
|
|
computeViewerBBox(min, max);
|
|
updateViewerBbox(viewer2, true, min, max);
|
|
viewer2->setObjectName("viewer2");
|
|
connect(viewer2, SIGNAL(doneInitGL(CGAL::Three::Viewer_interface*)),
|
|
scene, SLOT(newViewer(CGAL::Three::Viewer_interface*)));
|
|
connect(viewer2, &Viewer::contextIsDestroyed,
|
|
this, [this, viewer2](){
|
|
scene->removeViewer(viewer2);
|
|
viewerDestroyed(viewer2);
|
|
});
|
|
|
|
setupViewer(viewer2, subviewer);
|
|
viewer2->camera()->interpolateToFitScene();
|
|
subviewer->show();
|
|
ui->mdiArea->tileSubWindows();
|
|
QPoint pos = viewer_window->pos();
|
|
QSize size = viewer_window->size();
|
|
viewer_window->move(subviewer->pos());
|
|
viewer_window->resize(subviewer->size());
|
|
subviewer->move(pos);
|
|
subviewer->resize(size);
|
|
newViewerCreated(viewer2);
|
|
}
|
|
|
|
void MainWindow::recenterViewer()
|
|
{
|
|
scene->computeBbox();
|
|
CGAL::qglviewer::Vec min, max;
|
|
computeViewerBBox(min, max);
|
|
Viewer* target = qobject_cast<Viewer*>(childAt(cursor().pos()));
|
|
if(target)
|
|
{
|
|
scene->computeBbox();
|
|
updateViewerBbox(target, true, min, max);
|
|
target->camera()->interpolateToFitScene();
|
|
}
|
|
}
|
|
|
|
void MainWindow::updateViewerBbox(Viewer *vi, bool recenter,
|
|
CGAL::qglviewer::Vec min,
|
|
CGAL::qglviewer::Vec max){
|
|
CGAL::qglviewer::Vec center = viewer->camera()->pivotPoint();
|
|
vi->setSceneBoundingBox(min,
|
|
max);
|
|
if(recenter)
|
|
{
|
|
vi->resetFov();
|
|
vi->camera()->showEntireScene();
|
|
}
|
|
else
|
|
{
|
|
vi->camera()->setPivotPoint(center);
|
|
}
|
|
}
|
|
|
|
QObject* MainWindow::getDirectChild(QObject* widget)
|
|
{
|
|
|
|
if(!widget->property("helpText").toString().isEmpty())
|
|
return widget;
|
|
return getDirectChild(widget->parent());
|
|
}
|
|
|
|
void MainWindow::on_action_Rearrange_Viewers_triggered()
|
|
{
|
|
if(ui->mdiArea->subWindowList().size() == 1)
|
|
ui->mdiArea->subWindowList().first()->showMaximized();
|
|
else
|
|
{
|
|
ui->mdiArea->tileSubWindows();
|
|
QMdiSubWindow* subviewer = qobject_cast<QMdiSubWindow*>(
|
|
ui->mdiArea->childAt(ui->mdiArea->pos()));
|
|
if(!subviewer)//should not happen but better safe than sorry
|
|
{
|
|
return;
|
|
}
|
|
QPoint pos = viewer_window->pos();
|
|
QSize size = viewer_window->size();
|
|
viewer_window->move(subviewer->pos());
|
|
viewer_window->resize(subviewer->size());
|
|
subviewer->move(pos);
|
|
subviewer->resize(size);
|
|
}
|
|
}
|
|
|
|
SubViewer::SubViewer(QWidget *parent, MainWindow* mw, Viewer* mainviewer)
|
|
:QMdiSubWindow (parent),
|
|
mw(mw),
|
|
viewMenu(new QMenu(this)),
|
|
is_main(false)
|
|
{
|
|
if(mainviewer)
|
|
viewer = new Viewer(this, mainviewer);
|
|
else
|
|
{
|
|
viewer = new Viewer(this);
|
|
is_main = true;
|
|
}
|
|
setWidget(viewer);
|
|
QAction* actionRecenter = new QAction("Re¢er Scene",this);
|
|
actionRecenter->setObjectName("actionRecenter");
|
|
viewMenu->addAction(actionRecenter);
|
|
QAction* actionLookat = new QAction("&Look at...",this);
|
|
actionLookat->setObjectName("actionLookat");
|
|
viewMenu->addAction(actionLookat);
|
|
QAction* actionColor = new QAction("Change &Background Color...",this);
|
|
actionColor->setObjectName("actionColor");
|
|
viewMenu->addAction(actionColor);
|
|
QAction* actionDumpCamera = new QAction("&Dump Camera Coordinates",this);
|
|
actionDumpCamera->setObjectName("actionDumpCamera");
|
|
QAction* actionCopyCamera = new QAction("&Copy Camera",this);
|
|
actionCopyCamera->setObjectName("actionCopyCamera");
|
|
QAction* actionPasteCamera = new QAction("&Paste Camera",this);
|
|
actionPasteCamera->setObjectName("actionPasteCamera");
|
|
QMenu* cameraMenu = new QMenu("Ca&mera", mw);
|
|
cameraMenu->addAction(actionDumpCamera);
|
|
cameraMenu->addAction(actionCopyCamera);
|
|
cameraMenu->addAction(actionPasteCamera);
|
|
viewMenu->addMenu(cameraMenu);
|
|
|
|
QAction* actionAntiAliasing = new QAction("&Antialiasing",this);
|
|
actionAntiAliasing->setObjectName("actionAntiAliasing");
|
|
actionAntiAliasing->setCheckable(true);
|
|
actionAntiAliasing->setChecked(false);
|
|
viewMenu->addAction(actionAntiAliasing);
|
|
QAction* actionDrawTwoSide = new QAction("Draw &Two Sides",this);
|
|
actionDrawTwoSide->setObjectName("actionDrawTwoSide");
|
|
actionDrawTwoSide->setCheckable(true);
|
|
actionDrawTwoSide->setChecked(false);
|
|
viewMenu->addAction(actionDrawTwoSide);
|
|
QAction* actionQuick = new QAction("&Quick Camera Mode",this);
|
|
actionQuick->setObjectName("actionQuick");
|
|
actionQuick->setCheckable(true);
|
|
actionQuick->setChecked(true);
|
|
viewMenu->addAction(actionQuick);
|
|
QAction* actionOrtho = new QAction("&Orthographic Projection",this);
|
|
actionOrtho->setObjectName("actionOrtho");
|
|
actionOrtho->setCheckable(true);
|
|
actionOrtho->setChecked(false);
|
|
viewMenu->addAction(actionOrtho);
|
|
QAction* actionTotalPass = new QAction("Set Transparency Pass &Number...",this);
|
|
actionTotalPass->setObjectName("actionTotalPass");
|
|
viewMenu->addAction(actionTotalPass);
|
|
#ifdef CGAL_USE_WEBSOCKETS
|
|
QAction* actionShareCamera= new QAction("Join &WS Server",viewer);
|
|
actionShareCamera->setObjectName("actionShareCamera");
|
|
actionShareCamera->setCheckable(true);
|
|
actionShareCamera->setChecked(false);
|
|
viewMenu->addAction(actionShareCamera);
|
|
#endif
|
|
|
|
QAction* actionBackFrontShading = new QAction("Activate Back/Front shading.",this);
|
|
actionBackFrontShading->setObjectName("actionBackFrontShading");
|
|
actionBackFrontShading->setCheckable(true);
|
|
actionBackFrontShading->setChecked(false);
|
|
viewMenu->addAction(actionBackFrontShading);
|
|
|
|
QAction* actionScaleScene = new QAction("&Scale the Scene...",this);
|
|
actionScaleScene->setObjectName("actionScaleScene");
|
|
viewMenu->addAction(actionScaleScene);
|
|
|
|
if(mainviewer)
|
|
setAttribute(Qt::WA_DeleteOnClose);
|
|
setWindowIcon(QIcon(":/cgal/icons/resources/menu.png"));
|
|
setSystemMenu(viewMenu);
|
|
}
|
|
|
|
SubViewer::~SubViewer()
|
|
{
|
|
viewer->deleteLater();
|
|
}
|
|
|
|
void SubViewer::recenter()
|
|
{
|
|
CGAL::qglviewer::Vec min, max;
|
|
mw->computeViewerBBox(min, max);
|
|
mw->updateViewerBbox(viewer, true, min, max);
|
|
viewer->camera()->interpolateToFitScene();
|
|
}
|
|
|
|
void SubViewer::lookat()
|
|
{
|
|
Show_point_dialog dialog(mw);
|
|
dialog.setWindowTitle(tr("Look at..."));
|
|
int i = dialog.exec();
|
|
if( i == QDialog::Accepted &&
|
|
dialog.has_correct_coordinates() )
|
|
{
|
|
if (viewer->camera()->frame()->isSpinning())
|
|
viewer->camera()->frame()->stopSpinning();
|
|
mw->viewerShow(viewer,
|
|
static_cast<float>(dialog.get_x()) + viewer->offset().x,
|
|
static_cast<float>(dialog.get_y()) + viewer->offset().y,
|
|
static_cast<float>(dialog.get_z()) + viewer->offset().z);
|
|
}
|
|
}
|
|
|
|
void SubViewer::color()
|
|
{
|
|
QColor c = QColorDialog::getColor();
|
|
if(c.isValid()) {
|
|
viewer->setBackgroundColor(c);
|
|
viewer->update();
|
|
}
|
|
}
|
|
|
|
void SubViewer::closeEvent(QCloseEvent *closeEvent)
|
|
{
|
|
|
|
if(is_main)
|
|
{
|
|
QMessageBox::information(mw, "", "This is the main viewer. It cannot be closed.");
|
|
closeEvent->ignore();
|
|
}
|
|
else
|
|
QWidget::closeEvent(closeEvent);
|
|
}
|
|
void SubViewer::changeEvent(QEvent *event)
|
|
{
|
|
QMdiSubWindow::changeEvent(event);
|
|
if(event->type() == QEvent::WindowStateChange)
|
|
{
|
|
if(isMaximized())
|
|
{
|
|
QMenu* menu = mw->findChild<QMenu*>("menuView");
|
|
for(QAction* action : viewMenu->actions())
|
|
{
|
|
menu->addAction(action);
|
|
}
|
|
setWindowFlags(
|
|
Qt::SubWindow
|
|
| Qt::CustomizeWindowHint
|
|
| Qt::WindowMaximizeButtonHint
|
|
//| Qt::WindowSystemMenuHint
|
|
| Qt::WindowTitleHint
|
|
);
|
|
QAction* action = mw->findChild<QAction*>("action_Rearrange_Viewers");
|
|
action->setVisible(false);
|
|
viewer->update();
|
|
}
|
|
else
|
|
{
|
|
QMenu* menu = mw->findChild<QMenu*>("menuView");
|
|
for(QAction* action : viewMenu->actions())
|
|
{
|
|
menu->removeAction(action);
|
|
}
|
|
setWindowFlags(
|
|
Qt::SubWindow
|
|
| Qt::CustomizeWindowHint
|
|
| Qt::WindowMaximizeButtonHint
|
|
| Qt::WindowSystemMenuHint
|
|
| Qt::WindowTitleHint
|
|
);
|
|
QAction* action = mw->findChild<QAction*>("action_Rearrange_Viewers");
|
|
action->setVisible(true);
|
|
for(auto v : CGAL::QGLViewer::QGLViewerPool())
|
|
v->update();
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::invalidate_bbox(bool do_recenter)
|
|
{
|
|
bbox_need_update = true;
|
|
if(do_recenter)
|
|
updateViewersBboxes(true);
|
|
}
|
|
|
|
void MainWindow::on_action_Save_triggered()
|
|
{
|
|
if(QMessageBox::question(this, "Save", "Are you sure you want to override these files ?")
|
|
== QMessageBox::No)
|
|
return;
|
|
QList<Scene_item*> to_save;
|
|
|
|
for(Scene::Item_id id : scene->selectionIndices())
|
|
{
|
|
Scene_item* item = scene->item(id);
|
|
if(!item->property("source filename").toString().isEmpty())
|
|
{
|
|
QString filename = item->property("source filename").toString();
|
|
to_save.append(item);
|
|
save(filename, to_save);
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::test_all_actions()
|
|
{
|
|
int nb_items = scene->numberOfEntries();
|
|
selectSceneItem(0);
|
|
for(PluginNamePair pnp : plugins)
|
|
{
|
|
CGAL_Lab_plugin_interface* plugin = pnp.first;
|
|
for(QAction* action : plugin->actions()){
|
|
if(plugin->applicable(action)){
|
|
qDebug()<<"Testing "<<pnp.second<<"and "<<action->text()<<" on";
|
|
qDebug()<<scene->item(scene->mainSelectionIndex())->name()<<"...";
|
|
action->triggered();
|
|
getMutex()->lock();
|
|
if(isLocked())
|
|
{
|
|
getMutex()->unlock();
|
|
getMutex()->lock();
|
|
getWaitCondition()->wait(getMutex());
|
|
getMutex()->unlock();
|
|
//get the "done event" that add items after the meshing thread is finished to execute before we start the next action.
|
|
QCoreApplication::processEvents();
|
|
}
|
|
getMutex()->unlock();
|
|
while(scene->numberOfEntries() > nb_items)
|
|
{
|
|
scene->erase(nb_items);
|
|
}
|
|
selectSceneItem(0);
|
|
//if the item is hidden, the scene's bbox is 0 and that badly
|
|
//messes with the offset meshing, for example.
|
|
scene->item(scene->mainSelectionIndex())->setVisible(true);
|
|
reloadItem();
|
|
selectSceneItem(0);
|
|
}
|
|
}
|
|
}
|
|
while(scene->numberOfEntries() > 0)
|
|
scene->erase(0);
|
|
}
|
|
|
|
|
|
void MainWindow::on_actionLoad_a_Scene_from_a_Script_File_triggered()
|
|
{
|
|
bool do_download = false;
|
|
QString filename;
|
|
#ifdef CGAL_USE_SSH
|
|
QString user = settings.value("ssh_user", QString()).toString();
|
|
|
|
if(!user.isEmpty())
|
|
{
|
|
QMessageBox::StandardButton doyou =
|
|
QMessageBox::question(this, tr("Download ?"), tr("Do you wish to download the scene"
|
|
" using the SSH preferences ?"));
|
|
do_download= (doyou == QMessageBox::Yes);
|
|
}
|
|
#endif
|
|
|
|
if(do_download)
|
|
{
|
|
#ifdef CGAL_USE_SSH
|
|
using namespace CGAL::ssh_internal;
|
|
QString server = settings.value("ssh_server", QString()).toString();
|
|
QString pk = settings.value("ssh_public_key", QString()).toString();
|
|
QString privK = settings.value("ssh_priv_key", QString()).toString();
|
|
user = user.trimmed();
|
|
server = server.trimmed();
|
|
pk = pk.trimmed();
|
|
privK=privK.trimmed();
|
|
|
|
try{
|
|
ssh_session session = nullptr;
|
|
bool res = establish_ssh_session_from_agent(session,
|
|
user.toStdString().c_str(),
|
|
server.toStdString().c_str(),
|
|
pk.toStdString().c_str());
|
|
if(!res){
|
|
bool ok;
|
|
QString pass= QInputDialog::getText(this, "SSH Password",
|
|
"Enter ssh key password:",
|
|
QLineEdit::Password,
|
|
tr(""),
|
|
&ok);
|
|
if(!ok)
|
|
{
|
|
ssh_free(session);
|
|
return;
|
|
}
|
|
pass = pass.trimmed();
|
|
res = establish_ssh_session(session,
|
|
user.toStdString().c_str(),
|
|
server.toStdString().c_str(),
|
|
pk.toStdString().c_str(),
|
|
privK.toStdString().c_str(),
|
|
pass.toStdString().c_str());
|
|
}
|
|
if(!res)
|
|
{
|
|
QMessageBox::warning(this,
|
|
"Error",
|
|
"The SSH session could not be started.");
|
|
ssh_free(session);
|
|
return;
|
|
}
|
|
QStringList names;
|
|
if(!CGAL::ssh_internal::explore_the_galaxy(session, names))
|
|
{
|
|
QMessageBox::warning(this,
|
|
"Error",
|
|
"Could not find remote directory.");
|
|
}
|
|
QString path;
|
|
bool ok;
|
|
path = QInputDialog::getItem(this,
|
|
"Choose a file",
|
|
tr("Choose the scene file."),
|
|
names,0,true, &ok);
|
|
filename = QString("%1/load_scene.js").arg(QDir::tempPath());
|
|
if(path.isEmpty() || !ok)
|
|
{
|
|
ssh_free(session);
|
|
return;
|
|
}
|
|
path.prepend("CGAL_Lab_");
|
|
path = tr("/tmp/%2").arg(path);
|
|
res = pull_file(session,path.toStdString().c_str(), filename.toStdString().c_str());
|
|
if(!res)
|
|
{
|
|
QMessageBox::warning(this,
|
|
"Error",
|
|
"The file could not be fetched. Check your console for more info.");
|
|
close_connection(session);
|
|
ssh_free(session);
|
|
return;
|
|
}
|
|
close_connection(session);
|
|
ssh_free(session);
|
|
} catch( ssh::SshException e )
|
|
{
|
|
std::cout << "Error during connection : ";
|
|
std::cout << e.getError() << std::endl;
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
filename = QFileDialog::getOpenFileName(
|
|
this,
|
|
tr("Select a Whole Scene file..."),
|
|
".",
|
|
"Whole Scene files (*.js)");
|
|
if(filename.isEmpty())
|
|
return;
|
|
}
|
|
loadScript(QFileInfo(filename));
|
|
if(do_download){
|
|
QFile tmp_file(filename);
|
|
tmp_file.remove();
|
|
}
|
|
}
|