X-Git-Url: https://git.sur5r.net/?p=fstl;a=blobdiff_plain;f=src%2Fwindow.cpp;h=0954b33eb92f46b2951f7330c50221dcfc8917fe;hp=3002ecc83daa6008dd3c42c420b933c24605008e;hb=5dd213695da2eb22219ac5143aa8b5cfe7c89559;hpb=967d178c4343c689c728571bb10d5c94ab8d9d13 diff --git a/src/window.cpp b/src/window.cpp index 3002ecc..0954b33 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -6,20 +6,26 @@ #include "canvas.h" #include "loader.h" +const QString Window::RECENT_FILE_KEY = "recentFiles"; + Window::Window(QWidget *parent) : QMainWindow(parent), open_action(new QAction("Open", this)), about_action(new QAction("About", this)), - quit_action(new QAction("Quit", this)) + quit_action(new QAction("Quit", this)), + perspective_action(new QAction("Perspective", this)), + orthogonal_action(new QAction("Orthographic", this)), + reload_action(new QAction("Reload", this)), + autoreload_action(new QAction("Autoreload", this)), + recent_files(new QMenu("Open recent", this)), + recent_files_group(new QActionGroup(this)), + recent_files_clear_action(new QAction("Clear recent files", this)), + watcher(new QFileSystemWatcher(this)) { setWindowTitle("fstl"); setAcceptDrops(true); - QFile styleFile(":/qt/style.qss"); - styleFile.open( QFile::ReadOnly ); - setStyleSheet(styleFile.readAll()); - QGLFormat format; format.setVersion(2, 1); format.setSampleBuffers(true); @@ -27,6 +33,9 @@ Window::Window(QWidget *parent) : canvas = new Canvas(format, this); setCentralWidget(canvas); + QObject::connect(watcher, &QFileSystemWatcher::fileChanged, + this, &Window::on_watched_change); + open_action->setShortcut(QKeySequence::Open); QObject::connect(open_action, &QAction::triggered, this, &Window::on_open); @@ -35,13 +44,50 @@ Window::Window(QWidget *parent) : QObject::connect(quit_action, &QAction::triggered, this, &Window::close); + autoreload_action->setCheckable(true); + autoreload_action->setChecked(true); + autoreload_action->setEnabled(false); + QObject::connect(autoreload_action, &QAction::triggered, + this, &Window::on_autoreload_triggered); + + reload_action->setShortcut(QKeySequence::Refresh); + reload_action->setEnabled(false); + QObject::connect(reload_action, &QAction::triggered, + this, &Window::on_reload); + QObject::connect(about_action, &QAction::triggered, this, &Window::on_about); + QObject::connect(recent_files_clear_action, &QAction::triggered, + this, &Window::on_clear_recent); + QObject::connect(recent_files_group, &QActionGroup::triggered, + this, &Window::on_load_recent); + + rebuild_recent_files(); + auto file_menu = menuBar()->addMenu("File"); file_menu->addAction(open_action); + file_menu->addMenu(recent_files); + file_menu->addSeparator(); + file_menu->addAction(reload_action); + file_menu->addAction(autoreload_action); file_menu->addAction(quit_action); + auto view_menu = menuBar()->addMenu("View"); + auto projection_menu = view_menu->addMenu("Projection"); + projection_menu->addAction(perspective_action); + projection_menu->addAction(orthogonal_action); + auto projections = new QActionGroup(projection_menu); + for (auto p : {perspective_action, orthogonal_action}) + { + projections->addAction(p); + p->setCheckable(true); + } + perspective_action->setChecked(true); + projections->setExclusive(true); + QObject::connect(projections, &QActionGroup::triggered, + this, &Window::on_projection); + auto help_menu = menuBar()->addMenu("Help"); help_menu->addAction(about_action); @@ -65,25 +111,39 @@ void Window::on_about() "

A fast viewer for .stl files.
" "https://github.com/mkeeter/fstl

" - "

© 2014 Matthew Keeter
" + "

© 2014-2017 Matthew Keeter
" "matt.j.keeter@gmail.com

