Skip to content

Commit

Permalink
[viewer] Add option to restore recent files
Browse files Browse the repository at this point in the history
  • Loading branch information
tobiashienzsch committed Oct 24, 2023
1 parent 1d4b60f commit d6ed3f3
Show file tree
Hide file tree
Showing 8 changed files with 232 additions and 109 deletions.
157 changes: 157 additions & 0 deletions tools/jml-viewer/Application/Application.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
#include "Application.hpp"

#include "Application/CommandLine.hpp"
#include "Application/MainComponent.hpp"
#include "Command/Snapshot.hpp"
#include "Command/Test.hpp"

#include <jml_tools/jml_tools.hpp>

namespace jml::viewer {

namespace {

auto createSettingsOptions() -> juce::PropertiesFile::Options
{
auto const name = getApplication().getApplicationName();

auto options = juce::PropertiesFile::Options{};
options.applicationName = "settings";
options.filenameSuffix = ".xml";
options.commonToAllUsers = false;
options.ignoreCaseOfKeyNames = false;
options.doNotSave = false;
options.millisecondsBeforeSaving = 3'000;
options.storageFormat = juce::PropertiesFile::StorageFormat::storeAsXML;
options.processLock = nullptr;
options.osxLibrarySubFolder = "Application Support";

#if JUCE_LINUX
options.folderName = ".config/neo-sonar/" + juce::String{name};
#else
options.folderName = "neo-sonar/" + juce::String{name};
#endif

DBG(options.getDefaultFile().getFullPathName());
return options;
}

auto runCommand(auto const func, auto const& cli) -> void
{
auto& app = *juce::JUCEApplication::getInstance();
app.setApplicationReturnValue(EXIT_SUCCESS);

if (auto const result = func(cli); result.failed()) {
std::cerr << result.getErrorMessage() << '\n';
app.setApplicationReturnValue(EXIT_FAILURE);
}

app.quit();
}

} // namespace

Application::Application() { _settings.setStorageParameters(createSettingsOptions()); }

auto Application::getSettings() -> juce::ApplicationProperties& { return _settings; }

auto Application::getApplicationName() -> juce::String const { return JUCE_APPLICATION_NAME_STRING; }

auto Application::getApplicationVersion() -> juce::String const
{
return JUCE_APPLICATION_VERSION_STRING;
}

auto Application::moreThanOneInstanceAllowed() -> bool { return true; }

auto Application::initialise(juce::String const& commandLine) -> void
{
auto const [cli, shouldExit] = jml::viewer::parseCommandLine(commandLine);
if (cli == nullptr or shouldExit) {
setApplicationReturnValue(cli == nullptr ? EXIT_FAILURE : EXIT_SUCCESS);
quit();
return;
}

if (cli->app.got_subcommand("test")) {
return runCommand(jml::viewer::runTestScript, *cli);
}
if (cli->app.got_subcommand("snapshot")) {
return runCommand(jml::viewer::runSnapshotScript, *cli);
}

juce::LookAndFeel::setDefaultLookAndFeel(&_lnf);
_mainWindow = std::make_unique<MainWindow>(getApplicationName());
}

auto Application::shutdown() -> void
{
_mainWindow = nullptr;
juce::LookAndFeel::setDefaultLookAndFeel(nullptr);
}

auto Application::systemRequestedQuit() -> void { quit(); }

auto Application::anotherInstanceStarted(juce::String const& commandLine) -> void
{
juce::ignoreUnused(commandLine);
}

Application::MainWindow::MainWindow(juce::String const& name)
: DocumentWindow(
name,
juce::Desktop::getInstance().getDefaultLookAndFeel().findColour(
ResizableWindow::backgroundColourId
),
DocumentWindow::allButtons
)
{
setUsingNativeTitleBar(true);
setContentOwned(std::make_unique<jml::viewer::MainComponent>().release(), true);

#if JUCE_IOS || JUCE_ANDROID
setFullScreen(true);
#else
setResizable(true, true);
centreWithSize(getWidth(), getHeight());
#endif

setVisible(true);
}

auto Application::MainWindow::closeButtonPressed() -> void
{
JUCEApplication::getInstance()->systemRequestedQuit();
}

auto getApplication() -> Application&
{
auto* app = dynamic_cast<Application*>(juce::JUCEApplication::getInstance());
jassert(app != nullptr);
return *app;
}

auto getApplicationSettings() -> juce::PropertiesFile&
{
auto* settings = getApplication().getSettings().getUserSettings();
jassert(settings != nullptr);
return *settings;
}

auto getRecentOpenFiles() -> juce::StringArray
{
auto& settings = getApplicationSettings();
auto const property = settings.getValue("recent_open_files", "");
return juce::StringArray::fromTokens(property, ";", "");
}

auto appendToRecentOpenFiles(juce::File const& file) -> void
{
auto fileList = getRecentOpenFiles();
fileList.addIfNotAlreadyThere(file.getFullPathName());
getApplicationSettings().setValue("recent_open_files", fileList.joinIntoString(";"));
}

auto clearRecentOpenFiles() -> void { getApplicationSettings().setValue("recent_open_files", ""); }

} // namespace jml::viewer
46 changes: 46 additions & 0 deletions tools/jml-viewer/Application/Application.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#pragma once

