1
2 #ifdef CGAL_USE_SSH
3 # include "CGAL/Use_ssh.h"
4 #endif
5 #include <cmath>
6
7 #include "config.h"
8 #include "MainWindow.h"
9 #include "Scene.h"
10 #include <CGAL/Three/Scene_item.h>
11 #include <CGAL/Three/TextRenderer.h>
12 #include <CGAL/Three/exceptions.h>
13 #include <CGAL/Three/Three.h>
14 #include <CGAL/Qt/debug.h>
15 #include <CGAL/double.h>
16
17 #include <QJsonArray>
18 #include <QtDebug>
19 #include <QFileDialog>
20 #include <QFileInfo>
21 #include <QHeaderView>
22 #include <QMenu>
23 #include <QMenuBar>
24 #include <QChar>
25 #include <QAction>
26 #include <QShortcut>
27 #include <QKeySequence>
28 #include <QLibrary>
29 #include <QPluginLoader>
30 #include <QMessageBox>
31 #include <QScrollBar>
32 #include <QColor>
33 #include <QColorDialog>
34 #include <QClipboard>
35 #include <QCloseEvent>
36 #include <QInputDialog>
37 #include <QTreeView>
38 #include <QSortFilterProxyModel>
39 #include <QStandardItemModel>
40 #include <QStandardItem>
41 #include <QTreeWidgetItem>
42 #include <QTreeWidget>
43 #include <QDockWidget>
44 #include <QSpinBox>
45 #include <stdexcept>
46 #include <fstream>
47 #include <QElapsedTimer>
48 #include <QWidgetAction>
49 #include <QJsonArray>
50 #include <QSequentialIterable>
51 #include <QDir>
52 #ifdef QT_SCRIPT_LIB
53 # include <QScriptValue>
54 # ifdef QT_SCRIPTTOOLS_LIB
55 # include <QScriptEngineDebugger>
56 # endif
57 #endif
58
59 #include <CGAL/Three/Three.h>
60 #include <CGAL/Three/Polyhedron_demo_plugin_interface.h>
61 #include <CGAL/Three/Polyhedron_demo_io_plugin_interface.h>
62 #include <CGAL/Three/Scene_item_with_properties.h>
63 #include "ui_SubViewer.h"
64 #include "ui_MainWindow.h"
65 #include "ui_Preferences.h"
66 #include "ui_Details.h"
67 #include "ui_Statistics_on_item_dialog.h"
68 #include "ui_SSH_dialog.h"
69 #include "Show_point_dialog.h"
70 #include "File_loader_dialog.h"
71 #include "Viewer.h"
72
73 #include <CGAL/Qt/manipulatedCameraFrame.h>
74 #include <CGAL/Qt/manipulatedFrame.h>
75
76 #ifdef QT_SCRIPT_LIB
77 # include <QScriptEngine>
78 # include <QScriptValue>
79 #include "Color_map.h"
80
81
82 using namespace CGAL::Three;
83 QScriptValue
myScene_itemToScriptValue(QScriptEngine * engine,CGAL::Three::Scene_item * const & in)84 myScene_itemToScriptValue(QScriptEngine *engine,
85 CGAL::Three::Scene_item* const &in)
86 {
87 return engine->newQObject(in);
88 }
89
myScene_itemFromScriptValue(const QScriptValue & object,CGAL::Three::Scene_item * & out)90 void myScene_itemFromScriptValue(const QScriptValue &object,
91 CGAL::Three::Scene_item* &out)
92 {
93 out = qobject_cast<CGAL::Three::Scene_item*>(object.toQObject());
94 }
95 #endif // QT_SCRIPT_LIB
96
97 #ifdef QT_SCRIPT_LIB
98 # ifdef QT_SCRIPTTOOLS_LIB
99
100 const QScriptEngineDebugger::DebuggerWidget debug_widgets[9] = {
101 QScriptEngineDebugger::ConsoleWidget,
102 QScriptEngineDebugger::StackWidget,
103 QScriptEngineDebugger::ScriptsWidget,
104 QScriptEngineDebugger::LocalsWidget,
105 QScriptEngineDebugger::CodeWidget,
106 QScriptEngineDebugger::CodeFinderWidget,
107 QScriptEngineDebugger::BreakpointsWidget,
108 QScriptEngineDebugger::DebugOutputWidget,
109 QScriptEngineDebugger::ErrorLogWidget
110 };
111 const QString debug_widgets_names[9] = {
112 "Script console",
113 "Stack",
114 "Scripts",
115 "Locals",
116 "Code",
117 "CodeFinder",
118 "Breakpoints",
119 "DebugOutput",
120 "ErrorLog"
121 };
122
123 # endif
124 #endif
125
myPrintFunction(QScriptContext * context,QScriptEngine * engine)126 QScriptValue myPrintFunction(QScriptContext *context, QScriptEngine *engine)
127 {
128 MainWindow* mw = qobject_cast<MainWindow*>(engine->parent());
129 QString result;
130 for (int i = 0; i < context->argumentCount(); ++i) {
131 if (i > 0)
132 result.append(" ");
133 result.append(context->argument(i).toString());
134 }
135
136 if(mw) mw->message(QString("QtScript: ") + result, "");
137 QTextStream (stdout) << (QString("QtScript: ") + result) << "\n";
138
139 return engine->undefinedValue();
140 }
141
~MainWindow()142 MainWindow::~MainWindow()
143 {
144 searchAction->deleteLater();
145 delete ui;
146 delete statistics_ui;
147 }
MainWindow(const QStringList & keywords,bool verbose,QWidget * parent)148 MainWindow::MainWindow(const QStringList &keywords, bool verbose, QWidget* parent)
149 : CGAL::Qt::DemosMainWindow(parent),
150 accepted_keywords(keywords)
151 {
152 bbox_need_update = true;
153 ui = new Ui::MainWindow;
154 ui->setupUi(this);
155 menuBar()->setNativeMenuBar(false);
156 searchAction = new QWidgetAction(nullptr);
157 CGAL::Three::Three::s_mainwindow = this;
158 menu_map[ui->menuOperations->title()] = ui->menuOperations;
159 this->verbose = verbose;
160 // remove the Load Script menu entry, when the demo has not been compiled with QT_SCRIPT_LIB
161 #if !defined(QT_SCRIPT_LIB)
162 ui->menuBar->removeAction(ui->actionLoadScript);
163 ui->menuBar->removeAction(ui->on_actionLoad_a_Scene_from_a_Script_File);
164 #endif
165 // Save some pointers from ui, for latter use.
166 sceneView = ui->sceneView;
167 viewer_window = new SubViewer(ui->mdiArea, this, nullptr);
168 viewer = viewer_window->viewer;
169 CGAL::Three::Three::s_mainviewer = viewer;
170 viewer->setObjectName("mainViewer");
171 viewer_window->showMaximized();
172 viewer_window->setWindowFlags(
173 Qt::SubWindow
174 | Qt::CustomizeWindowHint
175 | Qt::WindowMaximizeButtonHint
176 | Qt::WindowSystemMenuHint
177 | Qt::WindowTitleHint
178 );
179 viewer_window->setWindowTitle("Main Viewer");
180 // setup scene
181 scene = new Scene(this);
182 CGAL::Three::Three::s_scene = scene;
183 CGAL::Three::Three::s_connectable_scene = scene;
184 {
185 QShortcut* shortcut = new QShortcut(QKeySequence(Qt::ALT+Qt::Key_Q), this);
186 connect(shortcut, SIGNAL(activated()),
187 this, SLOT(setFocusToQuickSearch()));
188 shortcut = new QShortcut(QKeySequence(Qt::Key_F5), this);
189 connect(shortcut, SIGNAL(activated()),
190 this, SLOT(reloadItem()));
191 shortcut = new QShortcut(QKeySequence(Qt::Key_F11), this);
192 connect(shortcut, SIGNAL(activated()),
193 this, SLOT(toggleFullScreen()));
194 shortcut = new QShortcut(QKeySequence(Qt::CTRL+Qt::Key_R), this);
195 connect(shortcut, &QShortcut::activated,
196 this, &MainWindow::recenterScene);
197 shortcut = new QShortcut(QKeySequence(Qt::CTRL+Qt::Key_T), this);
198 connect(shortcut, &QShortcut::activated,
199 this,
200 [](){
201 Viewer* viewer = qobject_cast<Viewer*>(CGAL::Three::Three::activeViewer());
202 bool b = viewer->property("draw_two_sides").toBool();
203 viewer->setTwoSides(!b);
204 }
205 );
206 }
207
208 proxyModel = new QSortFilterProxyModel(this);
209 proxyModel->setSourceModel(scene);
210 SceneDelegate *delegate = new SceneDelegate(this);
211 delegate->setProxy(proxyModel);
212 delegate->setScene(scene);
213
214
215 connect(ui->searchEdit, SIGNAL(textChanged(QString)),
216 proxyModel, SLOT(setFilterFixedString(QString)));
217 sceneView->setModel(proxyModel);
218
219 // setup the sceneview: delegation and columns sizing...
220 sceneView->setItemDelegate(delegate);
221 resetHeader();
222
223 // setup connections
224 connect(scene, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex & )),
225 this, SLOT(updateInfo()));
226
227 connect(scene, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex & )),
228 this, SLOT(updateDisplayInfo()));
229
230 connect(viewer, &Viewer::needNewContext,
231 [this](){create();});
232
233
234 connect(scene, SIGNAL(updated()),
235 this, SLOT(selectionChanged()));
236
237 connect(scene, SIGNAL(itemAboutToBeDestroyed(CGAL::Three::Scene_item*)),
238 this, SLOT(removeManipulatedFrame(CGAL::Three::Scene_item*)));
239
240 connect(scene, SIGNAL(updated_bbox(bool)),
241 this, SLOT(invalidate_bbox(bool)));
242
243 connect(scene, SIGNAL(selectionChanged(int)),
244 this, SLOT(selectSceneItem(int)));
245 connect(scene, SIGNAL(selectionChanged(QList<int>)),
246 this, SLOT(selectSceneItems(QList<int>)));
247
248 connect(scene, SIGNAL(itemPicked(const QModelIndex &)),
249 this, SLOT(recenterSceneView(const QModelIndex &)));
250
251 connect(sceneView->selectionModel(),
252 SIGNAL(selectionChanged ( const QItemSelection & , const QItemSelection & ) ),
253 this, SLOT(updateInfo()));
254
255 connect(sceneView->selectionModel(),
256 SIGNAL(selectionChanged ( const QItemSelection & , const QItemSelection & ) ),
257 this, SLOT(updateDisplayInfo()));
258
259 connect(sceneView->selectionModel(),
260 SIGNAL(selectionChanged ( const QItemSelection & , const QItemSelection & ) ),
261 this, SLOT(selectionChanged()));
262 // setup menu filtering
263
264 connect(sceneView->selectionModel(),
265 QOverload<const QItemSelection & , const QItemSelection &>::of(&QItemSelectionModel::selectionChanged),
266 this, [this](){filterOperations(false);});
267
268 sceneView->setContextMenuPolicy(Qt::CustomContextMenu);
269 connect(sceneView, SIGNAL(customContextMenuRequested(const QPoint & )),
270 this, SLOT(showSceneContextMenu(const QPoint &)));
271
272 connect(sceneView, SIGNAL(expanded(QModelIndex)),
273 this, SLOT(setExpanded(QModelIndex)));
274
275 connect(sceneView, SIGNAL(collapsed(QModelIndex)),
276 this, SLOT(setCollapsed(QModelIndex)));
277 connect(this, SIGNAL(collapsed(QModelIndex)),
278 scene, SLOT(setCollapsed(QModelIndex)));
279 connect(this, SIGNAL(expanded(QModelIndex)),
280 scene, SLOT(setExpanded(QModelIndex)));
281
282 connect(scene, SIGNAL(restoreCollapsedState()),
283 this, SLOT(restoreCollapseState()));
284
285 setupViewer(viewer, viewer_window);
286
287 // add the "About CGAL..." and "About demo..." entries
288 this->addAboutCGAL();
289 this->addAboutDemo(":/cgal/Polyhedron_3/about.html");
290
291 // Connect the button "addButton" with actionLoad
292 ui->addButton->setDefaultAction(ui->actionLoad);
293 // Same with "removeButton" and "duplicateButton"
294 ui->removeButton->setDefaultAction(ui->actionErase);
295 ui->duplicateButton->setDefaultAction(ui->actionDuplicate);
296
297 // Connect actionQuit (Ctrl+Q) and qApp->quit()
298 connect(ui->actionQuit, SIGNAL(triggered()),
299 this, SLOT(quit()));
300 // Connect "Select all items"
301 connect(ui->actionSelectAllItems, SIGNAL(triggered()),
302 this, SLOT(selectAll()));
303
304 connect(ui->actionColorItems, SIGNAL(triggered()),
305 this, SLOT(colorItems()));
306
307 // Recent files menu
308 this->addRecentFiles(ui->menuFile, ui->actionQuit);
309 connect(this, SIGNAL(openRecentFile(QString)),
310 this, SLOT(open(QString)));
311
312 // Reset the "Operation menu"
313 clearMenu(ui->menuOperations);
314
315 #ifdef QT_SCRIPT_LIB
316 std::cerr << "Enable scripts.\n";
317 script_engine = new QScriptEngine(this);
318 qScriptRegisterMetaType<CGAL::Three::Scene_item*>(script_engine,
319 myScene_itemToScriptValue,
320 myScene_itemFromScriptValue);
321 # ifdef QT_SCRIPTTOOLS_LIB
322 QScriptEngineDebugger* debugger = new QScriptEngineDebugger(this);
323 debugger->setObjectName("qt script debugger");
324 QAction* debuggerMenuAction =
325 menuBar()->addMenu(debugger->createStandardMenu());
326 debuggerMenuAction->setText(tr("Qt Script &Debug"));
327 for(unsigned int i = 0; i < 9; ++i)
328 {
329 QDockWidget* dock = new QDockWidget(debug_widgets_names[i], this);
330 dock->setObjectName(debug_widgets_names[i]);
331 dock->setWidget(debugger->widget(debug_widgets[i]));
332 this->QMainWindow::addDockWidget(Qt::BottomDockWidgetArea, dock);
333 dock->hide();
334 }
335 debugger->setAutoShowStandardWindow(false);
336 debugger->attachTo(script_engine);
337 # endif // QT_SCRIPTTOOLS_LIB
338 QScriptValue fun = script_engine->newFunction(myPrintFunction);
339 script_engine->globalObject().setProperty("print", fun);
340
341 // evaluate_script("print('hello', 'world', 'from QtScript!')");
342 QScriptValue mainWindowObjectValue = script_engine->newQObject(this);
343 script_engine->globalObject().setProperty("main_window", mainWindowObjectValue);
344
345 QScriptValue sceneObjectValue = script_engine->newQObject(scene);
346 mainWindowObjectValue.setProperty("scene", sceneObjectValue);
347 script_engine->globalObject().setProperty("scene", sceneObjectValue);
348
349 QScriptValue viewerObjectValue = script_engine->newQObject(viewer);
350 mainWindowObjectValue.setProperty("viewer", viewerObjectValue);
351 script_engine->globalObject().setProperty("viewer", viewerObjectValue);
352
353 QScriptValue cameraObjectValue = script_engine->newQObject(viewer->camera());
354 viewerObjectValue.setProperty("camera", cameraObjectValue);
355 script_engine->globalObject().setProperty("camera", cameraObjectValue);
356
357 evaluate_script("var plugins = new Array();");
358 # ifdef QT_SCRIPTTOOLS_LIB
359 QScriptValue debuggerObjectValue = script_engine->newQObject(debugger);
360 script_engine->globalObject().setProperty("debugger", debuggerObjectValue);
361 # endif
362 #endif
363
364 readSettings(); // Among other things, the column widths are stored.
365
366 // Load plugins, and re-enable actions that need it.
367 operationSearchBar.setPlaceholderText("Filter...");
368 searchAction->setDefaultWidget(&operationSearchBar);
369
370 connect(&operationSearchBar, &QLineEdit::textChanged,
371 this, [this](){filterOperations(true);});
372
373 loadPlugins();
374 accepted_keywords.clear();
375
376 // Setup the submenu of the View menu that can toggle the dockwidgets
377 Q_FOREACH(QDockWidget* widget, findChildren<QDockWidget*>()) {
378 widget->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetFloatable);
379 ui->menuDockWindows->addAction(widget->toggleViewAction());
380 }
381 ui->menuDockWindows->removeAction(ui->dummyAction);
382
383
384 this->readState("MainWindow", Size|State);
385
386 //Manages the group_item creation
387 actionAddToGroup= new QAction("Add New Group", this);
388
389 if(actionAddToGroup) {
390 connect(actionAddToGroup, SIGNAL(triggered()),
391 this, SLOT(makeNewGroup()));
392 }
393
394 QMenu* menuFile = findChild<QMenu*>("menuFile");
395 insertActionBeforeLoadPlugin(menuFile, actionAddToGroup);
396 statistics_dlg = nullptr;
397 statistics_ui = new Ui::Statistics_on_item_dialog();
398
399 actionResetDefaultLoaders = new QAction("Reset Default Loaders",this);
400
401 #ifdef QT_SCRIPT_LIB
402 // evaluate_script("print(plugins);");
403 Q_FOREACH(QAction* action, findChildren<QAction*>()) {
404 if(action->objectName() != "") {
405 QScriptValue objectValue = script_engine->newQObject(action);
406 script_engine->globalObject().setProperty(action->objectName(),
407 objectValue);
408 }
409 }
410 filterOperations(true);
411 // debugger->action(QScriptEngineDebugger::InterruptAction)->trigger();
412 #endif
413 }
414
addActionToMenu(QAction * action,QMenu * menu)415 void addActionToMenu(QAction* action, QMenu* menu)
416 {
417 auto actions = menu->actions();
418 auto it = std::lower_bound(actions.begin(), actions.end(),
419 action->text().remove("&"),
420 [](QAction* a, QString text) {
421 return a->text().remove("&").compare(text) < 0;
422 });
423 if(it == actions.end()) {
424 menu->addAction(action);
425 }
426 else {
427 menu->insertAction(*it, action);
428 }
429 }
430
431 //Recursive function that do a pass over a menu and its sub-menus(etc.) and hide them when they are empty
filterMenuOperations(QMenu * menu,QString filter,bool keep_from_here)432 void filterMenuOperations(QMenu* menu, QString filter, bool keep_from_here)
433 {
434 QList<QAction*> buffer;
435 Q_FOREACH(QAction* action, menu->actions())
436 buffer.append(action);
437
438 while(!buffer.isEmpty()){
439 Q_FOREACH(QAction* action, buffer) {
440 if(QMenu* submenu = action->menu())
441 {
442 bool keep = true;
443 if(!keep_from_here){
444 keep = submenu->menuAction()->text().contains(filter, Qt::CaseInsensitive);
445 if(!keep)
446 {
447 Q_FOREACH(QAction* subaction, submenu->actions())
448 {
449 submenu->removeAction(subaction);
450 buffer.append(subaction);
451 }
452 }
453 else
454 {
455 addActionToMenu(submenu->menuAction(), menu);
456 }
457 }
458 filterMenuOperations(submenu, filter, keep);
459 action->setVisible(!(submenu->isEmpty()));
460
461 }
462 else if(action->text().remove("&").contains(filter, Qt::CaseInsensitive)){
463 //menu->addAction(action);
464 addActionToMenu(action, menu);
465 }
466 buffer.removeAll(action);
467 }
468 }
469 }
470
471 #ifdef Q_OS_WIN
filterOperations(bool hide)472 void MainWindow::filterOperations(bool hide)
473 #else
474 void MainWindow::filterOperations(bool)
475 #endif
476 {
477 //on some platforms editing an open menu slows everything like hell,
478 //so we hide it for the time of the process.
479 #ifdef Q_OS_WIN
480 if(hide)
481 ui->menuOperations->hide();
482 #endif
483 //return actions to their true menu
484 Q_FOREACH(QMenu* menu, action_menu_map.values())
485 {
486 Q_FOREACH(QAction* action, menu->actions())
487 {
488 if(action != searchAction)
489 menu->removeAction(action);
490 }
491 }
492
493 Q_FOREACH(QAction* action, action_menu_map.keys())
494 {
495 QMenu* menu = action_menu_map[action];
496 addActionToMenu(action, menu);
497 }
498 QString filter=operationSearchBar.text();
499 Q_FOREACH(const PluginNamePair& p, plugins) {
500 Q_FOREACH(QAction* action, p.first->actions()) {
501 action->setVisible( p.first->applicable(action)
502 && (action->text().remove("&").contains(filter, Qt::CaseInsensitive)
503 || action->property("subMenuName")
504 .toString().contains(filter, Qt::CaseInsensitive)));
505 }
506 }
507 // do a pass over all menus in Operations and their sub-menus(etc.) and hide them when they are empty
508 filterMenuOperations(ui->menuOperations, filter, false);
509 #ifdef Q_OS_WIN
510 if(hide)
511 ui->menuOperations->show();
512 #endif
513 operationSearchBar.setFocus();
514 }
515
516 #include <CGAL/Three/exceptions.h>
517
evaluate_script(QString script,const QString & filename,const bool quiet)518 void MainWindow::evaluate_script(QString script,
519 const QString& filename,
520 const bool quiet) {
521 QScriptContext* context = script_engine->currentContext();
522 QScriptValue object = context->activationObject();
523 QScriptValue former_current_filename = object.property("current_filename");;
524 object.setProperty("current_filename", filename);
525
526 QScriptValue value = script_engine->evaluate(script, filename);
527 if(script_engine->hasUncaughtException()) {
528 QScriptValue js_exception = script_engine->uncaughtException();
529 QScriptValue js_bt =js_exception.property("backtrace");
530 QStringList bt = script_engine->uncaughtExceptionBacktrace();
531 if(js_bt.isValid()) {
532 QStringList other_bt;
533 qScriptValueToSequence(js_bt, other_bt);
534 if(!other_bt.isEmpty()) bt = other_bt;
535 }
536 if(!quiet) {
537 QTextStream err(stderr);
538 err << "Qt Script exception:\n"
539 << js_exception.toString()
540 << "\nBacktrace:\n";
541 Q_FOREACH(QString line, bt) {
542 err << " " << line << "\n";
543 }
544 }
545 throw CGAL::Three::Script_exception
546 (script_engine->uncaughtException().toString(), bt);
547 }
548 else if(!quiet && !value.isNull() && !value.isUndefined()) {
549 QTextStream(stderr) << "Qt Script evaluated to \""
550 << value.toString() << "\"\n";
551 }
552
553 object.setProperty("current_filename", former_current_filename);
554 }
555
evaluate_script_quiet(QString script,const QString & filename)556 void MainWindow::evaluate_script_quiet(QString script,
557 const QString& filename)
558 {
559 evaluate_script(script, filename, true);
560 }
561
enableScriptDebugger(bool b)562 void MainWindow::enableScriptDebugger(bool b /* = true */)
563 {
564 Q_UNUSED(b);
565 #ifdef QT_SCRIPT_LIB
566 # ifdef QT_SCRIPTTOOLS_LIB
567 QScriptEngineDebugger* debugger =
568 findChild<QScriptEngineDebugger*>("qt script debugger");
569 if(debugger) {
570 if(b) {
571 debugger->action(QScriptEngineDebugger::InterruptAction)->trigger();
572 }
573 else {
574 std::cerr << "Detach the script debugger\n";
575 debugger->detach();
576 }
577 }
578 return;
579 # endif
580 #endif
581 // If we are here, then the debugger is not available
582 this->error(tr("Your version of Qt is too old, and for that reason "
583 "the Qt Script Debugger is not available."));
584 }
585
586 namespace {
actionsByName(QAction * x,QAction * y)587 bool actionsByName(QAction* x, QAction* y) {
588 return x->text() < y->text();
589 }
590 }
591
592 //Recursively creates all subMenus containing an action.
593 // In the current implementation, there is a bug if a menu
594 // and a submenu have the same name (cf map menu_map).
setMenus(QString name,QString parentName,QAction * a)595 void MainWindow::setMenus(QString name, QString parentName, QAction* a )
596 {
597 QString menuName, subMenuName;
598 if (name.isNull())
599 return;
600 int slash_index = name.indexOf('/');
601
602 if(slash_index==-1)
603 menuName= name; // no extra sub-menu
604 else
605 {
606 int l = name.length();
607 menuName=name.mid(0,slash_index);
608 subMenuName=name.mid(slash_index+1,l-slash_index-1);
609 // recursively create sub-menus
610 setMenus(subMenuName, menuName, a);
611 }
612
613 //Create the menu if it does not already exist
614 if(!menu_map.contains(menuName))
615 menu_map[menuName] = new QMenu(menuName, this);
616
617 //Create the parent menu if it does not already exist
618 if(!menu_map.contains(parentName))
619 menu_map[parentName] = new QMenu(parentName, this);
620 // add the submenu in the menu
621 menu_map[parentName]->addMenu(menu_map[menuName]);
622 action_menu_map[menu_map[menuName]->menuAction()] = menu_map[parentName];
623
624 // only add the action in the last submenu
625 if(slash_index==-1)
626 {
627 ui->menuOperations->removeAction(a);
628 menu_map[menuName]->addAction(a);
629 action_menu_map[a] = menu_map[menuName];
630 }
631 }
632
load_plugin(QString fileName,bool blacklisted)633 bool MainWindow::load_plugin(QString fileName, bool blacklisted)
634 {
635 if(fileName.contains("plugin") && QLibrary::isLibrary(fileName)) {
636 //set plugin name
637 QFileInfo fileinfo(fileName);
638 //set plugin name
639 QString name = fileinfo.fileName();
640 name.remove(QRegExp("^lib"));
641 name.remove(QRegExp("\\..*"));
642 //do not load it if it is in the blacklist
643 if(blacklisted)
644 {
645 if ( plugin_blacklist.contains(name) ){
646 pluginsStatus_map[name] = QString("Blacklisted.");
647 ignored_map[name] = true;
648 //qDebug("### Ignoring plugin \"%s\".", qPrintable(fileName));
649 PathNames_map[name].push_back(fileinfo.absoluteDir().absolutePath());
650 return true;
651 }
652 }
653 QDebug qdebug = qDebug();
654 if(verbose)
655 qdebug << "### Loading \"" << fileName.toUtf8().data() << "\"... ";
656 QPluginLoader loader;
657 loader.setFileName(fileinfo.absoluteFilePath());
658 QJsonArray keywords = loader.metaData().value("MetaData").toObject().value("Keywords").toArray();
659 QString date = loader.metaData().value("MetaData").toObject().value("ConfigDate").toString();
660 QStringList s_keywords;
661 for(int i = 0; i < keywords.size(); ++i)
662 {
663 s_keywords.append(keywords[i].toString());
664 }
665 plugin_metadata_map[name] = qMakePair(s_keywords, date);
666 QObject *obj = loader.instance();
667 bool do_load = accepted_keywords.empty();
668 if(!do_load)
669 {
670 Q_FOREACH(QString k, s_keywords)
671 {
672 if(accepted_keywords.contains(k))
673 {
674 do_load = true;
675 break;
676 }
677 }
678 }
679 if(do_load && obj) {
680 obj->setObjectName(name);
681 bool init1 = initPlugin(obj);
682 bool init2 = initIOPlugin(obj);
683 if (!init1 && !init2)
684 {
685 //qdebug << "not for this program";
686 pluginsStatus_map[name] = QString("Not for this program.");
687 }
688 else
689 //qdebug << "success";
690 pluginsStatus_map[name] = QString("success");
691 }
692 else if(!do_load)
693 {
694 pluginsStatus_map[name]="Wrong Keywords.";
695 ignored_map[name] = true;
696 }
697 else{
698 //qdebug << "error: " << qPrintable(loader.errorString());
699 pluginsStatus_map[name] = loader.errorString();
700
701 }
702 PathNames_map[name].push_back(fileinfo.absoluteDir().absolutePath());
703 return true;
704 }
705 return false;
706 }
707
loadPlugins()708 void MainWindow::loadPlugins()
709 {
710 Q_FOREACH(QObject *obj, QPluginLoader::staticInstances())
711 {
712 initPlugin(obj);
713 initIOPlugin(obj);
714 }
715
716 QList<QDir> plugins_directories;
717 QString dirPath = qApp->applicationDirPath();
718 plugins_directories<<dirPath;
719 QDir msvc_dir(dirPath);
720 QString build_dir_name = msvc_dir.dirName();//Debug or Release for msvc
721 msvc_dir.cdUp();
722
723 QFileInfoList filist = QDir(dirPath).entryInfoList();
724 filist << msvc_dir.entryInfoList();
725
726 Q_FOREACH(QFileInfo fileinfo, filist)
727 {
728 //checks if the path leads to a directory
729 if(fileinfo.baseName().contains("Plugins"))
730 {
731 QString plugins_dir = fileinfo.absolutePath();
732 plugins_dir.append("/").append(fileinfo.baseName());
733
734 Q_FOREACH(QString package_dir,
735 QDir(plugins_dir).entryList(QDir::Dirs))
736 {
737 QString package_dir_path(plugins_dir);
738 package_dir_path.append("/").append(package_dir);
739
740 QString libdir_path(package_dir_path);
741 libdir_path.append("/").append(build_dir_name);
742
743 if (QDir(libdir_path).exists())
744 plugins_directories << QDir(libdir_path);
745 else
746 plugins_directories << QDir(package_dir_path);
747 }
748 }
749 }
750 QString env_path = qgetenv("POLYHEDRON_DEMO_PLUGINS_PATH");
751 #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
752 QChar separator = QDir::listSeparator();
753 #else
754 #if defined(_WIN32)
755 QChar separator = ';';
756 #else
757 QChar separator = ':';
758 #endif
759 #endif
760 if(!env_path.isEmpty()) {
761 #if defined(_WIN32)
762 QString path = qgetenv("PATH");
763 QByteArray new_path = path.append(env_path.prepend(separator)).toUtf8();
764 qputenv("PATH", new_path);
765 #endif
766 Q_FOREACH (QString pluginsDir,
767 env_path.split(separator, CGAL_QT_SKIP_EMPTY_PARTS)) {
768 QDir dir(pluginsDir);
769 if(dir.isReadable())
770 plugins_directories << dir;
771 }
772 }
773
774 QSet<QString> loaded;
775 Q_FOREACH (QDir pluginsDir, plugins_directories) {
776 if(verbose)
777 qDebug("# Looking for plugins in directory \"%s\"...",
778 qPrintable(pluginsDir.absolutePath()));
779 Q_FOREACH(QString fileName, pluginsDir.entryList(QDir::Files))
780 {
781 QString abs_name = pluginsDir.absoluteFilePath(fileName);
782 if(loaded.find(abs_name) == loaded.end())
783 {
784 if(load_plugin(abs_name, true))
785 {
786 loaded.insert(abs_name);
787 }
788 }
789 }
790 }
791 updateMenus();
792 }
793 //Creates sub-Menus for operations.
updateMenus()794 void MainWindow::updateMenus()
795 {
796 QList<QAction*> as = ui->menuOperations->actions();
797 Q_FOREACH(QAction* a, as)
798 {
799 QString menuPath = a->property("subMenuName").toString();
800 setMenus(menuPath, ui->menuOperations->title(), a);
801 }
802 // sort the operations menu by name
803 as = ui->menuOperations->actions();
804 std::sort(as.begin(), as.end(), actionsByName);
805 ui->menuOperations->clear();
806 ui->menuOperations->addAction(searchAction);
807 ui->menuOperations->addActions(as);
808 operationSearchBar.setFocus();
809 }
810
hasPlugin(const QString & pluginName) const811 bool MainWindow::hasPlugin(const QString& pluginName) const
812 {
813 Q_FOREACH(const PluginNamePair& p, plugins) {
814 if(p.second == pluginName) return true;
815 }
816 return false;
817 }
818
initPlugin(QObject * obj)819 bool MainWindow::initPlugin(QObject* obj)
820 {
821 QObjectList childs = this->children();
822 CGAL::Three::Polyhedron_demo_plugin_interface* plugin =
823 qobject_cast<CGAL::Three::Polyhedron_demo_plugin_interface*>(obj);
824 if(plugin) {
825 // Call plugin's init() method
826 obj->setParent(this);
827 plugin->init(this, this->scene, this);
828 plugins << qMakePair(plugin, obj->objectName());
829 #ifdef QT_SCRIPT_LIB
830 QScriptValue objectValue =
831 script_engine->newQObject(obj);
832 script_engine->globalObject().setProperty(obj->objectName(), objectValue);
833 evaluate_script_quiet(QString("plugins.push(%1);").arg(obj->objectName()));
834 #endif
835
836 Q_FOREACH(QAction* action, plugin->actions()) {
837 // If action does not belong to the menus, add it to "Operations" menu
838 if(!childs.contains(action)) {
839 ui->menuOperations->addAction(action);
840 action_menu_map[action] = ui->menuOperations;
841 }
842 // Show and enable menu item
843 addAction(action);
844 }
845 return true;
846 }
847 else
848 return false;
849 }
850
initIOPlugin(QObject * obj)851 bool MainWindow::initIOPlugin(QObject* obj)
852 {
853 CGAL::Three::Polyhedron_demo_io_plugin_interface* plugin =
854 qobject_cast<CGAL::Three::Polyhedron_demo_io_plugin_interface*>(obj);
855 if(plugin) {
856 plugin->init();
857 io_plugins << plugin;
858 return true;
859 }
860 else
861 return false;
862 }
863
clearMenu(QMenu * menu)864 void MainWindow::clearMenu(QMenu* menu)
865 {
866 Q_FOREACH(QAction* action, menu->actions())
867 {
868 QMenu* menu = action->menu();
869 if(menu) {
870 clearMenu(menu);
871 }
872 action->setVisible(false);
873 }
874 menu->menuAction()->setEnabled(false);
875 }
876
addAction(QAction * action)877 void MainWindow::addAction(QAction* action)
878 {
879 if(!action) return;
880
881 action->setVisible(true);
882 action->setEnabled(true);
883 Q_FOREACH(QWidget* widget, action->associatedWidgets())
884 {
885 // qDebug() << QString("%1 (%2)\n")
886 // .arg(widget->objectName())
887 // .arg(widget->metaObject()->className());
888 QMenu* menu = qobject_cast<QMenu*>(widget);
889 if(menu)
890 {
891 addAction(menu->menuAction());
892 }
893 }
894 }
895
addAction(QString actionName,QString actionText,QString menuName)896 void MainWindow::addAction(QString actionName,
897 QString actionText,
898 QString menuName) {
899 QMenu* menu = nullptr;
900 Q_FOREACH(QAction* action, findChildren<QAction*>()) {
901 if(!action->menu()) continue;
902 QString menuText = action->menu()->title();
903 if(menuText != menuName) continue;
904 menu = action->menu();
905 }
906 if(menu == nullptr) {
907 menu = new QMenu(menuName, this);
908 menuBar()->insertMenu(ui->menuView->menuAction(), menu);
909 }
910 QAction* action = new QAction(actionText, this);
911 action->setObjectName(actionName);
912 menu->addAction(action);
913 #ifdef QT_SCRIPT_LIB
914 QScriptValue objectValue = script_engine->newQObject(action);
915 script_engine->globalObject().setProperty(action->objectName(),
916 objectValue);
917 #endif
918 }
919
viewerShow(float xmin,float ymin,float zmin,float xmax,float ymax,float zmax)920 void MainWindow::viewerShow(float xmin,
921 float ymin,
922 float zmin,
923 float xmax,
924 float ymax,
925 float zmax)
926 {
927 CGAL::qglviewer::Vec
928 min_(xmin, ymin, zmin),
929 max_(xmax, ymax, zmax);
930
931 if(min_ == max_) return viewerShow(viewer, xmin, ymin, zmin);
932
933 viewer->camera()->setPivotPoint((min_+max_)*0.5);
934
935 CGAL::qglviewer::ManipulatedCameraFrame backup_frame(*viewer->camera()->frame());
936 viewer->camera()->fitBoundingBox(min_, max_);
937 CGAL::qglviewer::ManipulatedCameraFrame new_frame(*viewer->camera()->frame());
938 *viewer->camera()->frame() = backup_frame;
939 viewer->camera()->interpolateTo(new_frame, 1.f);
940 viewer->setVisualHintsMask(1);
941 }
942
viewerShow(Viewer_interface * vi,float x,float y,float z)943 void MainWindow::viewerShow(Viewer_interface* vi, float x, float y, float z) {
944
945 CGAL::qglviewer::ManipulatedCameraFrame backup_frame(*vi->camera()->frame());
946 vi->camera()->fitSphere(CGAL::qglviewer::Vec(x, y, z),
947 vi->camera()->sceneRadius()/100);
948 CGAL::qglviewer::ManipulatedCameraFrame new_frame(*vi->camera()->frame());
949 *vi->camera()->frame() = backup_frame;
950 vi->camera()->interpolateTo(new_frame, 1.f);
951 vi->setVisualHintsMask(1);
952
953 vi->camera()->setPivotPoint(CGAL::qglviewer::Vec(x, y, z));
954 }
955
message(QString message,QString colorName,QString font)956 void MainWindow::message(QString message, QString colorName, QString font) {
957 if (message.endsWith('\n')) {
958 message.remove(message.length()-1, 1);
959 }
960 statusBar()->showMessage(message, 5000);
961 QTimer::singleShot(5000, [this]{this->statusBar()->setStyleSheet("");});
962 message = "<font color=\"" + colorName + "\" style=\"font-style: " + font + ";\" >" +
963 message + "</font><br>";
964 message = "[" + QTime::currentTime().toString() + "] " + message;
965 ui->consoleTextEdit->append(message);
966 ui->consoleTextEdit->verticalScrollBar()->setValue(ui->consoleTextEdit->verticalScrollBar()->maximum());
967 }
968
message_information(QString text)969 void MainWindow::message_information(QString text) {
970 statusBar()->setStyleSheet("color: blue");
971 this->message("INFO: " + text, "blue");
972 }
973
message_warning(QString text)974 void MainWindow::message_warning(QString text) {
975 statusBar()->setStyleSheet("color: orange");
976 this->message("WARNING: " + text, "orange");
977 }
978
message_error(QString text)979 void MainWindow::message_error(QString text) {
980 statusBar()->setStyleSheet("color: red");
981 this->message("ERROR: " + text, "red");
982 }
983
updateViewersBboxes(bool recenter)984 void MainWindow::updateViewersBboxes(bool recenter)
985 {
986 if(bbox_need_update)
987 {
988 CGAL::qglviewer::Vec min, max;
989 computeViewerBBox(min, max);
990 Q_FOREACH(CGAL::QGLViewer* v, CGAL::QGLViewer::QGLViewerPool())
991 {
992 if(v == nullptr)
993 continue;
994 Viewer* vi = static_cast<Viewer*>(v);
995 updateViewerBbox(vi, recenter, min, max);
996 }
997 bbox_need_update = false;
998 }
999
1000 }
1001
computeViewerBBox(CGAL::qglviewer::Vec & vmin,CGAL::qglviewer::Vec & vmax)1002 void MainWindow::computeViewerBBox(CGAL::qglviewer::Vec& vmin, CGAL::qglviewer::Vec& vmax)
1003 {
1004 const Scene::Bbox bbox = scene->bbox();
1005 const double xmin = bbox.xmin();
1006 const double ymin = bbox.ymin();
1007 const double zmin = bbox.zmin();
1008 const double xmax = bbox.xmax();
1009 const double ymax = bbox.ymax();
1010 const double zmax = bbox.zmax();
1011
1012
1013
1014 vmin = CGAL::qglviewer::Vec(xmin, ymin, zmin);
1015 vmax= CGAL::qglviewer::Vec(xmax, ymax, zmax);
1016
1017 CGAL::qglviewer::Vec bbox_center((xmin+xmax)/2, (ymin+ymax)/2, (zmin+zmax)/2);
1018
1019 double bbox_diag = CGAL::approximate_sqrt(
1020 CGAL::square(xmax - xmin)
1021 + CGAL::square(ymax - ymin)
1022 + CGAL::square(zmax - zmin));
1023
1024 CGAL::qglviewer::Vec offset(0,0,0);
1025
1026 double l_dist = (std::max)((std::abs)(bbox_center.x - viewer->offset().x),
1027 (std::max)((std::abs)(bbox_center.y - viewer->offset().y),
1028 (std::abs)(bbox_center.z - viewer->offset().z)));
1029 if((std::log2)(l_dist/bbox_diag) > 11.0 )
1030 for(int i=0; i<3; ++i)
1031 {
1032 offset[i] = -bbox_center[i];
1033 }
1034 if(offset != viewer->offset())
1035 {
1036 Q_FOREACH(CGAL::QGLViewer* v, CGAL::QGLViewer::QGLViewerPool())
1037 {
1038 if(v == nullptr)
1039 continue;
1040 Viewer* vi = qobject_cast<Viewer*>(v);
1041 vi->setOffset(offset);
1042 }
1043 for(int i=0; i<scene->numberOfEntries(); ++i)
1044 {
1045 // scene->item(i)->invalidate(Scene_item::GEOMETRY);
1046 scene->item(i)->invalidateOpenGLBuffers();
1047 scene->item(i)->itemChanged();
1048 }
1049 }
1050 }
1051
reloadItem()1052 void MainWindow::reloadItem() {
1053
1054 Scene_item* item = nullptr;
1055
1056 Q_FOREACH(Scene::Item_id id, scene->selectionIndices())
1057 {
1058 item = scene->item(id);
1059 if(!item)//secure items like selection items that get deleted when their "parent" item is reloaded.
1060 continue;
1061 QString filename = item->property("source filename").toString();
1062 QString loader_name = item->property("loader_name").toString();
1063 if(filename.isEmpty() || loader_name.isEmpty()) {
1064 this->warning(QString("Cannot reload item %1: "
1065 "the item has no \"source filename\" or no \"loader_name\" attached\n").arg(item->name()));
1066 continue;
1067 }
1068
1069 CGAL::Three::Polyhedron_demo_io_plugin_interface* fileloader = findLoader(loader_name);
1070 QFileInfo fileinfo(filename);
1071 bool ok;
1072 QList<Scene_item*> new_items = loadItem(fileinfo, fileloader, ok, false);
1073 if(!ok)
1074 return;
1075 QVariant varian = item->property("load_mates");
1076 if(!varian.isValid()) //typically when a soup is oriented, the soup_item is deleted and thus the varain points to an unexisting item.
1077 {
1078 Scene_item* new_item = new_items.front();
1079 new_item->setName(item->name());
1080 new_item->setColor(item->color());
1081 new_item->setRenderingMode(item->renderingMode());
1082 new_item->setVisible(item->visible());
1083 Scene_item_with_properties *property_item = dynamic_cast<Scene_item_with_properties*>(new_item);
1084 scene->replaceItem(scene->item_id(item), new_item, true);
1085 if(property_item)
1086 property_item->copyProperties(item);
1087 new_item->invalidateOpenGLBuffers();
1088 item->deleteLater();
1089 return;
1090 }
1091 QSequentialIterable iterable = varian.value<QSequentialIterable>();
1092
1093 // Can use foreach:
1094 int mate_id = 0;
1095 Q_FOREACH(const QVariant &v, iterable)
1096 {
1097 Scene_item* mate = v.value<Scene_item*>();
1098 Scene_item* new_item = new_items[mate_id];
1099 new_item->setName(mate->name());
1100 new_item->setColor(mate->color());
1101 new_item->setRenderingMode(mate->renderingMode());
1102 new_item->setVisible(mate->visible());
1103 Scene_item_with_properties *property_item = dynamic_cast<Scene_item_with_properties*>(new_item);
1104 scene->replaceItem(scene->item_id(mate), new_item, true);
1105 if(property_item)
1106 property_item->copyProperties(mate);
1107 new_item->invalidateOpenGLBuffers();
1108 mate->deleteLater();
1109 }
1110 }
1111 }
1112
findLoader(const QString & loader_name) const1113 CGAL::Three::Polyhedron_demo_io_plugin_interface* MainWindow::findLoader(const QString& loader_name) const {
1114 Q_FOREACH(CGAL::Three::Polyhedron_demo_io_plugin_interface* io_plugin,
1115 io_plugins) {
1116 if(io_plugin->name() == loader_name) {
1117 return io_plugin;
1118 }
1119 }
1120 throw std::invalid_argument(QString("No loader found with the name %1 available")
1121 .arg(loader_name).toStdString()) ;
1122 }
1123
file_matches_filter(const QString & filters,const QString & filename)1124 bool MainWindow::file_matches_filter(const QString& filters,
1125 const QString& filename )
1126 {
1127 QFileInfo fileinfo(filename);
1128 QString filename_striped=fileinfo.fileName();
1129
1130 //match all filters between ()
1131 QRegExp all_filters_rx("\\((.*)\\)");
1132
1133 QStringList split_filters = filters.split(";;");
1134 Q_FOREACH(const QString& filter, split_filters) {
1135 //extract filters
1136 if ( all_filters_rx.indexIn(filter)!=-1 ){
1137 Q_FOREACH(const QString& pattern,all_filters_rx.cap(1).split(' ')){
1138 QRegExp rx(pattern);
1139 rx.setPatternSyntax(QRegExp::Wildcard);
1140 if ( rx.exactMatch(filename_striped) ){
1141 return true;
1142 }
1143 }
1144 }
1145 }
1146 return false;
1147 }
1148
open(QString filename)1149 void MainWindow::open(QString filename)
1150 {
1151 QFileInfo fileinfo(filename);
1152
1153 #ifdef QT_SCRIPT_LIB
1154 // Handles the loading of script file from the command line arguments,
1155 // and the special command line arguments that start with "javascript:"
1156 // or "qtscript:"
1157 QString program;
1158 if(filename.startsWith("javascript:")) {
1159 program=filename.right(filename.size() - 11);
1160 }
1161 if(filename.startsWith("qtscript:")) {
1162 program=filename.right(filename.size() - 9);
1163 }
1164 if(filename.endsWith(".js")) {
1165 loadScript(fileinfo);
1166 return;
1167 }
1168 if(!program.isEmpty())
1169 {
1170 {
1171 QTextStream(stderr) << "Execution of script \""
1172 << filename << "\"\n";
1173 // << filename << "\", with following content:\n"
1174 // << program;
1175 }
1176 QApplication::setOverrideCursor(Qt::WaitCursor);
1177 evaluate_script(program, filename);
1178 QApplication::restoreOverrideCursor();
1179 return;
1180 }
1181 #endif
1182
1183 if ( !fileinfo.exists() ){
1184 QMessageBox::warning(this,
1185 tr("Cannot open file"),
1186 tr("File %1 does not exist.")
1187 .arg(filename));
1188 return;
1189 }
1190
1191
1192 QStringList selected_items;
1193 QStringList all_items;
1194
1195 QMap<QString,QString>::iterator dfs_it =
1196 default_plugin_selection.find( fileinfo.completeSuffix() );
1197
1198 if ( dfs_it==default_plugin_selection.end() )
1199 {
1200 // collect all io_plugins and offer them to load if the file extension match one name filter
1201 // also collect all available plugin in case of a no extension match
1202 for(CGAL::Three::Polyhedron_demo_io_plugin_interface* io_plugin : io_plugins) {
1203 if ( file_matches_filter(io_plugin->loadNameFilters(), filename.toLower()) )
1204 {
1205 if ( !io_plugin->canLoad(fileinfo) ) continue;
1206 all_items << io_plugin->name();
1207 if(io_plugin->isDefaultLoader(fileinfo.completeSuffix()))
1208 selected_items.prepend(io_plugin->name());
1209 else
1210 selected_items << io_plugin->name();
1211 }
1212 }
1213 //if no plugin is correct, offer them all.
1214 for(CGAL::Three::Polyhedron_demo_io_plugin_interface* io_plugin : io_plugins) {
1215 all_items << io_plugin->name();
1216 }
1217 }
1218 else
1219 selected_items << *dfs_it;
1220
1221 bool ok;
1222 std::pair<QString, bool> load_pair;
1223
1224 switch( selected_items.size() )
1225 {
1226 case 1:
1227 load_pair = std::make_pair(selected_items.first(), false);
1228 ok=true;
1229 break;
1230 case 0:
1231 load_pair = File_loader_dialog::getItem(fileinfo.fileName(), all_items, &ok);
1232 break;
1233 default:
1234 load_pair = File_loader_dialog::getItem(fileinfo.fileName(), selected_items, &ok);
1235 }
1236 //viewer->makeCurrent();
1237 if(!ok || load_pair.first.isEmpty()) { return; }
1238
1239 if (load_pair.second)
1240 {
1241 connect(actionResetDefaultLoaders, SIGNAL(triggered()),
1242 this, SLOT(reset_default_loaders()));
1243 default_plugin_selection[fileinfo.completeSuffix()]=load_pair.first;
1244 insertActionBeforeLoadPlugin(ui->menuFile, actionResetDefaultLoaders);
1245 }
1246
1247
1248 settings.setValue("OFF open directory",
1249 fileinfo.absoluteDir().absolutePath());
1250 loadItem(fileinfo, findLoader(load_pair.first), ok);
1251
1252 if(!ok)
1253 return;
1254 this->addToRecentFiles(fileinfo.absoluteFilePath());
1255 updateViewersBboxes(true);
1256 }
1257
open(QString filename,QString loader_name)1258 bool MainWindow::open(QString filename, QString loader_name) {
1259 QFileInfo fileinfo(filename);
1260 boost::optional<bool> item_opt;
1261 try {
1262 item_opt = wrap_a_call_to_cpp
1263 ([this, fileinfo, loader_name]()
1264 {
1265 bool ok;
1266 loadItem(fileinfo, findLoader(loader_name), ok);
1267 return ok;
1268 },
1269 this, __FILE__, __LINE__
1270 );
1271 if(!item_opt) return false;
1272 }
1273 catch(std::logic_error& e) {
1274 std::cerr << e.what() << std::endl;
1275 return false;
1276 }
1277 return true;
1278 }
1279
1280
loadItem(QFileInfo fileinfo,CGAL::Three::Polyhedron_demo_io_plugin_interface * loader,bool & ok,bool add_to_scene)1281 QList<Scene_item*> MainWindow::loadItem(QFileInfo fileinfo,
1282 CGAL::Three::Polyhedron_demo_io_plugin_interface* loader,
1283 bool &ok,
1284 bool add_to_scene) {
1285 if(!fileinfo.isFile() || !fileinfo.isReadable()) {
1286 QMessageBox::warning(this, tr("Error"),
1287 QString("File %1 is not a readable file.")
1288 .arg(fileinfo.absoluteFilePath()));
1289 }
1290 QCursor tmp_cursor(Qt::WaitCursor);
1291 CGAL::Three::Three::CursorScopeGuard guard(tmp_cursor);
1292 QList<Scene_item*> result = loader->load(fileinfo, ok, add_to_scene);
1293 if(!ok)
1294 {
1295 QApplication::restoreOverrideCursor();
1296 QMessageBox::warning(this, tr("Error"),
1297 QString("Could not load item from file %1 using plugin %2")
1298 .arg(fileinfo.absoluteFilePath()).arg(loader->name()));
1299 return QList<Scene_item*>();
1300 }
1301 selectSceneItem(scene->item_id(result.back()));
1302 for(Scene_item* item : result)
1303 {
1304 CGAL::Three::Scene_group_item* group =
1305 qobject_cast<CGAL::Three::Scene_group_item*>(item);
1306 if(group)
1307 scene->redraw_model();
1308 item->setProperty("source filename", fileinfo.absoluteFilePath());
1309 item->setProperty("loader_name", loader->name());
1310 item->setProperty("load_mates",QVariant::fromValue(result));
1311 }
1312 return result;
1313 }
1314
1315
setFocusToQuickSearch()1316 void MainWindow::setFocusToQuickSearch()
1317 {
1318 ui->searchEdit->setFocus(Qt::ShortcutFocusReason);
1319 }
1320
selectSceneItem(int i)1321 void MainWindow::selectSceneItem(int i)
1322 {
1323 if(i < 0 || i >= scene->numberOfEntries()) {
1324 sceneView->selectionModel()->clearSelection();
1325 updateInfo();
1326 updateDisplayInfo();
1327 }
1328 else {
1329 QItemSelection s =
1330 proxyModel->mapSelectionFromSource(scene->createSelection(i));
1331 if(s.empty())
1332 return;
1333 QModelIndex mi = proxyModel->mapFromSource(scene->getModelIndexFromId(i).first());
1334 sceneView->setCurrentIndex(mi);
1335 sceneView->selectionModel()->select(s,
1336 QItemSelectionModel::ClearAndSelect);
1337 sceneView->scrollTo(s.indexes().first());
1338 sceneView->setCurrentIndex(sceneView->selectionModel()->selectedIndexes().first());
1339 }
1340 }
1341
selectSceneItems(QList<int> is)1342 void MainWindow::selectSceneItems(QList<int> is)
1343 {
1344 if(is.first() < 0 || is.last() >= scene->numberOfEntries()) {
1345 sceneView->selectionModel()->clearSelection();
1346 updateInfo();
1347 updateDisplayInfo();
1348 }
1349 else {
1350 QItemSelection s =
1351 proxyModel->mapSelectionFromSource(scene->createSelection(is));
1352
1353 QModelIndex i = proxyModel->mapFromSource(scene->getModelIndexFromId(is.first()).first());
1354 sceneView->setCurrentIndex(i);
1355 sceneView->selectionModel()->select(s,
1356 QItemSelectionModel::ClearAndSelect);
1357 if(!s.empty())
1358 sceneView->scrollTo(s.indexes().first());
1359 }
1360 }
1361
1362
showSelectedPoint(double x,double y,double z)1363 void MainWindow::showSelectedPoint(double x, double y, double z)
1364 {
1365 static double x_prev = 0;
1366 static double y_prev = 0;
1367 static double z_prev = 0;
1368 double dist = std::sqrt((x-x_prev)*(x-x_prev) + (y-y_prev)*(y-y_prev) + (z-z_prev)*(z-z_prev));
1369 information(QString("Selected point: (%1, %2, %3) distance to previous: %4").
1370 arg(x, 0, 'g', 10).
1371 arg(y, 0, 'g', 10).
1372 arg(z, 0, 'g', 10).
1373 arg(dist,0,'g',10));
1374 x_prev = x;
1375 y_prev = y;
1376 z_prev = z;
1377 }
1378
unSelectSceneItem(int i)1379 void MainWindow::unSelectSceneItem(int i)
1380 {
1381 removeSceneItemFromSelection(i);
1382 }
1383
addSceneItemInSelection(int i)1384 void MainWindow::addSceneItemInSelection(int i)
1385 {
1386 QItemSelection s =
1387 proxyModel->mapSelectionFromSource(scene->createSelection(i));
1388 sceneView->selectionModel()->select(s, QItemSelectionModel::Select);
1389 scene->itemChanged(i);
1390 }
1391
removeSceneItemFromSelection(int i)1392 void MainWindow::removeSceneItemFromSelection(int i)
1393 {
1394 QItemSelection s =
1395 proxyModel->mapSelectionFromSource(scene->createSelection(i));
1396 sceneView->selectionModel()->select(s,
1397 QItemSelectionModel::Deselect);
1398 scene->itemChanged(i);
1399 }
1400
selectAll()1401 void MainWindow::selectAll()
1402 {
1403 sceneView->selectAll();
1404 }
1405
getSelectedSceneItemIndex() const1406 int MainWindow::getSelectedSceneItemIndex() const
1407 {
1408 QModelIndexList selectedRows = sceneView->selectionModel()->selectedIndexes();
1409 if(selectedRows.size() == 0)
1410 return -1;
1411 else {
1412 QModelIndex i = proxyModel->mapToSource(selectedRows.first());
1413 return scene->getIdFromModelIndex(i);
1414 }
1415 }
1416
getSelectedSceneItemIndices() const1417 QList<int> MainWindow::getSelectedSceneItemIndices() const
1418 {
1419 QModelIndexList selectedIndices = sceneView->selectionModel()->selectedIndexes();
1420 QList<int> result;
1421 Q_FOREACH(QModelIndex index, selectedIndices) {
1422 int temp = scene->getIdFromModelIndex(proxyModel->mapToSource(index));
1423 if(!result.contains(temp))
1424 result<<temp;
1425 }
1426 return result;
1427 }
1428
selectionChanged()1429 void MainWindow::selectionChanged()
1430 {
1431 scene->setSelectedItemIndex(getSelectedSceneItemIndex());
1432 scene->setSelectedItemsList(getSelectedSceneItemIndices());
1433 CGAL::Three::Scene_item* item = scene->item(getSelectedSceneItemIndex());
1434 Q_FOREACH(CGAL::QGLViewer* vi, CGAL::QGLViewer::QGLViewerPool())
1435 {
1436 if(vi == nullptr)
1437 continue;
1438
1439 if(item != nullptr && item->manipulatable()) {
1440 vi->setManipulatedFrame(item->manipulatedFrame());
1441 } else {
1442 vi->setManipulatedFrame(nullptr);
1443 }
1444 if(vi->manipulatedFrame() == nullptr) {
1445 Q_FOREACH(CGAL::Three::Scene_item* item, scene->entries()) {
1446 if(item->manipulatable() && item->manipulatedFrame() != nullptr) {
1447 if(vi->manipulatedFrame() != nullptr) {
1448 // there are at least two possible frames
1449 vi->setManipulatedFrame(nullptr);
1450 break;
1451 } else {
1452 vi->setManipulatedFrame(item->manipulatedFrame());
1453 }
1454 }
1455 }
1456 }
1457 if(vi->manipulatedFrame() != nullptr) {
1458 connect(vi->manipulatedFrame(), SIGNAL(modified()),
1459 this, SLOT(updateInfo()));
1460 }
1461 vi->update();
1462 }
1463 }
contextMenuRequested(const QPoint & global_pos)1464 void MainWindow::contextMenuRequested(const QPoint& global_pos) {
1465 int index = scene->mainSelectionIndex();
1466 showSceneContextMenu(index, global_pos);
1467 }
1468
showSceneContextMenu(int selectedItemIndex,const QPoint & global_pos)1469 void MainWindow::showSceneContextMenu(int selectedItemIndex,
1470 const QPoint& global_pos)
1471 {
1472 CGAL::Three::Scene_item* item = scene->item(selectedItemIndex);
1473 if(!item) return;
1474
1475 const char* prop_name = "Menu modified by MainWindow.";
1476
1477 QMenu* menu = item->contextMenu();
1478 if(menu) {
1479 bool menuChanged = menu->property(prop_name).toBool();
1480 if(!menuChanged) {
1481 if(item->has_stats())
1482 {
1483 QAction* actionStatistics =
1484 menu->addAction(tr("Statistics..."));
1485 actionStatistics->setObjectName("actionStatisticsOnPolyhedron");
1486 connect(actionStatistics, SIGNAL(triggered()),
1487 this, SLOT(statisticsOnItem()));
1488 }
1489 menu->addSeparator();
1490 if(!item->property("source filename").toString().isEmpty()) {
1491 QAction* reload = menu->addAction(tr("&Reload Item from File"));
1492 reload->setProperty("is_groupable", true);
1493 connect(reload, SIGNAL(triggered()),
1494 this, SLOT(reloadItem()));
1495 }
1496 QAction* saveas = menu->addAction(tr("&Save as..."));
1497 saveas->setData(QVariant::fromValue((void*)item));
1498 connect(saveas, SIGNAL(triggered()),
1499 this, SLOT(on_actionSaveAs_triggered()));
1500 QAction* showobject = menu->addAction(tr("&Zoom to this Object"));
1501 showobject->setData(QVariant::fromValue((void*)item));
1502 connect(showobject, SIGNAL(triggered()),
1503 this, SLOT(viewerShowObject()));
1504
1505 menu->setProperty(prop_name, true);
1506 }
1507 }
1508 menu->addMenu(ui->menuOperations);
1509
1510 if(menu)
1511 menu->exec(global_pos);
1512 }
1513
showSceneContextMenu(const QPoint & p)1514 void MainWindow::showSceneContextMenu(const QPoint& p) {
1515 QWidget* sender = qobject_cast<QWidget*>(this->sender());
1516 if(!sender) return;
1517 if(scene->selectionIndices().isEmpty())return;
1518 int main_index = scene->selectionIndices().first();
1519
1520 if(sender == sceneView) {
1521 QModelIndex modelIndex = sceneView->indexAt(p);
1522 if(!modelIndex.isValid())
1523 {
1524 const char* prop_name = "Menu modified by MainWindow.";
1525
1526 QMenu* menu = ui->menuFile;
1527 if(menu) {
1528 bool menuChanged = menu->property(prop_name).toBool();
1529 if(!menuChanged) {
1530 menu->setProperty(prop_name, true);
1531 }
1532 }
1533 if(menu)
1534 menu->exec(sender->mapToGlobal(p));
1535 return;
1536 }
1537 else if(scene->selectionIndices().size() > 1 )
1538 {
1539 QMap<QString, QAction*> menu_actions;
1540 QVector<QMenu*> slider_menus;
1541 bool has_stats = false;
1542 bool has_reload = false;
1543 Q_FOREACH(Scene::Item_id id, scene->selectionIndices())
1544 {
1545 if(!scene->item(id)->property("source filename").toString().isEmpty())
1546 {
1547 has_reload = true;
1548 break;
1549 }
1550 }
1551 Q_FOREACH(QAction* action, scene->item(main_index)->contextMenu()->actions())
1552 {
1553 if(action->property("is_groupable").toBool())
1554 {
1555 menu_actions[action->text()] = action;
1556 if(action->text() == QString("Alpha value"))
1557 {
1558 menu_actions["alpha slider"] = action->menu()->actions().last();
1559 }
1560 else if(action->text() == QString("Points Size"))
1561 {
1562 menu_actions["points slider"] = action->menu()->actions().last();
1563 }
1564 else if(action->text() == QString("Normals Length"))
1565 {
1566 menu_actions["normals slider"] = action->menu()->actions().last();
1567 }
1568 else if(action->text() == QString("Line Width"))
1569 {
1570 menu_actions["Line width"] = action->menu()->actions().last();
1571 }
1572 }
1573
1574 }
1575 Q_FOREACH(Scene::Item_id index, scene->selectionIndices())
1576 {
1577 if(index == main_index)
1578 continue;
1579
1580 CGAL::Three::Scene_item* item = scene->item(index);
1581 if(!item)
1582 continue;
1583 if(item->has_stats())
1584 has_stats = true;
1585 }
1586 QMenu menu;
1587 menu.addAction(actionAddToGroup);
1588 menu.insertSeparator(nullptr);
1589 Q_FOREACH(QString name, menu_actions.keys())
1590 {
1591 if(name == QString("alpha slider")
1592 || name == QString("points slider")
1593 || name == QString("normals slider"))
1594 continue;
1595 if(name == QString("Alpha value"))
1596 {
1597 QWidgetAction* sliderAction = new QWidgetAction(&menu);
1598 QSlider* slider = new QSlider(&menu);
1599 slider->setMinimum(0);
1600 slider->setMaximum(255);
1601 slider->setValue(
1602 qobject_cast<QSlider*>(
1603 qobject_cast<QWidgetAction*>
1604 (menu_actions["alpha slider"])->defaultWidget()
1605 )->value());
1606 slider->setOrientation(Qt::Horizontal);
1607 sliderAction->setDefaultWidget(slider);
1608
1609 connect(slider, &QSlider::valueChanged, [this, slider]()
1610 {
1611 Q_FOREACH(Scene::Item_id id, scene->selectionIndices())
1612 {
1613 Scene_item* item = scene->item(id);
1614 Q_FOREACH(QAction* action, item->contextMenu()->actions())
1615 {
1616 if(action->text() == "Alpha value")
1617 {
1618 QWidgetAction* sliderAction = qobject_cast<QWidgetAction*>(action->menu()->actions().last());
1619 QSlider* ac_slider = qobject_cast<QSlider*>(sliderAction->defaultWidget());
1620 ac_slider->setValue(slider->value());
1621 break;
1622 }
1623 }
1624 }
1625 });
1626 QMenu* new_menu = new QMenu("Alpha value", &menu);
1627 new_menu->addAction(sliderAction);
1628 slider_menus.push_back(new_menu);
1629 }
1630 else if(name == QString("Points Size"))
1631 {
1632 QWidgetAction* sliderAction = new QWidgetAction(&menu);
1633 QSlider* slider = new QSlider(&menu);
1634 slider->setMinimum(1);
1635 slider->setMaximum(25);
1636 slider->setValue(
1637 qobject_cast<QSlider*>(
1638 qobject_cast<QWidgetAction*>
1639 (menu_actions["points slider"])->defaultWidget()
1640 )->value());
1641 slider->setOrientation(Qt::Horizontal);
1642 sliderAction->setDefaultWidget(slider);
1643
1644 connect(slider, &QSlider::valueChanged, [this, slider]()
1645 {
1646 Q_FOREACH(Scene::Item_id id, scene->selectionIndices())
1647 {
1648 Scene_item* item = scene->item(id);
1649 Q_FOREACH(QAction* action, item->contextMenu()->actions())
1650 {
1651 if(action->text() == "Points Size")
1652 {
1653 QWidgetAction* sliderAction = qobject_cast<QWidgetAction*>(action->menu()->actions().last());
1654 QSlider* ac_slider = qobject_cast<QSlider*>(sliderAction->defaultWidget());
1655 ac_slider->setValue(slider->value());
1656 break;
1657 }
1658 }
1659 }
1660 });
1661 QMenu* new_menu = new QMenu("Points Size", &menu);
1662 new_menu->addAction(sliderAction);
1663 slider_menus.push_back(new_menu);
1664 }
1665 else if(name == QString("Normals Length"))
1666 {
1667 QWidgetAction* sliderAction = new QWidgetAction(&menu);
1668 QSlider* slider = new QSlider(&menu);
1669 slider->setMinimum(0);
1670 slider->setMaximum(100);
1671 slider->setValue(
1672 qobject_cast<QSlider*>(
1673 qobject_cast<QWidgetAction*>
1674 (menu_actions["normals slider"])->defaultWidget()
1675 )->value());
1676 slider->setOrientation(Qt::Horizontal);
1677 sliderAction->setDefaultWidget(slider);
1678
1679 connect(slider, &QSlider::valueChanged, [this, slider]()
1680 {
1681 Q_FOREACH(Scene::Item_id id, scene->selectionIndices())
1682 {
1683 Scene_item* item = scene->item(id);
1684 Q_FOREACH(QAction* action, item->contextMenu()->actions())
1685 {
1686 if(action->text() == "Normals Length")
1687 {
1688 QWidgetAction* sliderAction = qobject_cast<QWidgetAction*>(action->menu()->actions().last());
1689 QSlider* ac_slider = qobject_cast<QSlider*>(sliderAction->defaultWidget());
1690 ac_slider->setValue(slider->value());
1691 break;
1692 }
1693 }
1694 }
1695 });
1696 QMenu* new_menu = new QMenu("Normals Length", &menu);
1697 new_menu->addAction(sliderAction);
1698 slider_menus.push_back(new_menu);
1699 }
1700 else if(name == QString("Line Width"))
1701 {
1702 QWidgetAction* sliderAction = new QWidgetAction(&menu);
1703 QSlider* slider = new QSlider(&menu);
1704 slider->setMinimum(1);
1705 float lineWidth[2];
1706 if(!viewer->isOpenGL_4_3())
1707 viewer->glGetFloatv(GL_LINE_WIDTH_RANGE, lineWidth);
1708 else
1709 {
1710 lineWidth[0] = 0;
1711 lineWidth[1] = 10;
1712 }
1713 slider->setMaximum(lineWidth[1]);
1714 slider->setValue(
1715 qobject_cast<QSlider*>(
1716 qobject_cast<QWidgetAction*>
1717 (menu_actions["Line width"])->defaultWidget()
1718 )->value());
1719 slider->setOrientation(Qt::Horizontal);
1720 sliderAction->setDefaultWidget(slider);
1721
1722 connect(slider, &QSlider::valueChanged, [this, slider]()
1723 {
1724 Q_FOREACH(Scene::Item_id id, scene->selectionIndices())
1725 {
1726 Scene_item* item = scene->item(id);
1727 Q_FOREACH(QAction* action, item->contextMenu()->actions())
1728 {
1729 if(action->text() == "Line Width")
1730 {
1731 QWidgetAction* sliderAction = qobject_cast<QWidgetAction*>(action->menu()->actions().last());
1732 QSlider* ac_slider = qobject_cast<QSlider*>(sliderAction->defaultWidget());
1733 ac_slider->setValue(slider->value());
1734 break;
1735 }
1736 }
1737 }
1738 });
1739 QMenu* new_menu = new QMenu("Line Width", &menu);
1740 new_menu->addAction(sliderAction);
1741 slider_menus.push_back(new_menu);
1742 }
1743 else
1744 {
1745 QAction* action = menu.addAction(name);
1746 connect(action, &QAction::triggered, this, &MainWindow::propagate_action);
1747 }
1748 }
1749 if(!slider_menus.empty())
1750 {
1751 Q_FOREACH(QMenu* m, slider_menus){
1752 menu.addMenu(m);
1753 }
1754 menu.insertSeparator(nullptr);
1755 }
1756 if(has_stats)
1757 {
1758 QAction* actionStatistics =
1759 menu.addAction(tr("Statistics..."));
1760 actionStatistics->setObjectName("actionStatisticsOnPolyhedron");
1761 connect(actionStatistics, SIGNAL(triggered()),
1762 this, SLOT(statisticsOnItem()));
1763 }
1764 if(has_reload)
1765 {
1766 QAction* reload = menu.addAction(tr("&Reload Item from File"));
1767 reload->setProperty("is_groupable", true);
1768 connect(reload, SIGNAL(triggered()),
1769 this, SLOT(reloadItem()));
1770 }
1771 QAction* saveas = menu.addAction(tr("&Save as..."));
1772 connect(saveas, SIGNAL(triggered()),
1773 this, SLOT(on_actionSaveAs_triggered()));
1774 menu.addMenu(ui->menuOperations);
1775 menu.exec(sender->mapToGlobal(p));
1776 return;
1777 }
1778 }
1779 showSceneContextMenu(main_index, sender->mapToGlobal(p));
1780 return;
1781 }
1782
removeManipulatedFrame(CGAL::Three::Scene_item * item)1783 void MainWindow::removeManipulatedFrame(CGAL::Three::Scene_item* item)
1784 {
1785 if(item->manipulatable() &&
1786 item->manipulatedFrame() == viewer->manipulatedFrame()) {
1787 viewer->setManipulatedFrame(nullptr);
1788 }
1789 }
1790
updateInfo()1791 void MainWindow::updateInfo() {
1792 CGAL::Three::Scene_item* item = scene->item(getSelectedSceneItemIndex());
1793 if(item) {
1794 QString item_text = item->toolTip();
1795 QString item_filename = item->property("source filename").toString();
1796 CGAL::Bbox_3 bbox = item->bbox();
1797 if(bbox !=CGAL::Bbox_3())
1798 item_text += QString("<div>Bounding box: min (%1,%2,%3), max (%4,%5,%6), dimensions (%7, %8, %9)</div>")
1799 .arg(bbox.xmin(),0, 'g', 17)
1800 .arg(bbox.ymin(),0, 'g', 17)
1801 .arg(bbox.zmin(),0, 'g', 17)
1802 .arg(bbox.xmax(),0, 'g', 17)
1803 .arg(bbox.ymax(),0, 'g', 17)
1804 .arg(bbox.zmax(),0, 'g', 17)
1805 .arg(bbox.xmax() - bbox.xmin(), 0, 'g', 17)
1806 .arg(bbox.ymax() - bbox.ymin(), 0, 'g', 17)
1807 .arg(bbox.zmax() - bbox.zmin(), 0, 'g', 17);
1808 if(!item_filename.isEmpty()) {
1809 item_text += QString("<div>File:<i> %1</div>").arg(item_filename);
1810 }
1811 ui->infoLabel->setText(item_text);
1812 }
1813 else
1814 ui->infoLabel->clear();
1815 }
updateDisplayInfo()1816 void MainWindow::updateDisplayInfo() {
1817 CGAL::Three::Scene_item* item = scene->item(getSelectedSceneItemIndex());
1818 if(item)
1819 ui->displayLabel->setPixmap(item->graphicalToolTip());
1820 else
1821 ui->displayLabel->clear();
1822
1823 }
1824
readSettings()1825 void MainWindow::readSettings()
1826 {
1827 viewer->setAntiAliasing(settings.value("antialiasing", false).toBool());
1828 viewer->setFastDrawing(settings.value("quick_camera_mode", true).toBool());
1829 scene->enableVisibilityRecentering(settings.value("offset_update", false).toBool());
1830 viewer->textRenderer()->setMax(settings.value("max_text_items", 10000).toInt());
1831 int val = settings.value("transparency_pass_number", 4).toInt();
1832 if (val < 4 ) {
1833 val = 4;
1834 settings.setValue("transparency_pass_number", 4);
1835 }
1836 viewer->setTotalPass(val);
1837 CGAL::Three::Three::s_defaultSMRM = CGAL::Three::Three::modeFromName(
1838 settings.value("default_sm_rm", "flat+edges").toString());
1839 CGAL::Three::Three::s_defaultPSRM = CGAL::Three::Three::modeFromName(
1840 settings.value("default_ps_rm", "points").toString());
1841 // read plugin blacklist
1842 QStringList blacklist=settings.value("plugin_blacklist",QStringList()).toStringList();
1843 Q_FOREACH(QString name,blacklist){ plugin_blacklist.insert(name); }
1844 def_save_dir = settings.value("default_saveas_dir", QDir::homePath()).toString();
1845 this->default_point_size = settings.value("points_size").toInt();
1846 this->default_normal_length = settings.value("normals_length").toInt();
1847 this->default_lines_width = settings.value("lines_width").toInt();
1848 setProperty("ws_url", settings.value("ws_server_url").toString());
1849 }
1850
writeSettings()1851 void MainWindow::writeSettings()
1852 {
1853 this->writeState("MainWindow");
1854 {
1855 //setting plugin blacklist
1856 QStringList blacklist;
1857 Q_FOREACH(QString name,plugin_blacklist){ blacklist << name; }
1858 if ( !blacklist.isEmpty() ) settings.setValue("plugin_blacklist",blacklist);
1859 else settings.remove("plugin_blacklist");
1860 }
1861 std::cerr << "Write setting... done.\n";
1862 }
1863
quit()1864 void MainWindow::quit()
1865 {
1866 close();
1867 }
1868
closeEvent(QCloseEvent * event)1869 void MainWindow::closeEvent(QCloseEvent *event)
1870 {
1871 for(int i=0; i<plugins.size(); i++)
1872 {
1873 plugins[i].first->closure();
1874 }
1875 writeSettings();
1876 event->accept();
1877 }
1878
loadScript(QString filename)1879 bool MainWindow::loadScript(QString filename)
1880 {
1881 QFileInfo fileinfo(filename);
1882 boost::optional<bool> opt = wrap_a_call_to_cpp
1883 ([this, fileinfo] {
1884 return loadScript(fileinfo);
1885 }, this, __FILE__, __LINE__, CGAL::Three::PARENT_CONTEXT);
1886 if(!opt) return false;
1887 else return *opt;
1888 }
1889
loadScript(QFileInfo info)1890 bool MainWindow::loadScript(QFileInfo info)
1891 {
1892 #if defined(QT_SCRIPT_LIB)
1893 QString program;
1894 QString filename = info.absoluteFilePath();
1895 QFile script_file(filename);
1896 script_file.open(QIODevice::ReadOnly);
1897 if(!script_file.isReadable()) {
1898 throw std::ios_base::failure(script_file.errorString().toStdString());
1899 }
1900 program = script_file.readAll();
1901 if(!program.isEmpty())
1902 {
1903 QTextStream(stderr)
1904 << "Execution of script \""
1905 << filename << "\"\n";
1906 evaluate_script(program, filename);
1907 return true;
1908 }
1909 #endif
1910 return false;
1911 }
1912
throw_exception()1913 void MainWindow::throw_exception() {
1914 wrap_a_call_to_cpp([]() {
1915 throw std::runtime_error("Exception thrown in "
1916 "MainWindow::throw_exception()");
1917 }, this, __FILE__, __LINE__);
1918 }
1919
on_actionLoadScript_triggered()1920 void MainWindow::on_actionLoadScript_triggered()
1921 {
1922 #if defined(QT_SCRIPT_LIB)
1923
1924 #endif
1925 }
1926
on_actionLoad_triggered()1927 void MainWindow::on_actionLoad_triggered()
1928 {
1929 QStringList filters;
1930 // we need to special case our way out of this
1931 filters << "All Files (*)";
1932
1933
1934 typedef QMap<QString, CGAL::Three::Polyhedron_demo_io_plugin_interface*> FilterPluginMap;
1935 FilterPluginMap filterPluginMap;
1936
1937 for(CGAL::Three::Polyhedron_demo_io_plugin_interface* plugin : io_plugins) {
1938 QStringList split_filters = plugin->loadNameFilters().split(";;");
1939 Q_FOREACH(const QString& filter, split_filters) {
1940 FilterPluginMap::iterator it = filterPluginMap.find(filter);
1941 if(it != filterPluginMap.end()) {
1942 if(verbose)
1943 {
1944 qDebug() << "Duplicate Filter: " << it.value()->name();
1945 qDebug() << "This filter will not be available.";
1946 }
1947 } else {
1948 filterPluginMap[filter] = plugin;
1949 }
1950 filters << filter;
1951 }
1952 }
1953
1954 QString directory = settings.value("OFF open directory",
1955 QDir::current().dirName()).toString();
1956
1957 QFileDialog dialog(this);
1958 dialog.setDirectory(directory);
1959 dialog.setNameFilters(filters);
1960 dialog.setFileMode(QFileDialog::ExistingFiles);
1961
1962 if(dialog.exec() != QDialog::Accepted) { return; }
1963 for(auto v : CGAL::QGLViewer::QGLViewerPool())
1964 v->update();
1965 FilterPluginMap::iterator it =
1966 filterPluginMap.find(dialog.selectedNameFilter());
1967
1968 CGAL::Three::Polyhedron_demo_io_plugin_interface* selectedPlugin = nullptr;
1969
1970 if(it != filterPluginMap.end()) {
1971 selectedPlugin = it.value();
1972 }
1973
1974 std::size_t nb_files = dialog.selectedFiles().size();
1975 std::vector<QColor> colors_;
1976 colors_.reserve(nb_files);
1977 compute_color_map(QColor(100, 100, 255),//Scene_item's default color
1978 static_cast<unsigned>(nb_files),
1979 std::back_inserter(colors_));
1980 std::size_t nb_item = -1;
1981
1982 Q_FOREACH(const QString& filename, dialog.selectedFiles()) {
1983
1984 CGAL::Three::Scene_item* item = nullptr;
1985 if(selectedPlugin) {
1986 QFileInfo info(filename);
1987 bool ok;
1988 QList<Scene_item*> result = loadItem(info, selectedPlugin, ok);
1989 if(!ok)
1990 continue;
1991 for(Scene_item* item : result)
1992 {
1993 if(!item->property("already_colored").toBool())
1994 {
1995 ++nb_item;
1996 item->setColor(colors_[nb_item]);
1997 }
1998 selectSceneItem(scene->item_id(item));
1999 CGAL::Three::Scene_group_item* group =
2000 qobject_cast<CGAL::Three::Scene_group_item*>(item);
2001 if(group)
2002 scene->redraw_model();
2003 }
2004 this->addToRecentFiles(filename);
2005 } else {
2006 int scene_size = scene->numberOfEntries();
2007 open(filename);
2008 item = scene->item(scene->numberOfEntries()-1);
2009 if(scene->numberOfEntries() != scene_size
2010 && !item->property("already_colored").toBool())
2011 item->setColor(colors_[++nb_item]);
2012 }
2013 }
2014 }
2015
on_actionSaveAs_triggered()2016 void MainWindow::on_actionSaveAs_triggered()
2017 {
2018 QList<Scene_item*> to_save;
2019 for(Scene::Item_id id : scene->selectionIndices())
2020 {
2021 Scene_item* item = scene->item(id);
2022 to_save.append(item);
2023 }
2024 while(!to_save.empty())
2025 {
2026 Scene_item* item = to_save.front();
2027 QVector<CGAL::Three::Polyhedron_demo_io_plugin_interface*> canSavePlugins;
2028 QStringList filters;
2029 QString sf;
2030 for(CGAL::Three::Polyhedron_demo_io_plugin_interface* plugin : io_plugins) {
2031 if(plugin->canSave(item)) {
2032 canSavePlugins << plugin;
2033 filters += plugin->saveNameFilters();
2034 if(plugin->isDefaultLoader(item))
2035 sf = plugin->saveNameFilters().split(";;").first();
2036 }
2037 }
2038 QRegExp extensions("\\(\\*\\..+\\)");
2039 QStringList filter_exts;
2040 if(filters.empty())
2041 {
2042 QMessageBox::warning(this,
2043 tr("Cannot save"),
2044 tr("The selected object %1 cannot be saved.")
2045 .arg(item->name()));
2046 return;
2047 }
2048 Q_FOREACH(QString string, filters)
2049 {
2050 QStringList sl = string.split(";;");
2051 Q_FOREACH(QString s, sl){
2052 int pos = extensions.indexIn(s);
2053 if( pos >-1)
2054 filter_exts.append(extensions.capturedTexts());
2055 }
2056 }
2057 filters << tr("All files (*)");
2058 if(canSavePlugins.isEmpty()) {
2059 QMessageBox::warning(this,
2060 tr("Cannot save"),
2061 tr("The selected object %1 cannot be saved.")
2062 .arg(item->name()));
2063 continue;
2064 }
2065 QString caption = tr("Save %1 to File...").arg(item->name());
2066 QString dir = item->property("source filename").toString();
2067 if(dir.isEmpty() &&
2068 !item->property("defaultSaveDir").toString().isEmpty())
2069 {
2070 dir = item->property("defaultSaveDir").toString();
2071 }
2072 else if(!last_saved_dir.isEmpty() && dir.isEmpty())
2073 dir = QString("%1/%2").arg(last_saved_dir).arg(item->defaultSaveName());
2074 else if(dir.isEmpty())
2075 dir = QString("%1/%2").arg(def_save_dir).arg(item->name());
2076 QString filename =
2077 QFileDialog::getSaveFileName(this,
2078 caption,
2079 dir,
2080 filters.join(";;"),
2081 &sf);
2082
2083 if(filename.isEmpty())
2084 return;
2085 last_saved_dir = QFileInfo(filename).absoluteDir().path();
2086 extensions.indexIn(sf.split(";;").first());
2087 QString filter_ext, filename_ext;
2088 filter_ext = extensions.cap().split(" ").first();// in case of syntax like (*.a *.b)
2089
2090 filter_ext.remove(")");
2091 filter_ext.remove("(");
2092 //remove *
2093 filter_ext=filter_ext.right(filter_ext.size()-1);
2094
2095 QStringList filename_split = filename.split(".");
2096 filename_split.removeFirst();
2097 filename_ext = filename_split.join(".");
2098 filename_ext.push_front(".");
2099
2100 QStringList final_extensions;
2101 Q_FOREACH(QString string, filter_exts)
2102 {
2103 Q_FOREACH(QString s, string.split(" ")){// in case of syntax like (*.a *.b)
2104 s.remove(")");
2105 s.remove("(");
2106 //remove *
2107 s=s.right(s.size()-1);
2108 final_extensions.append(s);
2109 }
2110 }
2111 bool ok = false;
2112 while(!ok)
2113 {
2114 if(final_extensions.contains(filename_ext))
2115 {
2116 ok = true;
2117 }
2118 else{
2119 QStringList shatterd_filename_ext = filename_ext.split(".");
2120 if(!shatterd_filename_ext.last().isEmpty())
2121 {
2122 shatterd_filename_ext.removeFirst();//removes ""
2123 shatterd_filename_ext.removeFirst();
2124 filename_ext = shatterd_filename_ext.join(".");
2125 filename_ext.push_front(".");
2126 }
2127 else
2128 break;
2129 }
2130 }
2131 if(!ok)
2132 {
2133 filename = filename.append(filter_ext);
2134 }
2135 for(auto v : CGAL::QGLViewer::QGLViewerPool())
2136 v->update();
2137 save(filename, to_save);
2138 }
2139 }
2140
save(QString filename,QList<CGAL::Three::Scene_item * > & to_save)2141 void MainWindow::save(QString filename, QList<CGAL::Three::Scene_item*>& to_save) {
2142 QFileInfo fileinfo(filename);
2143 bool saved = false;
2144 for(CGAL::Three::Polyhedron_demo_io_plugin_interface* plugin : io_plugins) {
2145 if( plugin->canSave(to_save.front()) &&
2146 file_matches_filter(plugin->saveNameFilters(),filename.toLower()) )
2147 {
2148 if(plugin->save(fileinfo, to_save))
2149 {
2150 saved = true;
2151 break;
2152 }
2153 }
2154 }
2155 if(!saved)
2156 {
2157 QMessageBox::warning(this,
2158 tr("Cannot save"),
2159 tr("The selected object %1 was not saved. (Maybe a wrong extension ?)")
2160 .arg(to_save.front()->name()));
2161 to_save.pop_front();
2162 }
2163 }
2164
on_actionSaveSnapshot_triggered()2165 void MainWindow::on_actionSaveSnapshot_triggered()
2166 {
2167 viewer->saveSnapshot();
2168 }
2169
on_actionErase_triggered()2170 bool MainWindow::on_actionErase_triggered()
2171 {
2172 int next_index = scene->erase(scene->selectionIndices());
2173 //Secure the case where erase triggers other items deletions
2174 if(scene->numberOfEntries()< next_index +1 )
2175 next_index = -1;
2176 selectSceneItem(next_index);
2177 return next_index >= 0;
2178 }
2179
on_actionEraseAll_triggered()2180 void MainWindow::on_actionEraseAll_triggered()
2181 {
2182 QList<int> all_ids;
2183 for(int i = 0; i < scene->numberOfEntries(); ++i)
2184 all_ids.push_back(i);
2185 scene->setSelectedItemsList(all_ids);
2186 on_actionErase_triggered();
2187 }
2188
on_actionDuplicate_triggered()2189 void MainWindow::on_actionDuplicate_triggered()
2190 {
2191 int index = scene->duplicate(getSelectedSceneItemIndex());
2192 selectSceneItem(index);
2193 }
2194
on_actionShowHide_triggered()2195 void MainWindow::on_actionShowHide_triggered()
2196 {
2197 scene->setUpdatesEnabled(false);
2198 Q_FOREACH(QModelIndex index, sceneView->selectionModel()->selectedRows())
2199 {
2200 int i = scene->getIdFromModelIndex(proxyModel->mapToSource(index));
2201 CGAL::Three::Scene_item* item = scene->item(i);
2202 item->setVisible(!item->visible());
2203 item->redraw();
2204 }
2205 scene->setUpdatesEnabled(true);
2206 // updateViewersBboxes(false); //Not usable :when the scene changes scale, smaller items disappear.
2207 }
2208
on_actionSetPolyhedronA_triggered()2209 void MainWindow::on_actionSetPolyhedronA_triggered()
2210 {
2211 int i = getSelectedSceneItemIndex();
2212 scene->setItemA(i);
2213 }
2214
on_actionSetPolyhedronB_triggered()2215 void MainWindow::on_actionSetPolyhedronB_triggered()
2216 {
2217 int i = getSelectedSceneItemIndex();
2218 scene->setItemB(i);
2219 }
2220
on_actionPreferences_triggered()2221 void MainWindow::on_actionPreferences_triggered()
2222 {
2223 QDialog dialog(this);
2224 Ui::PreferencesDialog prefdiag;
2225 prefdiag.setupUi(&dialog);
2226 float lineWidth[2];
2227 if(!viewer->isOpenGL_4_3())
2228 viewer->glGetFloatv(GL_LINE_WIDTH_RANGE, lineWidth);
2229 else
2230 {
2231 lineWidth[0] = 0;
2232 lineWidth[1] = 10;
2233 }
2234 prefdiag.linesHorizontalSlider->setMinimum(lineWidth[0]);
2235 prefdiag.linesHorizontalSlider->setMaximum(lineWidth[1]);
2236
2237 prefdiag.offset_updateCheckBox->setChecked(
2238 settings.value("offset_update", false).toBool());
2239 connect(prefdiag.offset_updateCheckBox, SIGNAL(toggled(bool)),
2240 scene, SLOT(enableVisibilityRecentering(bool)));
2241
2242 prefdiag.antialiasingCheckBox->setChecked(settings.value("antialiasing", false).toBool());
2243 connect(prefdiag.antialiasingCheckBox, SIGNAL(toggled(bool)),
2244 viewer, SLOT(setAntiAliasing(bool)));
2245
2246 prefdiag.quick_cameraCheckBox->setChecked(
2247 settings.value("quick_camera_mode", true).toBool());
2248 connect(prefdiag.quick_cameraCheckBox, SIGNAL(toggled(bool)),
2249 viewer, SLOT(setFastDrawing(bool)));
2250 prefdiag.max_itemsSpinBox->setValue(viewer->textRenderer()->getMax_textItems());
2251
2252 connect(prefdiag.max_itemsSpinBox,static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
2253 this, [this](int i){
2254 setMaxTextItemsDisplayed(i);
2255 });
2256 prefdiag.transpSpinBox->setValue(viewer->total_pass());
2257 connect(prefdiag.transpSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
2258 this, [this](int i)
2259 {
2260 setTransparencyPasses(i);
2261 });
2262 prefdiag.pointsHorizontalSlider->setValue(this->default_point_size);
2263 connect(prefdiag.pointsHorizontalSlider, &QSlider::valueChanged,
2264 this, [this](int i)
2265 {
2266 this->default_point_size = i;
2267 });
2268 prefdiag.normalsHorizontalSlider->setValue(this->default_normal_length);
2269 connect(prefdiag.normalsHorizontalSlider, &QSlider::valueChanged,
2270 this, [this](int i)
2271 {
2272 this->default_normal_length = i;
2273 });
2274 prefdiag.linesHorizontalSlider->setValue(this->default_lines_width);
2275 connect(prefdiag.linesHorizontalSlider, &QSlider::valueChanged,
2276 this, [this](int i)
2277 {
2278 this->default_lines_width = i;
2279 });
2280 connect(prefdiag.background_colorPushButton, &QPushButton::clicked,
2281 this, &MainWindow::setBackgroundColor);
2282
2283 connect(prefdiag.default_save_asPushButton, &QPushButton::clicked,
2284 this, &MainWindow::setDefaultSaveDir);
2285
2286 connect(prefdiag.lightingPushButton, &QPushButton::clicked,
2287 this, &MainWindow::setLighting_triggered);
2288
2289 prefdiag.surface_meshComboBox->setCurrentText(CGAL::Three::Three::modeName(
2290 CGAL::Three::Three::s_defaultSMRM));
2291 connect(prefdiag.surface_meshComboBox, &QComboBox::currentTextChanged,
2292 this, [this](const QString& text){
2293 this->s_defaultSMRM = CGAL::Three::Three::modeFromName(text);
2294 });
2295
2296 prefdiag.point_setComboBox->setCurrentText(CGAL::Three::Three::modeName(
2297 CGAL::Three::Three::s_defaultPSRM));
2298 connect(prefdiag.point_setComboBox, &QComboBox::currentTextChanged,
2299 this, [this](const QString& text){
2300 this->s_defaultPSRM = CGAL::Three::Three::modeFromName(text);
2301 });
2302
2303 connect(prefdiag.backFrontColor_pushButton, &QPushButton::clicked,
2304 this, [](){
2305 qobject_cast<Viewer*>(CGAL::Three::Three::activeViewer())->setBackFrontColors();
2306 });
2307
2308 std::vector<QTreeWidgetItem*> items;
2309 QBrush successBrush(Qt::green),
2310 errorBrush(Qt::red),
2311 ignoredBrush(Qt::lightGray);
2312
2313 //add blacklisted plugins
2314 Q_FOREACH (QString name, PathNames_map.keys())
2315 {
2316 QTreeWidgetItem *item = new QTreeWidgetItem(prefdiag.treeWidget);
2317 item->setText(1, name);
2318 if(plugin_blacklist.contains(name)){
2319 item->setCheckState(0, Qt::Unchecked);
2320 }
2321 else{
2322 item->setCheckState(0, Qt::Checked);
2323 }
2324 if(pluginsStatus_map[name] == QString("success"))
2325 item->setBackground(1, successBrush);
2326 else if(ignored_map[name]){
2327 item->setBackground(1, ignoredBrush);
2328 }
2329 else{
2330 item->setBackground(1, errorBrush);
2331 }
2332 items.push_back(item);
2333 }
2334 connect(prefdiag.detailsPushButton, &QPushButton::clicked,
2335 this, [this, prefdiag](){
2336 QStringList titles;
2337 titles << "Name" << "Keywords" << "ConfigDate";
2338 QDialog dialog(this);
2339 Ui::DetailsDialog detdiag;
2340 detdiag.setupUi(&dialog);
2341 QTreeWidgetItem *header = new QTreeWidgetItem(titles);
2342 detdiag.treeWidget->setHeaderItem(header);
2343 Q_FOREACH(QTreeWidgetItem* plugin_item, prefdiag.treeWidget->selectedItems())
2344 {
2345 QString name = plugin_item->text(1);
2346 QString keywords = plugin_metadata_map[name].first.join(", ");
2347 QString date = plugin_metadata_map[name].second;
2348 QStringList values;
2349 values << name << keywords << date;
2350 new QTreeWidgetItem(detdiag.treeWidget, values);
2351 }
2352 for(int i=0; i<3; ++i)
2353 {
2354 detdiag.treeWidget->resizeColumnToContents(i);
2355 }
2356 connect(detdiag.treeWidget, &QTreeWidget::clicked,
2357 this, [this, detdiag](){
2358 if(detdiag.treeWidget->selectedItems().isEmpty())
2359 detdiag.textBrowser->setText("");
2360 else {
2361 QString name = detdiag.treeWidget->selectedItems().first()->text(0);
2362 QString status = pluginsStatus_map[name];
2363 QString path = PathNames_map[name];
2364 detdiag.textBrowser->setText(QString("Path: %1 \nStatus: %2").arg(path).arg(status));
2365 }
2366 });
2367 dialog.exec();
2368 });
2369 connect(prefdiag.sshButton, &QPushButton::clicked,
2370 this, [this](){
2371 QDialog dialog(this);
2372 Ui::SSHDialog sshdiag;
2373 sshdiag.setupUi(&dialog);
2374
2375 #ifdef CGAL_USE_SSH
2376 sshdiag.userBox->setEnabled(true);
2377 sshdiag.serverBox->setEnabled(true);
2378 sshdiag.pkBox->setEnabled(true);
2379 sshdiag.privkBox->setEnabled(true);
2380 sshdiag.userEdit->setText(settings.value("ssh_user", QString()).toString());
2381 sshdiag.serverEdit->setText(settings.value("ssh_server", QString()).toString());
2382 sshdiag.publicEdit->setText(settings.value("ssh_public_key", QString()).toString());
2383 sshdiag.privkEdit->setText(settings.value("ssh_priv_key", QString()).toString());
2384 connect(sshdiag.pubButton, &QPushButton::clicked,
2385 this, [this, sshdiag](){
2386 QFileDialog diag(this,
2387 "Public Key",
2388 "",
2389 "All Files (*)");
2390 diag.setFilter(QDir::Hidden|QDir::Files|QDir::Dirs|QDir::NoDotAndDotDot);
2391 if(!diag.exec())
2392 return;
2393 sshdiag.publicEdit->setText(diag.selectedFiles().front());
2394 });
2395 connect(sshdiag.privButton, &QPushButton::clicked,
2396 this, [this, sshdiag](){
2397 QFileDialog diag(this,
2398 "Private Key",
2399 "",
2400 "All Files (*)");
2401 diag.setFilter(QDir::Hidden|QDir::Files|QDir::Dirs|QDir::NoDotAndDotDot);
2402 if(!diag.exec())
2403 return;
2404 sshdiag.privkEdit->setText(diag.selectedFiles().front());
2405 });
2406 #else
2407 sshdiag.userBox->setEnabled(false);
2408 sshdiag.serverBox->setEnabled(false);
2409 sshdiag.pkBox->setEnabled(false);
2410 sshdiag.privkBox->setEnabled(false);
2411 #endif
2412 sshdiag.wsEdit->setText(settings.value("ws_server_url", QString()).toString());
2413
2414 dialog.exec();
2415 if ( dialog.result() )
2416 {
2417 #ifdef CGAL_USE_SSH
2418 settings.setValue("ssh_user",
2419 sshdiag.userEdit->text());
2420 settings.setValue("ssh_server",
2421 sshdiag.serverEdit->text());
2422 settings.setValue("ssh_public_key",
2423 sshdiag.publicEdit->text());
2424 settings.setValue("ssh_priv_key",
2425 sshdiag.privkEdit->text());
2426 #endif
2427 settings.setValue("ws_server_url",
2428 sshdiag.wsEdit->text());
2429 setProperty("ws_url", sshdiag.wsEdit->text());
2430 }
2431 });
2432
2433 dialog.exec();
2434
2435 if ( dialog.result() )
2436 {
2437 plugin_blacklist.clear();
2438
2439 for (std::size_t k=0; k<items.size(); ++k)
2440 {
2441 QTreeWidgetItem* item=items[k];
2442 if (item->checkState(0)==Qt::Unchecked)
2443 plugin_blacklist.insert(item->text(1));
2444 }
2445
2446 //write settings
2447 settings.setValue("antialiasing",
2448 prefdiag.antialiasingCheckBox->isChecked());
2449 settings.setValue("offset_update",
2450 prefdiag.offset_updateCheckBox->isChecked());
2451 settings.setValue("quick_camera_mode",
2452 prefdiag.quick_cameraCheckBox->isChecked());
2453 settings.setValue("transparency_pass_number",
2454 viewer->total_pass());
2455 settings.setValue("max_text_items",
2456 viewer->textRenderer()->getMax_textItems());
2457 settings.setValue("background_color",viewer->backgroundColor().name());
2458 settings.setValue("default_sm_rm", CGAL::Three::Three::modeName(
2459 CGAL::Three::Three::defaultSurfaceMeshRenderingMode()));
2460 settings.setValue("default_ps_rm", CGAL::Three::Three::modeName(
2461 CGAL::Three::Three::defaultPointSetRenderingMode()));
2462 settings.setValue("points_size", this->default_point_size);
2463 settings.setValue("normals_length", this->default_normal_length);
2464 settings.setValue("lines_width", this->default_lines_width);
2465
2466 }
2467 else
2468 {
2469 readSettings();
2470 }
2471 }
2472
setBackgroundColor()2473 void MainWindow::setBackgroundColor()
2474 {
2475 QColor c = QColorDialog::getColor();
2476 if(c.isValid()) {
2477 Q_FOREACH(CGAL::QGLViewer* v, CGAL::QGLViewer::QGLViewerPool())
2478 {
2479 if(v == nullptr)
2480 continue;
2481 v->setBackgroundColor(c);
2482 v->update();
2483 }
2484 }
2485 }
2486
setLighting_triggered()2487 void MainWindow::setLighting_triggered()
2488 {
2489 qobject_cast<Viewer*>(CGAL::Three::Three::activeViewer())->setLighting();
2490 }
2491
viewerShowObject()2492 void MainWindow::viewerShowObject()
2493 {
2494 Scene_item* item = nullptr;
2495 QAction* sender_action = qobject_cast<QAction*>(sender());
2496 if(sender_action && !sender_action->data().isNull()) {
2497 item = (Scene_item*)sender_action->data().value<void*>();
2498 }
2499 if(item) {
2500 const Scene::Bbox bbox = item->bbox();
2501 CGAL::qglviewer::Vec min((float)bbox.xmin()+viewer->offset().x, (float)bbox.ymin()+viewer->offset().y, (float)bbox.zmin()+viewer->offset().z),
2502 max((float)bbox.xmax()+viewer->offset().x, (float)bbox.ymax()+viewer->offset().y, (float)bbox.zmax()+viewer->offset().z);
2503 viewer->setSceneBoundingBox(min, max);
2504 viewerShow((float)min.x, (float)min.y, (float)min.z,
2505 (float)max.x, (float)max.y, (float)max.z);
2506 }
2507 }
2508 /* to check
2509 QString MainWindow::cameraString() const
2510 {
2511 const CGAL::qglviewer::Vec pos = viewer->camera()->position() - viewer->offset();
2512 const CGAL::qglviewer::Quaternion q = viewer->camera()->orientation();
2513
2514 return QString("%1 %2 %3 %4 %5 %6 %7")
2515 .arg(pos[0])
2516 .arg(pos[1])
2517 .arg(pos[2])
2518 .arg(q[0])
2519 .arg(q[1])
2520 .arg(q[2])
2521 .arg(q[3]);
2522 }*/
cameraString(CGAL::Three::Viewer_interface * v) const2523 QString MainWindow::cameraString(CGAL::Three::Viewer_interface* v) const
2524 {
2525 return v->dumpCameraCoordinates();
2526 }
2527
setAddKeyFrameKeyboardModifiers(::Qt::KeyboardModifiers m)2528 void MainWindow::setAddKeyFrameKeyboardModifiers(::Qt::KeyboardModifiers m)
2529 {
2530 viewer->setAddKeyFrameKeyboardModifiers(m);
2531 }
2532
recenterScene()2533 void MainWindow::recenterScene()
2534 {
2535 //force the recomputaion of the bbox
2536 bbox_need_update = true;
2537 CGAL::qglviewer::Vec min, max;
2538 computeViewerBBox(min, max);
2539 updateViewerBbox(static_cast<Viewer*>(activeViewer()), true, min, max);
2540 activeViewer()->showEntireScene();
2541 }
2542
on_actionLoadPlugin_triggered()2543 void MainWindow::on_actionLoadPlugin_triggered()
2544 {
2545 //pop a dialog of path selection, get the path and add it to plugins_directory
2546
2547 QString filters("Library files (*.dll *.DLL *.so *.a *.sl *.dylib *.bundle);;"
2548 "Any files (*)");
2549
2550 QStringList paths = QFileDialog::getOpenFileNames(
2551 this,
2552 tr("Select the directory containing your plugins:"),
2553 ".",filters);
2554 Q_FOREACH(QString name, paths)
2555 load_plugin(name, false);
2556
2557 updateMenus();
2558 }
2559
recurseExpand(QModelIndex index)2560 void MainWindow::recurseExpand(QModelIndex index)
2561 {
2562 int row = index.row();
2563 if(scene->index(0,0,index).isValid())
2564 {
2565 recurseExpand(scene->index(0,0,index));
2566 }
2567 CGAL::Three::Scene_group_item* group =
2568 qobject_cast<CGAL::Three::Scene_group_item*>(scene->item(scene->getIdFromModelIndex(index)));
2569 if(group && group->isExpanded())
2570 {
2571 sceneView->setExpanded(proxyModel->mapFromSource(index), true);
2572 }
2573 else if (group && !group->isExpanded()){
2574 sceneView->setExpanded(proxyModel->mapFromSource(index), false);
2575 }
2576
2577 if( index.sibling(row+1,0).isValid())
2578 recurseExpand(index.sibling(row+1,0));
2579 }
restoreCollapseState()2580 void MainWindow::restoreCollapseState()
2581 {
2582 QModelIndex modelIndex = scene->index(0,0,scene->invisibleRootItem()->index());
2583 if(modelIndex.isValid())
2584 recurseExpand(modelIndex);
2585 resetHeader();
2586 }
makeNewGroup()2587 void MainWindow::makeNewGroup()
2588 {
2589 Scene_group_item * group = new Scene_group_item();
2590 scene->addItem(group);
2591 for(Scene::Item_id id : scene->selectionIndices())
2592 {
2593 scene->changeGroup(scene->item(id), group);
2594 }
2595 }
2596
on_upButton_pressed()2597 void MainWindow::on_upButton_pressed()
2598 {
2599 scene->moveRowUp();
2600 }
2601
on_downButton_pressed()2602 void MainWindow::on_downButton_pressed()
2603 {
2604 scene->moveRowDown();
2605 }
2606
recenterSceneView(const QModelIndex & id)2607 void MainWindow::recenterSceneView(const QModelIndex &id)
2608 {
2609 if(id.isValid())
2610 {
2611 // mapFromSource is necessary to convert the QModelIndex received
2612 // from the Scene into a valid QModelIndex in the view, beacause of
2613 // the proxymodel
2614 sceneView->scrollTo(proxyModel->mapFromSource(id));
2615 }
2616 }
2617
statisticsOnItem()2618 void MainWindow::statisticsOnItem()
2619 {
2620 QApplication::setOverrideCursor(Qt::WaitCursor);
2621
2622 if (statistics_dlg == nullptr)
2623 {
2624 statistics_dlg = new QDialog(this);
2625 statistics_ui->setupUi(statistics_dlg);
2626 connect(statistics_ui->okButtonBox, SIGNAL(accepted()),
2627 statistics_dlg, SLOT(accept()));
2628 connect(statistics_ui->updateButton, SIGNAL(clicked()),
2629 this, SLOT(statisticsOnItem()));
2630 connect(statistics_ui->exportButton, &QPushButton::clicked,
2631 this, &MainWindow::exportStatistics);
2632 }
2633 statistics_ui->label_htmltab->setText(get_item_stats());
2634
2635 statistics_dlg->show();
2636 statistics_dlg->raise();
2637
2638 QApplication::restoreOverrideCursor();
2639 }
2640
2641 /* Creates a string containing an html table. This string is constructed by appending each parts of each row, so that the data can
2642 depend on the number of selected items. This String is then returned.*/
get_item_stats()2643 QString MainWindow::get_item_stats()
2644 {
2645 //1st step : get all classnames of the selected items
2646 QList<QString> classnames;
2647 Q_FOREACH(int id, scene->selectionIndices())
2648 {
2649 Scene_item* item = scene->item(id);
2650 QString classname = item->property("classname").toString();
2651 if(classname.isEmpty())
2652 classname = item->metaObject()->className();
2653 if(!classnames.contains(classname))
2654 classnames << classname;
2655 }
2656 //2nd step : separate the selection in lists corresponding to their classname
2657 QVector< QList<Scene_item*> > items;
2658 items.resize(classnames.size());
2659 Q_FOREACH(int id, scene->selectionIndices())
2660 {
2661 Scene_item* s_item = scene->item(id);
2662 for(int i=0; i<items.size(); i++)
2663 {
2664 Scene_item* item = scene->item(id);
2665 QString classname = item->property("classname").toString();
2666 if(classname.isEmpty())
2667 classname = item->metaObject()->className();
2668 if(classnames.at(i) == classname)
2669 {
2670 items[i] << s_item;
2671 break;
2672 }
2673 }
2674 }
2675 //last step :: making tables for each type of item
2676 QString str;
2677 for(int i=0; i< classnames.size(); i++)
2678 {
2679 CGAL::Three::Scene_item::Header_data data = items[i].at(0)->header();
2680 int title = 0;
2681 int titles_limit =0;
2682 if(data.titles.size()>0)
2683 {
2684 //1st row : item names
2685 str.append("<html> <table border=1>""<tr><td colspan = 2></td>");
2686 Q_FOREACH(Scene_item* sit, items[i])
2687 {
2688 str.append(QString("<td>%1</td>").arg(sit->name()));
2689 }
2690
2691
2692
2693 for(int j=0; j<data.categories.size(); j++)
2694 {
2695 str.append(QString("<tr><th rowspan=%1> %2 </th>")
2696 .arg(QString::number(data.categories[j].second))
2697 .arg(data.categories[j].first));
2698 titles_limit+=data.categories[j].second;
2699 str.append(QString("<td> %1 </td>").arg(data.titles.at(title)));
2700 Q_FOREACH(Scene_item* sit, items[i])
2701 {
2702 str.append(QString("<td>%1</td>").arg(sit->computeStats(title)));
2703 }
2704 title++;
2705 for(;title<titles_limit; title++)
2706 {
2707 str.append(QString("</tr><tr><td> %1 </td>").arg(data.titles.at(title)));
2708 Q_FOREACH(Scene_item* sit, items[i])
2709 {
2710 str.append(QString("<td>%1</td>").arg(sit->computeStats(title)));
2711 }
2712 }
2713
2714 str.append("</tr>");
2715 }
2716
2717 str.append(QString("</tr>""</table></html>"));
2718 }
2719 }
2720 return str;
2721 }
2722
setCollapsed(QModelIndex index)2723 void MainWindow::setCollapsed(QModelIndex index)
2724 {
2725 Q_EMIT collapsed(proxyModel->mapToSource(index));
2726 }
2727
setExpanded(QModelIndex index)2728 void MainWindow::setExpanded(QModelIndex index)
2729 {
2730 Q_EMIT expanded(proxyModel->mapToSource(index));
2731 }
2732
2733
setMaxTextItemsDisplayed(int val)2734 void MainWindow::setMaxTextItemsDisplayed(int val)
2735 {
2736 for(auto v : CGAL::QGLViewer::QGLViewerPool())
2737 qobject_cast<CGAL::Three::Viewer_interface*>(v)->textRenderer()->setMax(val);
2738 }
2739
resetHeader()2740 void MainWindow::resetHeader()
2741 {
2742 sceneView->header()->setStretchLastSection(false);
2743 scene->invisibleRootItem()->setColumnCount(5);
2744 sceneView->header()->setSectionResizeMode(Scene::NameColumn, QHeaderView::Stretch);
2745 sceneView->header()->setSectionResizeMode(Scene::ColorColumn, QHeaderView::Fixed);
2746 sceneView->header()->setSectionResizeMode(Scene::RenderingModeColumn, QHeaderView::ResizeToContents);
2747 sceneView->header()->setSectionResizeMode(Scene::ABColumn, QHeaderView::Fixed);
2748 sceneView->header()->setSectionResizeMode(Scene::VisibleColumn, QHeaderView::Fixed);
2749 #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
2750 sceneView->header()->resizeSection(Scene::ColorColumn, sceneView->header()->fontMetrics().horizontalAdvance("_#_"));
2751 sceneView->resizeColumnToContents(Scene::RenderingModeColumn);
2752 sceneView->header()->resizeSection(Scene::ABColumn, sceneView->header()->fontMetrics().horizontalAdvance(QString("_AB_")));
2753 sceneView->header()->resizeSection(Scene::VisibleColumn, sceneView->header()->fontMetrics().horizontalAdvance(QString("_View_")));
2754 #else
2755 sceneView->header()->resizeSection(Scene::ColorColumn, sceneView->header()->fontMetrics().width("_#_"));
2756 sceneView->resizeColumnToContents(Scene::RenderingModeColumn);
2757 sceneView->header()->resizeSection(Scene::ABColumn, sceneView->header()->fontMetrics().width(QString("_AB_")));
2758 sceneView->header()->resizeSection(Scene::VisibleColumn, sceneView->header()->fontMetrics().width(QString("_View_")));
2759 #endif
2760 }
2761
reset_default_loaders()2762 void MainWindow::reset_default_loaders()
2763 {
2764 default_plugin_selection.clear();
2765
2766 const char* prop_name = "Menu modified by MainWindow.";
2767 QMenu* menu = ui->menuFile;
2768 if(!menu)
2769 return;
2770 bool menuChanged = menu->property(prop_name).toBool();
2771 if(!menuChanged) {
2772 menu->setProperty(prop_name, true);
2773 }
2774 QList<QAction*> menuActions = menu->actions();
2775 menu->removeAction(actionResetDefaultLoaders);
2776 }
2777
insertActionBeforeLoadPlugin(QMenu * menu,QAction * actionToInsert)2778 void MainWindow::insertActionBeforeLoadPlugin(QMenu* menu, QAction* actionToInsert)
2779 {
2780 if(menu)
2781 {
2782 QList<QAction*> menuActions = menu->actions();
2783 if(!menuActions.contains(actionToInsert))
2784 menu->insertAction(ui->actionLoadPlugin, actionToInsert);
2785 }
2786 }
2787
colorItems()2788 void MainWindow::colorItems()
2789 {
2790 std::size_t nb_files = scene->selectionIndices().size();
2791 if(nb_files<2)
2792 return;
2793 std::vector<QColor> colors_;
2794 colors_.reserve(nb_files);
2795 compute_color_map(scene->item(scene->selectionIndices().last())->color(),
2796 static_cast<unsigned>(nb_files),
2797 std::back_inserter(colors_));
2798 std::size_t nb_item = -1;
2799 Q_FOREACH(int id, scene->selectionIndices())
2800 {
2801 scene->item(id)->setColor(colors_[++nb_item]);
2802 }
2803 for(auto v : CGAL::QGLViewer::QGLViewerPool())
2804 v->update();
2805 }
2806
2807
exportStatistics()2808 void MainWindow::exportStatistics()
2809 {
2810 std::vector<Scene_item*> items;
2811 Q_FOREACH(int id, getSelectedSceneItemIndices())
2812 {
2813 Scene_item* s_item = scene->item(id);
2814 items.push_back(s_item);
2815 }
2816
2817 QString str;
2818 Q_FOREACH(Scene_item* sit, items)
2819 {
2820 CGAL::Three::Scene_item::Header_data data = sit->header();
2821 if(data.titles.size()>0)
2822 {
2823 int titles_limit =0;
2824 int title = 0;
2825 str.append(QString("%1: \n").arg(sit->name()));
2826 for(int j=0; j<data.categories.size(); j++)
2827 {
2828 str.append(QString(" %1: \n")
2829 .arg(data.categories[j].first));
2830 titles_limit+=data.categories[j].second;
2831 for(;title<titles_limit; ++title)
2832 {
2833 str.append(QString(" %1: ").arg(data.titles.at(title)));
2834 str.append(QString("%1\n").arg(sit->computeStats(title)));
2835 }
2836 }
2837 }
2838 }
2839
2840 QString filename =
2841 QFileDialog::getSaveFileName((QWidget*)sender(),
2842 "",
2843 QString("Statistics.txt"),
2844 "Text Files (*.txt)");
2845 if(filename.isEmpty())
2846 return;
2847 QFile output(filename);
2848 output.open(QIODevice::WriteOnly | QIODevice::Text);
2849
2850 if(!output.isOpen()){
2851 qDebug() << "- Error, unable to open" << "outputFilename" << "for output";
2852 }
2853 QTextStream outStream(&output);
2854 outStream << str;
2855 output.close();
2856 }
2857
propagate_action()2858 void MainWindow::propagate_action()
2859 {
2860 QAction* sender = qobject_cast<QAction*>(this->sender());
2861 if(!sender) return;
2862 QString name = sender->text();
2863 Q_FOREACH(Scene::Item_id id, scene->selectionIndices())
2864 {
2865 Scene_item* item = scene->item(id);
2866 Q_FOREACH(QAction* action, item->contextMenu()->actions())
2867 {
2868 if(action->text() == name)
2869 {
2870 action->trigger();
2871 break;
2872 }
2873 }
2874 }
2875 }
2876
make_fullpath(const QString & filename,bool duplicate=false)2877 QString make_fullpath(const QString& filename, bool duplicate = false)
2878 {
2879 QString fullpath = QString("%1/%2").arg(QDir::tempPath()).arg(filename);
2880 QString tmp_fullpath = fullpath;
2881 if(duplicate)
2882 {
2883 int i=0;
2884 while(QFileInfo(tmp_fullpath).exists())
2885 {
2886 QString basename = QFileInfo(tmp_fullpath).baseName();
2887 QString dir = QFileInfo(tmp_fullpath).dir().path();
2888 QString suffix= QFileInfo(fullpath).completeSuffix();
2889 tmp_fullpath=QString("%1/%2%3.%4").arg(dir).arg(basename).arg(++i).arg(suffix);
2890 }
2891 }
2892 return tmp_fullpath;
2893 }
2894 /*
2895 The two following functions allow to create files from string and strings from files.
2896 This is used as a workaround of the absence of stream management in our I/O system.
2897 The whole to/from Base64 is used to avoid problems with binary formats. Everything is written
2898 as a base64 binary string, and converted back to what it was.
2899 */
file_to_string(const char * filename)2900 QByteArray file_to_string(const char* filename)
2901 {
2902 std::ifstream f(filename, std::ifstream::binary);
2903 // get size of file
2904 f.seekg (0,f.end);
2905 long size = f.tellg();
2906 f.seekg (0);
2907 std::ostringstream ss;
2908 // allocate memory for file content
2909 char* buffer = new char[size];
2910
2911 // read content of infile
2912 f.read(buffer,size);
2913
2914 // write to outfile
2915 ss.write(buffer,size);
2916 // release dynamically-allocated memory
2917 delete[] buffer;
2918 //ss.write( << f.rdbuf(); // reading data
2919 f.close();
2920 std::string st = ss.str();
2921 QByteArray ba(st.c_str(), static_cast<int>(st.size()));
2922 return ba;
2923 }
2924
write_string_to_file(const QString & str,const QString & filename)2925 QString MainWindow::write_string_to_file(const QString& str, const QString &filename)
2926 {
2927 QString fullpath = make_fullpath(filename);
2928 std::ofstream f(fullpath.toStdString().c_str(), std::ofstream::binary);
2929 QByteArray compressed_item(str.toStdString().c_str());
2930 QByteArray item = qUncompress(QByteArray::fromBase64(compressed_item));
2931 QByteArray bb = item;
2932 f.write(bb.constData(),bb.toStdString().size());
2933 f.close();
2934 return fullpath;
2935 }
2936
on_actionSa_ve_Scene_as_Script_triggered()2937 void MainWindow::on_actionSa_ve_Scene_as_Script_triggered()
2938 {
2939 if(scene->numberOfEntries() == 0)
2940 return;
2941 bool do_upload = false;
2942 #ifdef CGAL_USE_SSH
2943 QString user = settings.value("ssh_user", QString()).toString();
2944 if(!user.isEmpty())
2945 {
2946 QMessageBox::StandardButton doyou =
2947 QMessageBox::question(this, tr("Upload ?"), tr("Do you wish to upload the scene"
2948 " using the SSH preferences ?"));
2949 do_upload = (doyou == QMessageBox::Yes);
2950 }
2951 #endif
2952
2953 QString filename;
2954
2955 if(do_upload){
2956 filename = QString("%1/save_scene.js").arg(QDir::tempPath());
2957 }else{
2958 filename = QFileDialog::getSaveFileName(this,
2959 "Save the Scene as a Script File",
2960 last_saved_dir,
2961 "Qt Script files (*.js)");
2962 }
2963 if(!filename.endsWith(".js"))
2964 filename.append(".js");
2965 std::ofstream os(filename.toUtf8(), std::ofstream::binary);
2966 if(!os)
2967 return;
2968 QApplication::setOverrideCursor(Qt::WaitCursor);
2969 std::vector<std::pair<QString, QString> > names;
2970 std::vector<std::pair<QString, QString> > loaders;
2971 std::vector<QColor> colors;
2972 std::vector<int> rendering_modes;
2973 QStringList not_saved;
2974 Polyhedron_demo_io_plugin_interface* camera_plugin = nullptr;
2975 QMap<QString, QVector<QString> > group_children_map;
2976 for(int i = 0; i < scene->numberOfEntries(); ++i)
2977 {
2978 Scene_item* item = scene->item(i);
2979 QString loader;// = item->property("loader_name").toString();
2980 QString ext;
2981 for(Polyhedron_demo_io_plugin_interface* iop : io_plugins)
2982 {
2983 if(iop->name() == "camera_positions_plugin")
2984 camera_plugin = iop;
2985 if(iop->isDefaultLoader(item))
2986 {
2987 QString sf = iop->saveNameFilters().split(";;").first();
2988 //OFF Files (*.off)
2989 QRegularExpression re("\\(\\*\\.(.*)\\)");
2990 QRegularExpressionMatch rem = re.match(sf);
2991 if(!rem.hasMatch())
2992 continue;
2993 ext = rem.captured(1);
2994 //check if it is in a group
2995 if(item->parentGroup())
2996 {
2997 group_children_map[item->parentGroup()->name()].append(item->name());
2998 ;
2999 }
3000 QList<Scene_item*>to_save;
3001 to_save.append(item);
3002 QString savename(tr("%1.%2").arg(item->name()).arg(ext));
3003 QString fullpath = make_fullpath(savename, true);
3004 savename = QFileInfo(fullpath).fileName();
3005 iop->save(QFileInfo(fullpath), to_save);
3006 names.push_back(std::make_pair(savename, item->name()));
3007 loader=iop->name();
3008 break;
3009 }
3010 }
3011 if(loader.isEmpty())
3012 {
3013 QApplication::restoreOverrideCursor();
3014 QMessageBox::warning(this, "", tr("No plugin found for %1. Not saved.").arg(item->name()));
3015 QApplication::setOverrideCursor(Qt::WaitCursor);
3016 continue;
3017 }
3018
3019 loaders.push_back(std::make_pair(loader, ext));
3020 colors.push_back(item->color());
3021 rendering_modes.push_back(item->renderingMode());
3022 }
3023 bool has_camera_positions = false;
3024 if(camera_plugin)
3025 {
3026 QString fullpath = make_fullpath("camera_tmp.camera.txt");
3027 QList<Scene_item*> dummy;
3028 if(camera_plugin->save(QFileInfo(fullpath), dummy))
3029 {
3030 QByteArray item = file_to_string(fullpath.toStdString().c_str());
3031 os << "var camera_positions= [\'";
3032 os<<qCompress(item, 9).toBase64().toStdString().c_str();
3033 os << "\']\n" ;
3034 //delete temp file
3035 QFile tmp_file(fullpath);
3036 tmp_file.remove();
3037 has_camera_positions =true;
3038 }
3039 }
3040 if(loaders.empty())
3041 return;
3042 //path
3043 os << "var camera = \""<<viewer->dumpCameraCoordinates().toStdString()<<"\";\n";
3044 os << "var items = [";
3045 for(std::size_t i = 0; i< names.size() -1; ++i)
3046 {
3047 QString fullpath = make_fullpath(names[i].first);
3048
3049 QByteArray item = file_to_string(fullpath.toStdString().c_str());
3050 os<<"[\'";
3051 os<<qCompress(item, 9).toBase64().toStdString().c_str();
3052 os << "\', \'"<<names[i].second.toStdString().c_str()<<"\']," ;
3053 //delete temp file
3054 QFile tmp_file(fullpath);
3055 tmp_file.remove();
3056 }
3057 QString fullpath = make_fullpath(names.back().first);
3058 QByteArray item = file_to_string(fullpath.toStdString().c_str());
3059 os<<"[\'";
3060 os<<qCompress(item, 9).toBase64().toStdString().c_str();
3061 os << "\', \'"<<names.back().second.toStdString().c_str()<<"\']];\n";
3062 //delete temp file
3063 QFile tmp_file(fullpath);
3064 tmp_file.remove();
3065 //group relations
3066 if(!group_children_map.empty())
3067 {
3068 os << "var groups = [";
3069 for(int i = 0; i< group_children_map.size() -1; ++i)
3070 {
3071 QString group_name = group_children_map.keys()[i];
3072 os << "[\'" << group_name.toStdString().c_str()<<"\', [";
3073 for(int j = 0; j<group_children_map[group_name].size()-1; ++j)
3074 {
3075 os << group_children_map[group_name][j].toStdString().c_str()<<", ";
3076 }
3077 os << group_children_map[group_name].back().toStdString().c_str()<<"]],";
3078 }
3079 QString group_name = group_children_map.keys().back();
3080 os << "[\'" << group_name.toStdString().c_str()<<"\', [";
3081 for(int j = 0; j<group_children_map[group_name].size()-1; ++j)
3082 {
3083 os << "\'"<<group_children_map[group_name][j].toStdString().c_str()<<"\', ";
3084 }
3085 os << "\'"<<group_children_map[group_name].back().toStdString().c_str()<<"\']]];\n";
3086 }
3087 //plugin
3088 os << "var loaders = [";
3089 for(std::size_t i = 0; i< names.size() -1; ++i)
3090 {
3091 os << "[\'" << loaders[i].first.toStdString() << "\', \'"<<loaders[i].second.toStdString()<< "\'],";
3092 }
3093 os << "[\'" << loaders.back().first.toStdString() << "\', \'"<<loaders.back().second.toStdString()<< "\']];\n";
3094
3095 //color
3096 os << "var colors = [";
3097 for(std::size_t i = 0; i< names.size() -1; ++i)
3098 {
3099 os << "[" << colors[i].red() <<", "<< colors[i].green() <<", "<< colors[i].blue() <<"], ";
3100 }
3101 os<<"[" << colors.back().red() <<", "<< colors.back().green() <<", "<< colors.back().blue() <<"]];\n";
3102
3103 //rendering mode
3104 os << "var rendering_modes = [";
3105 for(std::size_t i = 0; i< names.size() -1; ++i)
3106 {
3107 os << rendering_modes[i] << ", ";
3108 }
3109 os << rendering_modes.back()<<"];\n";
3110 os << "function indexFromName(name) {\n";
3111 os << " for(var i = 0; i < items.length; i++){\n";
3112 os << " var itemName=items[i][1];\n";
3113 os << " if( itemName === name)\n";
3114 os << " {\n";
3115 os << " return i;\n";
3116 os << " }\n";
3117 os << " };\n";
3118 os << "}\n";
3119 os << "items.forEach(function(item, index, array){\n";
3120 os<<" var path=items[index][1];\n";
3121 os<<" path+='.';\n";
3122 os<<" path+=loaders[index][1];\n";
3123 os<<" var fullpath = main_window.write_string_to_file(item[0], path);\n";
3124 os<<" main_window.open(fullpath,loaders[index][0]);\n";
3125 os << " var it = scene.item(scene.numberOfEntries-1);\n";
3126 os << " var r = colors[index][0];\n";
3127 os << " var g = colors[index][1];\n";
3128 os << " var b = colors[index][2];\n";
3129 os << " it.setRgbColor(r,g,b);\n";
3130 os << " it.setRenderingMode(rendering_modes[index]);\n";
3131 os << "});\n";
3132 if(!group_children_map.empty())
3133 {
3134 os << "groups.forEach(function(group, index, array){\n";
3135 os << " main_window.selectSceneItem(-1);\n";
3136 os << " var group_name = group[0];\n";
3137 os << " var item_list = group[1];\n";
3138 os << " main_window.makeNewGroup();\n";
3139 os << " var it = scene.item(scene.numberOfEntries-1);\n";
3140 os << " it.setName(group_name);\n";
3141 os << " item_list.forEach(function(child, index, array){\n";
3142 os << " scene.changeGroup(scene.item(indexFromName(child)), it);\n";
3143 os << " });\n";
3144 os << "});\n";
3145 }
3146 os << "viewer.moveCameraToCoordinates(camera, 0.05);\n";
3147 if(has_camera_positions)
3148 {
3149 os<<" var path=\"cams.camera.txt\";\n";
3150 os<<" var fullpath = main_window.write_string_to_file(camera_positions, path);\n";
3151 os<<" main_window.open(fullpath,\'camera_positions_plugin\');\n";
3152 }
3153 os.close();
3154 if(!not_saved.empty()){
3155 QApplication::restoreOverrideCursor();
3156 QMessageBox::warning(this,
3157 "Items Not Saved",
3158 QString("The following items could not be saved: %1").arg(
3159 not_saved.join(", ")));
3160 QApplication::setOverrideCursor(Qt::WaitCursor);
3161 }
3162 #ifdef CGAL_USE_SSH
3163 using namespace CGAL::ssh_internal;
3164 if(do_upload)
3165 {
3166 QString server = settings.value("ssh_server", QString()).toString();
3167 QString pk = settings.value("ssh_public_key", QString()).toString();
3168 QString privK = settings.value("ssh_priv_key", QString()).toString();
3169 user = user.trimmed();
3170 server = server.trimmed();
3171 pk = pk.trimmed();
3172 privK=privK.trimmed();
3173 if(user.isEmpty()){
3174 return;
3175 }
3176 QString path;
3177 path = QInputDialog::getText(this,
3178 "",
3179 tr("Enter the name of your scene file."));
3180 if(path.isEmpty())
3181 return;
3182 if(!path.contains("Polyhedron_demo_"))
3183 path.prepend("Polyhedron_demo_");
3184 try{
3185 ssh_session session = nullptr;
3186 bool res = establish_ssh_session_from_agent(session,
3187 user.toStdString().c_str(),
3188 server.toStdString().c_str(),
3189 pk.toStdString().c_str());
3190
3191 if(!res)
3192 {
3193 bool ok;
3194 QString pass;
3195 pass = QInputDialog::getText(this, "SSH Password",
3196 "Enter ssh key password:",
3197 QLineEdit::Password,
3198 tr(""),
3199 &ok);
3200 if(!ok)
3201 {
3202 ssh_free(session);
3203 QApplication::restoreOverrideCursor();
3204 return;
3205 }
3206 pass = pass.trimmed();
3207 res = establish_ssh_session(session,
3208 user.toStdString().c_str(),
3209 server.toStdString().c_str(),
3210 pk.toStdString().c_str(),
3211 privK.toStdString().c_str(),
3212 pass.toStdString().c_str());
3213 }
3214
3215 if(!res)
3216 {
3217 QMessageBox::warning(this,
3218 "Error",
3219 "The SSH session could not be started.");
3220 ssh_free(session);
3221 QApplication::restoreOverrideCursor();
3222 return;
3223 }
3224 res = push_file(session,path.toStdString().c_str(), filename.toStdString().c_str());
3225 if(!res)
3226 {
3227 QApplication::restoreOverrideCursor();
3228 QMessageBox::warning(this,
3229 "Error",
3230 "The file could not be uploaded. Check your console for more information.");
3231 close_connection(session);
3232 ssh_free(session);
3233 return;
3234 }
3235 close_connection(session);
3236 ssh_free(session);
3237 QFile tmp_file(filename);
3238 tmp_file.remove();
3239 } catch( ssh::SshException e )
3240 {
3241 std::cout << "Error during connection : ";
3242 std::cout << e.getError() << std::endl;
3243 }
3244 }
3245 #endif
3246 QApplication::restoreOverrideCursor();
3247 }
setTransparencyPasses(int val)3248 void MainWindow::setTransparencyPasses(int val)
3249 {
3250 viewer->setTotalPass(val);
3251 viewer->update();
3252 }
3253
toggleFullScreen()3254 void MainWindow::toggleFullScreen()
3255 {
3256 QList<QDockWidget *> dockWidgets = findChildren<QDockWidget *>();
3257 if(visibleDockWidgets.isEmpty())
3258 {
3259 Q_FOREACH(QDockWidget * dock, dockWidgets)
3260 {
3261 if(dock->isVisible())
3262 {
3263 visibleDockWidgets.append(dock);
3264 dock->hide();
3265 }
3266 }
3267 }
3268 else
3269 {
3270 Q_FOREACH(QDockWidget * dock, visibleDockWidgets){
3271 dock->show();
3272 }
3273 visibleDockWidgets.clear();
3274
3275 }
3276 }
3277
setDefaultSaveDir()3278 void MainWindow::setDefaultSaveDir()
3279 {
3280 QString dirpath = QFileDialog::getExistingDirectory(this, "Set Default Save as Directory", def_save_dir);
3281 if(!dirpath.isEmpty())
3282 def_save_dir = dirpath;
3283 settings.setValue("default_saveas_dir", def_save_dir);
3284 }
3285
3286
setupViewer(Viewer * viewer,SubViewer * subviewer)3287 void MainWindow::setupViewer(Viewer* viewer, SubViewer* subviewer)
3288 {
3289 viewer->textRenderer()->setScene(scene);
3290 viewer->setScene(scene);
3291 connect(scene, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex & )),
3292 viewer, SLOT(update()));
3293 connect(scene, SIGNAL(updated()),
3294 viewer, SLOT(update()));
3295
3296 QAction* action = subviewer->findChild<QAction*>("actionRecenter");
3297 connect(action, SIGNAL(triggered()),
3298 viewer, SLOT(update()));
3299 connect(action, &QAction::triggered,
3300 subviewer, &SubViewer::recenter);
3301 action= subviewer->findChild<QAction*>("actionLookat");
3302 connect(action, SIGNAL(triggered()),
3303 subviewer, SLOT(lookat()));
3304 action= subviewer->findChild<QAction*>("actionColor");
3305 connect(action, &QAction::triggered,
3306 subviewer, &SubViewer::color);
3307 action= subviewer->findChild<QAction*>("actionDumpCamera");
3308 connect(action, &QAction::triggered,
3309 [this, viewer](){
3310 information(QString("Camera: %1")
3311 .arg(cameraString(viewer)));
3312 });
3313 action= subviewer->findChild<QAction*>("actionCopyCamera");
3314 connect(action, &QAction::triggered,
3315 [this, viewer](){
3316 qApp->clipboard()->setText(cameraString(viewer));
3317 });
3318 action= subviewer->findChild<QAction*>("actionPasteCamera");
3319 connect(action, &QAction::triggered,
3320 this, [viewer](){
3321 QString s = qApp->clipboard()->text();
3322 viewer->moveCameraToCoordinates(s, 0.5f);
3323 });
3324 action= subviewer->findChild<QAction*>("actionAntiAliasing");
3325 connect(action, SIGNAL(toggled(bool)),
3326 viewer, SLOT(setAntiAliasing(bool)));
3327 action= subviewer->findChild<QAction*>("actionDrawTwoSide");
3328 connect(action, SIGNAL(toggled(bool)),
3329 viewer, SLOT(setTwoSides(bool)));
3330 action= subviewer->findChild<QAction*>("actionQuick");
3331 connect(action, SIGNAL(toggled(bool)),
3332 viewer, SLOT(setFastDrawing(bool)));
3333 action= subviewer->findChild<QAction*>("actionOrtho");
3334 connect(action, SIGNAL(toggled(bool)),
3335 viewer, SLOT(SetOrthoProjection(bool)));
3336 action= subviewer->findChild<QAction*>("actionTotalPass");
3337 connect(action, &QAction::triggered,
3338 this, [this, viewer]() {
3339 bool ok;
3340 int nb = QInputDialog::getInt(this, "Set Maximum Number of Passes",
3341 "Enter number of transparency passes:",
3342 4, 4, 99, 1, &ok);
3343 if (!ok){
3344 return;
3345 }
3346 viewer->setTotalPass(nb);
3347 });
3348
3349 action = subviewer->findChild<QAction*>("actionScaleScene");
3350 action->setCheckable(true);
3351 action->setChecked(false);
3352 connect(action, &QAction::triggered,
3353 viewer, &Viewer::scaleScene);
3354
3355 action= subviewer->findChild<QAction*>("actionBackFrontShading");
3356 connect(action, SIGNAL(toggled(bool)),
3357 viewer, SLOT(setBackFrontShading(bool)));
3358 connect(viewer, SIGNAL(requestContextMenu(QPoint)),
3359 this, SLOT(contextMenuRequested(QPoint)));
3360 connect(viewer, SIGNAL(selected(int)),
3361 this, SLOT(selectSceneItem(int)));
3362 connect(viewer, SIGNAL(selectedPoint(double, double, double)),
3363 this, SLOT(showSelectedPoint(double, double, double)));
3364
3365 connect(viewer, SIGNAL(selectionRay(double, double, double,
3366 double, double, double)),
3367 scene, SIGNAL(selectionRay(double, double, double,
3368 double, double, double)));
3369
3370 connect(viewer, &Viewer::sendMessage,
3371 this, [](QString s){
3372 information(s);
3373 });
3374
3375 #ifdef CGAL_USE_WEBSOCKETS
3376 action= subviewer->viewer->findChild<QAction*>("actionShareCamera");
3377 connect(action, &QAction::toggled,
3378 this, [this, viewer](bool b)
3379 {
3380 if(!viewer){
3381 return;
3382 }
3383 QString session;
3384 if(b){
3385 bool ok;
3386 session = QInputDialog::getText(
3387 this,"Session",
3388 "Please enter the session name.\n"
3389 "Only the machines that enter the same session name will be connected.\n"
3390 "Several sessions can run simultaneously on a same server. ",
3391 QLineEdit::Normal, QString(), &ok);
3392 if(session.isEmpty() || !ok)
3393 {
3394 viewer->setShareCam(false, session);
3395 return;
3396 }
3397 }
3398 viewer->setShareCam(b, session);
3399 });
3400 #endif
3401
3402 }
3403
on_actionAdd_Viewer_triggered()3404 void MainWindow::on_actionAdd_Viewer_triggered()
3405 {
3406 SubViewer* subviewer = new SubViewer(ui->mdiArea, this, viewer);
3407 Viewer* viewer2 = subviewer->viewer;
3408 viewer2->setManipulatedFrame(viewer->manipulatedFrame());
3409 CGAL::qglviewer::Vec min, max;
3410 computeViewerBBox(min, max);
3411 updateViewerBbox(viewer2, true, min, max);
3412 viewer2->setObjectName("viewer2");
3413 connect(viewer2, SIGNAL(doneInitGL(CGAL::Three::Viewer_interface*)),
3414 scene, SLOT(newViewer(CGAL::Three::Viewer_interface*)));
3415 connect(viewer2, &Viewer::contextIsDestroyed,
3416 this, [this, viewer2](){
3417 scene->removeViewer(viewer2);
3418 viewerDestroyed(viewer2);
3419 });
3420
3421 setupViewer(viewer2, subviewer);
3422 viewer2->camera()->interpolateToFitScene();
3423 subviewer->show();
3424 ui->mdiArea->tileSubWindows();
3425 QPoint pos = viewer_window->pos();
3426 QSize size = viewer_window->size();
3427 viewer_window->move(subviewer->pos());
3428 viewer_window->resize(subviewer->size());
3429 subviewer->move(pos);
3430 subviewer->resize(size);
3431 newViewerCreated(viewer2);
3432 }
3433
recenterViewer()3434 void MainWindow::recenterViewer()
3435 {
3436 scene->computeBbox();
3437 CGAL::qglviewer::Vec min, max;
3438 computeViewerBBox(min, max);
3439 Viewer* target = qobject_cast<Viewer*>(childAt(cursor().pos()));
3440 if(target)
3441 {
3442 scene->computeBbox();
3443 updateViewerBbox(target, true, min, max);
3444 target->camera()->interpolateToFitScene();
3445 }
3446 }
3447
updateViewerBbox(Viewer * vi,bool recenter,CGAL::qglviewer::Vec min,CGAL::qglviewer::Vec max)3448 void MainWindow::updateViewerBbox(Viewer *vi, bool recenter,
3449 CGAL::qglviewer::Vec min,
3450 CGAL::qglviewer::Vec max){
3451 CGAL::qglviewer::Vec center = viewer->camera()->pivotPoint();
3452 vi->setSceneBoundingBox(min,
3453 max);
3454 if(recenter)
3455 {
3456 vi->resetFov();
3457 vi->camera()->showEntireScene();
3458 }
3459 else
3460 {
3461 vi->camera()->setPivotPoint(center);
3462 }
3463 }
3464
getDirectChild(QObject * widget)3465 QObject* MainWindow::getDirectChild(QObject* widget)
3466 {
3467
3468 if(!widget->property("helpText").toString().isEmpty())
3469 return widget;
3470 return getDirectChild(widget->parent());
3471 }
3472
on_action_Rearrange_Viewers_triggered()3473 void MainWindow::on_action_Rearrange_Viewers_triggered()
3474 {
3475 if(ui->mdiArea->subWindowList().size() == 1)
3476 ui->mdiArea->subWindowList().first()->showMaximized();
3477 else
3478 {
3479 ui->mdiArea->tileSubWindows();
3480 QMdiSubWindow* subviewer = qobject_cast<QMdiSubWindow*>(
3481 ui->mdiArea->childAt(ui->mdiArea->pos()));
3482 if(!subviewer)//should not happen but better safe than sorry
3483 {
3484 return;
3485 }
3486 QPoint pos = viewer_window->pos();
3487 QSize size = viewer_window->size();
3488 viewer_window->move(subviewer->pos());
3489 viewer_window->resize(subviewer->size());
3490 subviewer->move(pos);
3491 subviewer->resize(size);
3492 }
3493 }
3494
SubViewer(QWidget * parent,MainWindow * mw,Viewer * mainviewer)3495 SubViewer::SubViewer(QWidget *parent, MainWindow* mw, Viewer* mainviewer)
3496 :QMdiSubWindow (parent),
3497 mw(mw),
3498 viewMenu(new QMenu(this)),
3499 is_main(false)
3500 {
3501 if(mainviewer)
3502 viewer = new Viewer(this, mainviewer);
3503 else
3504 {
3505 viewer = new Viewer(this);
3506 is_main = true;
3507 }
3508 setWidget(viewer);
3509 QAction* actionRecenter = new QAction("Re¢er Scene",this);
3510 actionRecenter->setObjectName("actionRecenter");
3511 viewMenu->addAction(actionRecenter);
3512 QAction* actionLookat = new QAction("&Look at...",this);
3513 actionLookat->setObjectName("actionLookat");
3514 viewMenu->addAction(actionLookat);
3515 QAction* actionColor = new QAction("Change &Background Color...",this);
3516 actionColor->setObjectName("actionColor");
3517 viewMenu->addAction(actionColor);
3518 QAction* actionDumpCamera = new QAction("&Dump Camera Coordinates",this);
3519 actionDumpCamera->setObjectName("actionDumpCamera");
3520 QAction* actionCopyCamera = new QAction("&Copy Camera",this);
3521 actionCopyCamera->setObjectName("actionCopyCamera");
3522 QAction* actionPasteCamera = new QAction("&Paste Camera",this);
3523 actionPasteCamera->setObjectName("actionPasteCamera");
3524 QMenu* cameraMenu = new QMenu("Ca&mera", mw);
3525 cameraMenu->addAction(actionDumpCamera);
3526 cameraMenu->addAction(actionCopyCamera);
3527 cameraMenu->addAction(actionPasteCamera);
3528 viewMenu->addMenu(cameraMenu);
3529
3530 QAction* actionAntiAliasing = new QAction("&Antialiasing",this);
3531 actionAntiAliasing->setObjectName("actionAntiAliasing");
3532 actionAntiAliasing->setCheckable(true);
3533 actionAntiAliasing->setChecked(false);
3534 viewMenu->addAction(actionAntiAliasing);
3535 QAction* actionDrawTwoSide = new QAction("Draw &Two Sides",this);
3536 actionDrawTwoSide->setObjectName("actionDrawTwoSide");
3537 actionDrawTwoSide->setCheckable(true);
3538 actionDrawTwoSide->setChecked(false);
3539 viewMenu->addAction(actionDrawTwoSide);
3540 QAction* actionQuick = new QAction("&Quick Camera Mode",this);
3541 actionQuick->setObjectName("actionQuick");
3542 actionQuick->setCheckable(true);
3543 actionQuick->setChecked(true);
3544 viewMenu->addAction(actionQuick);
3545 QAction* actionOrtho = new QAction("&Orthographic Projection",this);
3546 actionOrtho->setObjectName("actionOrtho");
3547 actionOrtho->setCheckable(true);
3548 actionOrtho->setChecked(false);
3549 viewMenu->addAction(actionOrtho);
3550 QAction* actionTotalPass = new QAction("Set Transparency Pass &Number...",this);
3551 actionTotalPass->setObjectName("actionTotalPass");
3552 viewMenu->addAction(actionTotalPass);
3553 #ifdef CGAL_USE_WEBSOCKETS
3554 QAction* actionShareCamera= new QAction("Join &WS Server",viewer);
3555 actionShareCamera->setObjectName("actionShareCamera");
3556 actionShareCamera->setCheckable(true);
3557 actionShareCamera->setChecked(false);
3558 viewMenu->addAction(actionShareCamera);
3559 #endif
3560
3561 QAction* actionBackFrontShading = new QAction("Activate Back/Front shading.",this);
3562 actionBackFrontShading->setObjectName("actionBackFrontShading");
3563 actionBackFrontShading->setCheckable(true);
3564 actionBackFrontShading->setChecked(false);
3565 viewMenu->addAction(actionBackFrontShading);
3566
3567 QAction* actionScaleScene = new QAction("&Scale the Scene...",this);
3568 actionScaleScene->setObjectName("actionScaleScene");
3569 viewMenu->addAction(actionScaleScene);
3570
3571 if(mainviewer)
3572 setAttribute(Qt::WA_DeleteOnClose);
3573 setWindowIcon(QIcon(":/cgal/icons/resources/menu.png"));
3574 setSystemMenu(viewMenu);
3575 }
3576
~SubViewer()3577 SubViewer::~SubViewer()
3578 {
3579 viewer->deleteLater();
3580 }
3581
recenter()3582 void SubViewer::recenter()
3583 {
3584 CGAL::qglviewer::Vec min, max;
3585 mw->computeViewerBBox(min, max);
3586 mw->updateViewerBbox(viewer, true, min, max);
3587 viewer->camera()->interpolateToFitScene();
3588 }
3589
lookat()3590 void SubViewer::lookat()
3591 {
3592 Show_point_dialog dialog(mw);
3593 dialog.setWindowTitle(tr("Look at..."));
3594 int i = dialog.exec();
3595 if( i == QDialog::Accepted &&
3596 dialog.has_correct_coordinates() )
3597 {
3598 if (viewer->camera()->frame()->isSpinning())
3599 viewer->camera()->frame()->stopSpinning();
3600 mw->viewerShow(viewer,
3601 (float)dialog.get_x() + viewer->offset().x,
3602 (float)dialog.get_y() + viewer->offset().y,
3603 (float)dialog.get_z() + viewer->offset().z);
3604 }
3605 }
3606
color()3607 void SubViewer::color()
3608 {
3609 QColor c = QColorDialog::getColor();
3610 if(c.isValid()) {
3611 viewer->setBackgroundColor(c);
3612 viewer->update();
3613 }
3614 }
3615
closeEvent(QCloseEvent * closeEvent)3616 void SubViewer::closeEvent(QCloseEvent *closeEvent)
3617 {
3618
3619 if(is_main)
3620 {
3621 QMessageBox::information(mw, "", "This is the main viewer. It cannot be closed.");
3622 closeEvent->ignore();
3623 }
3624 else
3625 QWidget::closeEvent(closeEvent);
3626 }
changeEvent(QEvent * event)3627 void SubViewer::changeEvent(QEvent *event)
3628 {
3629 QMdiSubWindow::changeEvent(event);
3630 if(event->type() == QEvent::WindowStateChange)
3631 {
3632 if(isMaximized())
3633 {
3634 QMenu* menu = mw->findChild<QMenu*>("menuView");
3635 Q_FOREACH(QAction* action, viewMenu->actions())
3636 {
3637 menu->addAction(action);
3638 }
3639 setWindowFlags(
3640 Qt::SubWindow
3641 | Qt::CustomizeWindowHint
3642 | Qt::WindowMaximizeButtonHint
3643 //| Qt::WindowSystemMenuHint
3644 | Qt::WindowTitleHint
3645 );
3646 QAction* action = mw->findChild<QAction*>("action_Rearrange_Viewers");
3647 action->setVisible(false);
3648 viewer->update();
3649 }
3650 else
3651 {
3652 QMenu* menu = mw->findChild<QMenu*>("menuView");
3653 Q_FOREACH(QAction* action, viewMenu->actions())
3654 {
3655 menu->removeAction(action);
3656 }
3657 setWindowFlags(
3658 Qt::SubWindow
3659 | Qt::CustomizeWindowHint
3660 | Qt::WindowMaximizeButtonHint
3661 | Qt::WindowSystemMenuHint
3662 | Qt::WindowTitleHint
3663 );
3664 QAction* action = mw->findChild<QAction*>("action_Rearrange_Viewers");
3665 action->setVisible(true);
3666 for(auto v : CGAL::QGLViewer::QGLViewerPool())
3667 v->update();
3668 }
3669 }
3670 }
3671
invalidate_bbox(bool do_recenter)3672 void MainWindow::invalidate_bbox(bool do_recenter)
3673 {
3674 bbox_need_update = true;
3675 if(do_recenter)
3676 updateViewersBboxes(true);
3677 }
3678
on_action_Save_triggered()3679 void MainWindow::on_action_Save_triggered()
3680 {
3681 if(QMessageBox::question(this, "Save", "Are you sure you want to override these files ?")
3682 == QMessageBox::No)
3683 return;
3684 QList<Scene_item*> to_save;
3685
3686 for(Scene::Item_id id : scene->selectionIndices())
3687 {
3688 Scene_item* item = scene->item(id);
3689 if(!item->property("source filename").toString().isEmpty())
3690 {
3691 QString filename = item->property("source filename").toString();
3692 to_save.append(item);
3693 save(filename, to_save);
3694 }
3695 }
3696 }
3697
on_actionLoad_a_Scene_from_a_Script_File_triggered()3698 void MainWindow::on_actionLoad_a_Scene_from_a_Script_File_triggered()
3699 {
3700 bool do_download = false;
3701 QString filename;
3702 #ifdef CGAL_USE_SSH
3703 QString user = settings.value("ssh_user", QString()).toString();
3704
3705 if(!user.isEmpty())
3706 {
3707 QMessageBox::StandardButton doyou =
3708 QMessageBox::question(this, tr("Download ?"), tr("Do you wish to download the scene"
3709 " using the SSH preferences ?"));
3710 do_download= (doyou == QMessageBox::Yes);
3711 }
3712 #endif
3713
3714 if(do_download)
3715 {
3716 #ifdef CGAL_USE_SSH
3717 using namespace CGAL::ssh_internal;
3718 QString server = settings.value("ssh_server", QString()).toString();
3719 QString pk = settings.value("ssh_public_key", QString()).toString();
3720 QString privK = settings.value("ssh_priv_key", QString()).toString();
3721 user = user.trimmed();
3722 server = server.trimmed();
3723 pk = pk.trimmed();
3724 privK=privK.trimmed();
3725
3726 try{
3727 ssh_session session = nullptr;
3728 bool res = establish_ssh_session_from_agent(session,
3729 user.toStdString().c_str(),
3730 server.toStdString().c_str(),
3731 pk.toStdString().c_str());
3732 if(!res){
3733 bool ok;
3734 QString pass= QInputDialog::getText(this, "SSH Password",
3735 "Enter ssh key password:",
3736 QLineEdit::Password,
3737 tr(""),
3738 &ok);
3739 if(!ok)
3740 {
3741 ssh_free(session);
3742 return;
3743 }
3744 pass = pass.trimmed();
3745 res = establish_ssh_session(session,
3746 user.toStdString().c_str(),
3747 server.toStdString().c_str(),
3748 pk.toStdString().c_str(),
3749 privK.toStdString().c_str(),
3750 pass.toStdString().c_str());
3751 }
3752 if(!res)
3753 {
3754 QMessageBox::warning(this,
3755 "Error",
3756 "The SSH session could not be started.");
3757 ssh_free(session);
3758 return;
3759 }
3760 QStringList names;
3761 if(!CGAL::ssh_internal::explore_the_galaxy(session, names))
3762 {
3763 QMessageBox::warning(this,
3764 "Error",
3765 "Could not find remote directory.");
3766 }
3767 QString path;
3768 bool ok;
3769 path = QInputDialog::getItem(this,
3770 "Choose a file",
3771 tr("Choose the scene file."),
3772 names,0,true, &ok);
3773 filename = QString("%1/load_scene.js").arg(QDir::tempPath());
3774 if(path.isEmpty() || !ok)
3775 {
3776 ssh_free(session);
3777 return;
3778 }
3779 path.prepend("Polyhedron_demo_");
3780 path = tr("/tmp/%2").arg(path);
3781 res = pull_file(session,path.toStdString().c_str(), filename.toStdString().c_str());
3782 if(!res)
3783 {
3784 QMessageBox::warning(this,
3785 "Error",
3786 "The file could not be fetched. Check your console for more info.");
3787 close_connection(session);
3788 ssh_free(session);
3789 return;
3790 }
3791 close_connection(session);
3792 ssh_free(session);
3793 } catch( ssh::SshException e )
3794 {
3795 std::cout << "Error during connection : ";
3796 std::cout << e.getError() << std::endl;
3797 }
3798 #endif
3799 }
3800 else
3801 {
3802 filename = QFileDialog::getOpenFileName(
3803 this,
3804 tr("Select a Whole Scene file..."),
3805 ".",
3806 "Whole Scene files (*.js)");
3807 if(filename.isEmpty())
3808 return;
3809 }
3810 loadScript(QFileInfo(filename));
3811 if(do_download){
3812 QFile tmp_file(filename);
3813 tmp_file.remove();
3814 }
3815 }
3816