"); } -void Window::on_ascii_stl() +void Window::on_bad_stl() { QMessageBox::critical(this, "Error", "Error:
" - "Cannot open ASCII .stl file
" - "Please convert to binary .stl and retry"); + "This .stl file is invalid or corrupted.
" + "Please export it from the original source, verify, and retry."); } -void Window::on_bad_stl() +void Window::on_empty_mesh() { QMessageBox::critical(this, "Error", "Error:
" - "This .stl file is invalid or corrupted.
" - "Please export it from the original source, verify, and retry."); + "This file is syntactically correct
but contains no triangles."); +} + +void Window::on_confusing_stl() +{ + QMessageBox::warning(this, "Warning", + "Warning:
" + "This .stl file begins with solid but appears to be a binary file.
" + "fstl loaded it, but other programs may be confused by this file."); +} + +void Window::on_missing_file() +{ + QMessageBox::critical(this, "Error", + "Error:
" + "The target file is missing.
"); } void Window::enable_open() @@ -96,22 +156,126 @@ void Window::disable_open() open_action->setEnabled(false); } -bool Window::load_stl(const QString& filename) +void Window::set_watched(const QString& filename) +{ + const auto files = watcher->files(); + if (files.size()) + { + watcher->removePaths(watcher->files()); + } + watcher->addPath(filename); + + QSettings settings; + auto recent = settings.value(RECENT_FILE_KEY).toStringList(); + const auto f = QFileInfo(filename).absoluteFilePath(); + recent.removeAll(f); + recent.prepend(f); + while (recent.size() > MAX_RECENT_FILES) + { + recent.pop_back(); + } + settings.setValue(RECENT_FILE_KEY, recent); + rebuild_recent_files(); +} + +void Window::on_projection(QAction* proj) +{ + if (proj == perspective_action) + { + canvas->view_perspective(); + } + else + { + canvas->view_orthographic(); + } +} + +void Window::on_watched_change(const QString& filename) +{ + if (autoreload_action->isChecked()) + { + load_stl(filename, true); + } +} + +void Window::on_autoreload_triggered(bool b) +{ + if (b) + { + on_reload(); + } +} + +void Window::on_clear_recent() +{ + QSettings settings; + settings.setValue(RECENT_FILE_KEY, QStringList()); + rebuild_recent_files(); +} + +void Window::on_load_recent(QAction* a) +{ + load_stl(a->data().toString()); +} + +void Window::rebuild_recent_files() +{ + QSettings settings; + QStringList files = settings.value(RECENT_FILE_KEY).toStringList(); + + const auto actions = recent_files_group->actions(); + for (auto a : actions) + { + recent_files_group->removeAction(a); + } + recent_files->clear(); + + for (auto f : files) + { + const auto a = new QAction(f, recent_files); + a->setData(f); + recent_files_group->addAction(a); + recent_files->addAction(a); + } + if (files.size() == 0) + { + auto a = new QAction("No recent files", recent_files); + recent_files->addAction(a); + a->setEnabled(false); + } + recent_files->addSeparator(); + recent_files->addAction(recent_files_clear_action); +} + +void Window::on_reload() +{ + auto fs = watcher->files(); + if (fs.size() == 1) + { + load_stl(fs[0], true); + } +} + +bool Window::load_stl(const QString& filename, bool is_reload) { if (!open_action->isEnabled()) return false; canvas->set_status("Loading " + filename); - Loader* loader = new Loader(this, filename); + Loader* loader = new Loader(this, filename, is_reload); connect(loader, &Loader::started, this, &Window::disable_open); connect(loader, &Loader::got_mesh, canvas, &Canvas::load_mesh); - connect(loader, &Loader::error_ascii_stl, - this, &Window::on_ascii_stl); connect(loader, &Loader::error_bad_stl, this, &Window::on_bad_stl); + connect(loader, &Loader::error_empty_mesh, + this, &Window::on_empty_mesh); + connect(loader, &Loader::warning_confusing_stl, + this, &Window::on_confusing_stl); + connect(loader, &Loader::error_missing_file, + this, &Window::on_missing_file); connect(loader, &Loader::finished, loader, &Loader::deleteLater); @@ -124,6 +288,10 @@ bool Window::load_stl(const QString& filename) { connect(loader, &Loader::loaded_file, this, &Window::setWindowTitle); + connect(loader, &Loader::loaded_file, + this, &Window::set_watched); + autoreload_action->setEnabled(true); + reload_action->setEnabled(true); } loader->start();