#include <jml_tools/jml_tools.hpp>
#include <juce_gui_basics/juce_gui_basics.h>

namespace jml::viewer {

struct Application final : juce::JUCEApplication
{
Application();
~Application() override = default;

auto getSettings() -> juce::ApplicationProperties&;

auto getApplicationName() -> juce::String const override;
auto getApplicationVersion() -> juce::String const override;

auto initialise(juce::String const& commandLine) -> void override;
auto shutdown() -> void override;
auto systemRequestedQuit() -> void override;

auto moreThanOneInstanceAllowed() -> bool override;
auto anotherInstanceStarted(juce::String const& commandLine) -> void override;

struct MainWindow : juce::DocumentWindow
{
explicit MainWindow(juce::String const& name);
~MainWindow() override = default;

auto closeButtonPressed() -> void override;
};

private:
juce::ApplicationProperties _settings;
jml::LookAndFeel _lnf;
std::unique_ptr<MainWindow> _mainWindow;
};

[[nodiscard]] auto getApplication() -> Application&;
[[nodiscard]] auto getApplicationSettings() -> juce::PropertiesFile&;

[[nodiscard]] auto getRecentOpenFiles() -> juce::StringArray;
auto appendToRecentOpenFiles(juce::File const& file) -> void;
auto clearRecentOpenFiles() -> void;

} // namespace jml::viewer
111 changes: 2 additions & 109 deletions tools/jml-viewer/Application/Main.cpp
Original file line number Diff line number Diff line change
@@ -1,110 +1,3 @@
#include "Application/CommandLine.hpp"
#include "Application/MainComponent.hpp"
#include "Command/Snapshot.hpp"
#include "Command/Test.hpp"
#include "Application/Application.hpp"

#include <jml_tools/jml_tools.hpp>

namespace {
auto runCommand(auto const func, auto const& cli) -> void
{
auto& app = *juce::JUCEApplication::getInstance();
app.setApplicationReturnValue(EXIT_SUCCESS);

if (auto const result = func(cli); result.failed()) {
std::cerr << result.getErrorMessage() << '\n';
app.setApplicationReturnValue(EXIT_FAILURE);
}

app.quit();
}
} // namespace

struct GuiAppApplication final : juce::JUCEApplication
{

GuiAppApplication() = default;

auto getApplicationName() -> juce::String const override { return JUCE_APPLICATION_NAME_STRING; }

auto getApplicationVersion() -> juce::String const override
{
return JUCE_APPLICATION_VERSION_STRING;
}

auto moreThanOneInstanceAllowed() -> bool override { return true; }

auto initialise(juce::String const& commandLine) -> void override
{
auto const [cli, shouldExit] = jml::viewer::parseCommandLine(commandLine);
if (cli == nullptr or shouldExit) {
setApplicationReturnValue(cli == nullptr ? EXIT_FAILURE : EXIT_SUCCESS);
quit();
return;
}

if (cli->app.got_subcommand("test")) {
return runCommand(jml::viewer::runTestScript, *cli);
}
if (cli->app.got_subcommand("snapshot")) {
return runCommand(jml::viewer::runSnapshotScript, *cli);
}

juce::LookAndFeel::setDefaultLookAndFeel(&_lnf);
_mainWindow = std::make_unique<MainWindow>(getApplicationName());
}

auto shutdown() -> void override
{
_mainWindow = nullptr;
juce::LookAndFeel::setDefaultLookAndFeel(nullptr);
}

auto systemRequestedQuit() -> void override { quit(); }

auto anotherInstanceStarted(juce::String const& commandLine) -> void override
{
juce::ignoreUnused(commandLine);
}

struct MainWindow : juce::DocumentWindow
{

explicit MainWindow(juce::String const& name)
: DocumentWindow(
name,
juce::Desktop::getInstance().getDefaultLookAndFeel().findColour(
ResizableWindow::backgroundColourId
),
DocumentWindow::allButtons
)
{
setUsingNativeTitleBar(true);
setContentOwned(std::make_unique<jml::viewer::MainComponent>().release(), true);

#if JUCE_IOS || JUCE_ANDROID
setFullScreen(true);
#else
setResizable(true, true);
centreWithSize(getWidth(), getHeight());
#endif

setVisible(true);
}

auto closeButtonPressed() -> void override
{
JUCEApplication::getInstance()->systemRequestedQuit();
}

private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MainWindow) // NOLINT
};

