diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 72ef7db66..5f8036aec 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -11,6 +11,8 @@ * The rugnux report header records the exact build: the git commit (stamped at build time, so it cannot go stale; `-dirty` marks uncommitted changes), the compiler flags, and the download page of the release it came from. * The rugnux report opens by stating who wrote the program and its terms of use: GPLv3, free for academic institutions and commercial companies alike. * The viewer's file manager lists CBF frames beside HDF5 datasets. +* The viewer's file manager starts at the beamline's raw data directory for an SLS account, and at the home directory for everyone else. +* The viewer's file manager expands and selects a dataset opened from the File menu or over D-Bus, when it lies inside the current root. * The viewer's image statistics state the collection time, detector distance, beam centre and wavelength as entries of their own rather than in a tooltip, and no longer report a B-factor. * The viewer plots spot count and background together on one plot, which is what a connection to a live source now shows by default. * The viewer's combined spots + background plot scales each quantity on its own axis - background on the left, spot count on the right, each axis coloured like its curve - and keeps the two curves in separate halves of the plot. diff --git a/viewer/JFJochViewerWindow.cpp b/viewer/JFJochViewerWindow.cpp index 1c5fb8861..7438bf97d 100644 --- a/viewer/JFJochViewerWindow.cpp +++ b/viewer/JFJochViewerWindow.cpp @@ -194,6 +194,8 @@ JFJochViewerWindow::JFJochViewerWindow(QWidget *parent, bool dbus, const QString connect(menuBar, &JFJochViewerMenu::fileOpenSelected, reading_worker, &JFJochImageReadingWorker::LoadFile); + connect(menuBar, &JFJochViewerMenu::fileOpenSelected, + this, &JFJochViewerWindow::FollowInFileBrowser); connect(menuBar, &JFJochViewerMenu::fileCloseSelected, reading_worker, &JFJochImageReadingWorker::CloseFile); @@ -665,6 +667,12 @@ void JFJochViewerWindow::closeEvent(QCloseEvent *event) { void JFJochViewerWindow::LoadFile(const QString &filename, qint64 image_number, qint64 summation, bool retry) { emit LoadFileRequest(filename, image_number, summation, true); + FollowInFileBrowser(filename); +} + +void JFJochViewerWindow::FollowInFileBrowser(const QString &filename) { + for (auto *browser : findChildren()) + browser->FollowPath(filename); } void JFJochViewerWindow::LoadImage(qint64 image_number, qint64 summation) { diff --git a/viewer/JFJochViewerWindow.h b/viewer/JFJochViewerWindow.h index 71dc4dac0..726952553 100644 --- a/viewer/JFJochViewerWindow.h +++ b/viewer/JFJochViewerWindow.h @@ -59,6 +59,10 @@ private: void ResetAllSettings(); + // Both ways of opening a dataset from outside the file manager - the File menu and D-Bus - + // come through here, so the browser follows the same way for either. + void FollowInFileBrowser(const QString &filename); + QThread *reading_thread; // Named layouts. Image = just the image + inspector; Processing = also the plots + jobs panel. diff --git a/viewer/widgets/JFJochViewerFileBrowser.cpp b/viewer/widgets/JFJochViewerFileBrowser.cpp index 8c9660159..eea5c61be 100644 --- a/viewer/widgets/JFJochViewerFileBrowser.cpp +++ b/viewer/widgets/JFJochViewerFileBrowser.cpp @@ -12,6 +12,8 @@ #include #include #include +#include +#include namespace { constexpr auto kDataRootEnvVar = "JUNGFRAUJOCH_DATA_ROOT"; @@ -34,11 +36,19 @@ namespace { }; } +QString JFJochViewerFileBrowser::SlsDataRoot(const QString &user) { + const auto match = QRegularExpression("\\Ae(\\d{5})\\z").match(user); + if (!match.hasMatch()) + return {}; + return "/sls/mx/data/p" + match.captured(1) + "/raw"; +} + QString JFJochViewerFileBrowser::DefaultRoot() { // Site default (e.g. a beamline launcher setting it to /sls/mx/data/p), then whatever // root was last browsed to (so re-launching without the variable set doesn't start over from - // scratch), then the user's home - never the current directory, which for an installed binary - // is typically the install location rather than anywhere the user would keep data. + // scratch), then - for an SLS beamline account - that proposal's raw data, then the user's home + // - never the current directory, which for an installed binary is typically the install + // location rather than anywhere the user would keep data. const QString env_root = qEnvironmentVariable(kDataRootEnvVar); if (!env_root.isEmpty() && QDir(env_root).exists()) return env_root; @@ -47,6 +57,12 @@ QString JFJochViewerFileBrowser::DefaultRoot() { if (!last_root.isEmpty() && QDir(last_root).exists()) return last_root; + // USER is the portable spelling Qt exposes; it is simply empty where the beamline directory + // could not exist anyway, so everyone else silently gets home, as before. + const QString sls_root = SlsDataRoot(qEnvironmentVariable("USER")); + if (!sls_root.isEmpty() && QDir(sls_root).exists()) + return sls_root; + return QDir::homePath(); } @@ -117,6 +133,23 @@ void JFJochViewerFileBrowser::ResetToDefaultRoot() { SetRoot(DefaultRoot()); } +void JFJochViewerFileBrowser::FollowPath(const QString &path) { + const QString absolute = QFileInfo(path).absoluteFilePath(); + // Containment by path components, not by string prefix, so /data/foo2 is not inside /data/foo. + // relativeFilePath hands back an absolute path when there is no relative route at all (a file on + // another Windows drive). + const QString relative = QDir(model_->rootPath()).relativeFilePath(absolute); + if (QDir::isAbsolutePath(relative) || relative == ".." || relative.startsWith("../")) + return; + + const QModelIndex index = proxy_->mapFromSource(model_->index(absolute)); + if (!index.isValid()) + return; + + tree_->setCurrentIndex(index); + tree_->scrollTo(index); +} + void JFJochViewerFileBrowser::onBrowseClicked() { const QString dir = QFileDialog::getExistingDirectory(this, "Select data root", root_edit_->text()); if (!dir.isEmpty()) diff --git a/viewer/widgets/JFJochViewerFileBrowser.h b/viewer/widgets/JFJochViewerFileBrowser.h index 90630b065..067bffd6f 100644 --- a/viewer/widgets/JFJochViewerFileBrowser.h +++ b/viewer/widgets/JFJochViewerFileBrowser.h @@ -28,6 +28,9 @@ class JFJochViewerFileBrowser : public QWidget { void SetRoot(const QString &path); [[nodiscard]] static QString DefaultRoot(); + // An SLS account eNNNNN browses its proposal group's raw data, which is pNNNNN with the + // same five digits; empty for any other user name, on any platform. + [[nodiscard]] static QString SlsDataRoot(const QString &user); public slots: // "Reset all settings to defaults": back to the root a fresh start would pick. @@ -44,4 +47,8 @@ signals: public: explicit JFJochViewerFileBrowser(QWidget *parent = nullptr); + + // A dataset opened elsewhere (menu, D-Bus) and lying inside the current root moves the tree's + // selection to it; one outside the root leaves the browser exactly as it was. + void FollowPath(const QString &path); };