aseprite/src/app/commands/cmd_open_file.cpp

281 lines
7.3 KiB
C++
Raw Normal View History

2015-02-12 23:16:25 +08:00
// Aseprite
// Copyright (C) 2019-2024 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
2015-02-12 23:16:25 +08:00
//
2016-08-27 04:02:58 +08:00
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/commands/cmd_open_file.h"
#include "app/app.h"
#include "app/commands/command.h"
#include "app/commands/params.h"
#include "app/console.h"
2018-07-07 22:54:44 +08:00
#include "app/doc.h"
#include "app/file/file.h"
#include "app/file_selector.h"
#include "app/i18n/strings.h"
#include "app/job.h"
#include "app/modules/gui.h"
#include "app/pref/preferences.h"
#include "app/recent_files.h"
#include "app/ui/status_bar.h"
#include "app/ui_context.h"
#include "base/fs.h"
#include "base/thread.h"
#include "doc/sprite.h"
#include "ui/ui.h"
#include <cstdio>
namespace app {
class OpenFileJob : public Job, public IFileOpProgress {
public:
OpenFileJob(FileOp* fop, const bool showProgress)
: Job(Strings::open_file_loading(), showProgress)
, m_fop(fop)
{
}
void showProgressWindow() {
startJob();
if (isCanceled())
2015-09-29 22:27:00 +08:00
m_fop->stop();
waitJob();
}
private:
// Thread to do the hard work: load the file from the disk.
virtual void onJob() override {
try {
2015-09-29 22:27:00 +08:00
m_fop->operate(this);
}
catch (const std::exception& e) {
2015-09-29 22:27:00 +08:00
m_fop->setError("Error loading file:\n%s", e.what());
}
2015-09-29 22:27:00 +08:00
if (m_fop->isStop() && m_fop->document())
delete m_fop->releaseDocument();
2015-09-29 22:27:00 +08:00
m_fop->done();
}
virtual void ackFileOpProgress(double progress) override {
jobProgress(progress);
}
FileOp* m_fop;
};
OpenFileCommand::OpenFileCommand()
: Command(CommandId::OpenFile(), CmdRecordableFlag)
, m_ui(true)
, m_repeatCheckbox(false)
, m_oneFrame(false)
, m_seqDecision(gen::SequenceDecision::ASK)
{
}
void OpenFileCommand::onLoadParams(const Params& params)
{
m_filename = params.get("filename");
m_folder = params.get("folder"); // Initial folder
if (params.has_param("ui"))
m_ui = params.get_as<bool>("ui");
else
m_ui = true;
m_repeatCheckbox = params.get_as<bool>("repeat_checkbox");
m_oneFrame = params.get_as<bool>("oneframe");
std::string sequence = params.get("sequence");
if (m_oneFrame ||
sequence == "skip" ||
sequence == "no") {
m_seqDecision = gen::SequenceDecision::NO;
}
else if (sequence == "agree" ||
sequence == "yes") {
m_seqDecision = gen::SequenceDecision::YES;
}
else {
m_seqDecision = gen::SequenceDecision::ASK;
}
}
void OpenFileCommand::onExecute(Context* context)
{
Console console;
m_usedFiles.clear();
base::paths filenames;
// interactive
#ifdef ENABLE_UI
if (context->isUIAvailable() && m_filename.empty()) {
base::paths exts = get_readable_extensions();
// Add backslash as show_file_selector() expected a filename as
// initial path (and the file part is removed from the path).
if (!m_folder.empty() && !base::is_path_separator(m_folder[m_folder.size()-1]))
m_folder.push_back(base::path_separator);
if (!app::show_file_selector(Strings::open_file_title(), m_folder, exts,
FileSelectorType::OpenMultiple,
filenames)) {
// The user cancelled the operation through UI
return;
}
// If the user selected several files, show the checkbox to repeat
// the action for future filenames in the batch of selected files
// to open.
if (filenames.size() > 1)
m_repeatCheckbox = true;
}
else
#endif // ENABLE_UI
if (!m_filename.empty()) {
filenames.push_back(m_filename);
}
if (filenames.empty())
return;
int flags =
FILE_LOAD_DATA_FILE |
FILE_LOAD_CREATE_PALETTE |
(m_repeatCheckbox ? FILE_LOAD_SEQUENCE_ASK_CHECKBOX: 0);
if (context->isUIAvailable() &&
m_seqDecision == gen::SequenceDecision::ASK) {
if (Preferences::instance().openFile.openSequence() == gen::SequenceDecision::ASK) {
// Do nothing (ask by default, or whatever the command params
// specified)
}
else {
m_seqDecision = Preferences::instance().openFile.openSequence();
}
}
switch (m_seqDecision) {
case gen::SequenceDecision::ASK:
flags |= FILE_LOAD_SEQUENCE_ASK;
break;
case gen::SequenceDecision::YES:
flags |= FILE_LOAD_SEQUENCE_YES;
break;
case gen::SequenceDecision::NO:
flags |= FILE_LOAD_SEQUENCE_NONE;
break;
}
if (m_oneFrame)
flags |= FILE_LOAD_ONE_FRAME;
std::string filename;
while (!filenames.empty()) {
filename = filenames[0];
filenames.erase(filenames.begin());
std::unique_ptr<FileOp> fop(
FileOp::createLoadDocumentOperation(
context, filename, flags));
bool unrecent = false;
// Do nothing (the user cancelled or something like that)
if (!fop)
return;
if (fop->hasError()) {
console.printf(fop->error().c_str());
unrecent = true;
}
else {
if (fop->isSequence()) {
if (fop->sequenceFlags() & FILE_LOAD_SEQUENCE_YES) {
m_seqDecision = gen::SequenceDecision::YES;
flags &= ~FILE_LOAD_SEQUENCE_ASK;
flags |= FILE_LOAD_SEQUENCE_YES;
}
else if (fop->sequenceFlags() & FILE_LOAD_SEQUENCE_NONE) {
m_seqDecision = gen::SequenceDecision::NO;
flags &= ~FILE_LOAD_SEQUENCE_ASK;
flags |= FILE_LOAD_SEQUENCE_NONE;
}
for (std::string fn : fop->filenames()) {
fn = base::normalize_path(fn);
m_usedFiles.push_back(fn);
auto it = std::find(filenames.begin(), filenames.end(), fn);
if (it != filenames.end())
filenames.erase(it);
}
}
else {
auto fn = base::normalize_path(fop->filename());
m_usedFiles.push_back(fn);
}
OpenFileJob task(fop.get(), m_ui);
task.showProgressWindow();
// Post-load processing, it is called from the GUI because may require user intervention.
fop->postLoad();
// Show any error
if (fop->hasError() && !fop->isStop())
console.printf(fop->error().c_str());
Doc* doc = fop->document();
if (doc) {
if (context->isUIAvailable()) {
App::instance()->recentFiles()->addRecentFile(fop->filename().c_str());
auto& docPref = Preferences::instance().document(doc);
if (fop->hasEmbeddedGridBounds() &&
!doc->sprite()->gridBounds().isEmpty()) {
// If the sprite contains the grid bounds inside, we put
// those grid bounds into the settings (e.g. useful to
// interact with old versions of Aseprite saving the grid
// bounds in the aseprite.ini file)
docPref.grid.bounds(doc->sprite()->gridBounds());
}
else {
// Get grid bounds from preferences
doc->sprite()->setGridBounds(docPref.grid.bounds());
}
}
doc->setContext(context);
}
else if (!fop->isStop())
unrecent = true;
}
// The file was not found or was loaded loaded with errors,
// so we can remove it from the recent-file list
if (unrecent) {
if (context->isUIAvailable())
App::instance()->recentFiles()->removeRecentFile(m_filename);
}
}
}
Command* CommandFactory::createOpenFileCommand()
{
return new OpenFileCommand;
}
} // namespace app