private:
jml::LookAndFeel _lnf;
std::unique_ptr<MainWindow> _mainWindow;
};

// This macro generates the main() routine that launches the app.
START_JUCE_APPLICATION(GuiAppApplication) // NOLINT
START_JUCE_APPLICATION(jml::viewer::Application) // NOLINT
5 changes: 5 additions & 0 deletions tools/jml-viewer/Application/MainComponent.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "MainComponent.hpp"

#include "Application/Application.hpp"
#include "Application/CommandIDs.hpp"

#include <jml_tools/jml_tools.hpp>
Expand All @@ -8,6 +9,8 @@ namespace jml::viewer {

MainComponent::MainComponent()
{
_menuBar.onFileLoad = [this](auto const& file) { _documents.openScript(file); };

_commandManager.registerAllCommandsForTarget(this);
addKeyListener(_commandManager.getKeyMappings());
setWantsKeyboardFocus(true);
Expand Down Expand Up @@ -142,6 +145,7 @@ auto MainComponent::perform(juce::ApplicationCommandTarget::InvocationInfo const
case CommandIDs::save:
case CommandIDs::saveAs: /*saveProject();*/ break;
case CommandIDs::settings: showSettingsWindow(); break;
case CommandIDs::quit: getApplication().quit(); break;
case CommandIDs::undo: _undoManager.undo(); break;
case CommandIDs::redo: _undoManager.redo(); break;
case CommandIDs::about: showAboutWindow(); break;
Expand Down Expand Up @@ -197,6 +201,7 @@ auto MainComponent::loadScriptPath() -> void
_fileChooser->launchAsync(mode, [this](auto const& chooser) {
if (auto const result = chooser.getResult(); result.existsAsFile()) {
_documents.openScript(result);
appendToRecentOpenFiles(result);
}
});
}
Expand Down
14 changes: 14 additions & 0 deletions tools/jml-viewer/Application/MenuBar.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "MenuBar.hpp"

#include "Application/Application.hpp"
#include "Application/CommandIDs.hpp"

#include <jml_tools/jml_tools.hpp>
Expand Down Expand Up @@ -31,9 +32,22 @@ auto MenuBar::getMenuForIndex(int menuIndex, juce::String const& /*menuName*/) -
auto const index = static_cast<MenuIndex>(menuIndex);

if (index == MenuIndex::File) {
auto files = juce::PopupMenu{};
for (auto const& filePath : getRecentOpenFiles()) {
auto const file = juce::File{filePath};
files.addItem(file.getFileNameWithoutExtension(), [this, file] {
if (onFileLoad) {
onFileLoad(file);
}
});
}
files.addSeparator();
files.addItem("Clear", [] { clearRecentOpenFiles(); });

auto menu = juce::PopupMenu{};
menu.addCommandItem(cmd, IDs::open, "Open", getIcon("launch_black_48dp_svg"));
menu.addCommandItem(cmd, IDs::reload, "Reload", getIcon("autorenew_black_48dp_svg"));
menu.addSubMenu("Recent Files", files, true, getIcon("restore_black_48dp_svg"));
menu.addSeparator();
menu.addCommandItem(cmd, IDs::save, "Save", getIcon("save_black_48dp_svg"));
menu.addCommandItem(cmd, IDs::saveAs, "Save As", getIcon("save_as_black_48dp_svg"));
Expand Down
2 changes: 2 additions & 0 deletions tools/jml-viewer/Application/MenuBar.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ struct MenuBar final
auto getMenuForIndex(int menuIndex, juce::String const& /*menuName*/) -> juce::PopupMenu override;
auto menuItemSelected(int /*menuItemID*/, int /*topLevelMenuIndex*/) -> void override;

std::function<void(juce::File const&)> onFileLoad;

private:
enum struct MenuIndex
{
Expand Down
3 changes: 3 additions & 0 deletions tools/jml-viewer/Asset/Icon/restore_black_48dp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit d6ed3f3

Please sign in to comment.