2015-02-12 23:16:25 +08:00
|
|
|
// Aseprite
|
2024-05-10 01:26:29 +08:00
|
|
|
// Copyright (C) 2019-2024 Igara Studio S.A.
|
2018-02-21 21:39:30 +08:00
|
|
|
// 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.
|
2012-01-06 06:45:03 +08:00
|
|
|
|
2013-08-06 08:20:19 +08:00
|
|
|
#ifdef HAVE_CONFIG_H
|
2012-01-06 06:45:03 +08:00
|
|
|
#include "config.h"
|
2013-08-06 08:20:19 +08:00
|
|
|
#endif
|
|
|
|
|
2016-11-15 06:44:29 +08:00
|
|
|
#include "app/commands/cmd_open_file.h"
|
|
|
|
|
2013-08-06 08:20:19 +08:00
|
|
|
#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"
|
2013-08-06 08:20:19 +08:00
|
|
|
#include "app/file/file.h"
|
2012-06-16 10:37:59 +08:00
|
|
|
#include "app/file_selector.h"
|
2022-01-07 17:02:38 +08:00
|
|
|
#include "app/i18n/strings.h"
|
2013-08-06 08:20:19 +08:00
|
|
|
#include "app/modules/gui.h"
|
2019-10-11 02:08:59 +08:00
|
|
|
#include "app/pref/preferences.h"
|
2013-08-06 08:20:19 +08:00
|
|
|
#include "app/recent_files.h"
|
|
|
|
#include "app/ui/status_bar.h"
|
|
|
|
#include "app/ui_context.h"
|
2024-09-11 05:05:08 +08:00
|
|
|
#include "app/util/open_file_job.h"
|
2016-11-02 06:14:05 +08:00
|
|
|
#include "base/fs.h"
|
2012-01-06 06:45:03 +08:00
|
|
|
#include "base/thread.h"
|
2014-10-21 09:21:31 +08:00
|
|
|
#include "doc/sprite.h"
|
2013-08-06 08:20:19 +08:00
|
|
|
#include "ui/ui.h"
|
2012-01-06 06:45:03 +08:00
|
|
|
|
2013-10-15 06:58:11 +08:00
|
|
|
#include <cstdio>
|
2012-07-06 12:06:00 +08:00
|
|
|
|
2013-08-06 08:20:19 +08:00
|
|
|
namespace app {
|
2012-01-06 06:45:03 +08:00
|
|
|
|
|
|
|
OpenFileCommand::OpenFileCommand()
|
2025-07-24 07:00:12 +08:00
|
|
|
: Command(CommandId::OpenFile())
|
2024-05-10 01:26:29 +08:00
|
|
|
, m_ui(true)
|
2016-11-16 05:11:47 +08:00
|
|
|
, m_repeatCheckbox(false)
|
2016-12-01 23:06:35 +08:00
|
|
|
, m_oneFrame(false)
|
2021-04-13 03:57:02 +08:00
|
|
|
, m_seqDecision(gen::SequenceDecision::ASK)
|
2012-01-06 06:45:03 +08:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2015-03-12 02:40:22 +08:00
|
|
|
void OpenFileCommand::onLoadParams(const Params& params)
|
2012-01-06 06:45:03 +08:00
|
|
|
{
|
2015-03-12 02:40:22 +08:00
|
|
|
m_filename = params.get("filename");
|
|
|
|
m_folder = params.get("folder"); // Initial folder
|
2024-05-10 01:26:29 +08:00
|
|
|
|
|
|
|
if (params.has_param("ui"))
|
|
|
|
m_ui = params.get_as<bool>("ui");
|
|
|
|
else
|
|
|
|
m_ui = true;
|
|
|
|
|
2019-03-22 22:47:45 +08:00
|
|
|
m_repeatCheckbox = params.get_as<bool>("repeat_checkbox");
|
|
|
|
m_oneFrame = params.get_as<bool>("oneframe");
|
2016-12-01 22:37:45 +08:00
|
|
|
|
|
|
|
std::string sequence = params.get("sequence");
|
2021-04-13 03:57:02 +08:00
|
|
|
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;
|
|
|
|
}
|
2012-01-06 06:45:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void OpenFileCommand::onExecute(Context* context)
|
|
|
|
{
|
|
|
|
Console console;
|
|
|
|
|
2016-11-15 06:44:29 +08:00
|
|
|
m_usedFiles.clear();
|
|
|
|
|
2018-02-21 21:39:30 +08:00
|
|
|
base::paths filenames;
|
2017-04-08 11:06:25 +08:00
|
|
|
|
2012-01-06 06:45:03 +08:00
|
|
|
// interactive
|
2015-05-19 04:04:31 +08:00
|
|
|
if (context->isUIAvailable() && m_filename.empty()) {
|
2018-02-21 21:39:30 +08:00
|
|
|
base::paths exts = get_readable_extensions();
|
2015-03-02 22:18:33 +08:00
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
2022-01-07 17:02:38 +08:00
|
|
|
if (!app::show_file_selector(Strings::open_file_title(),
|
|
|
|
m_folder,
|
|
|
|
exts,
|
2017-04-08 11:06:25 +08:00
|
|
|
FileSelectorType::OpenMultiple,
|
|
|
|
filenames)) {
|
|
|
|
// The user cancelled the operation through UI
|
|
|
|
return;
|
|
|
|
}
|
2020-07-31 03:27:23 +08:00
|
|
|
|
|
|
|
// 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;
|
2017-04-08 11:06:25 +08:00
|
|
|
}
|
2024-09-03 10:12:13 +08:00
|
|
|
else if (!m_filename.empty()) {
|
2017-04-08 11:06:25 +08:00
|
|
|
filenames.push_back(m_filename);
|
2012-01-06 06:45:03 +08:00
|
|
|
}
|
|
|
|
|
2017-04-08 11:06:25 +08:00
|
|
|
if (filenames.empty())
|
2016-12-01 23:06:35 +08:00
|
|
|
return;
|
|
|
|
|
2019-03-26 09:09:22 +08:00
|
|
|
int flags = FILE_LOAD_DATA_FILE | FILE_LOAD_CREATE_PALETTE |
|
2017-03-07 06:27:43 +08:00
|
|
|
(m_repeatCheckbox ? FILE_LOAD_SEQUENCE_ASK_CHECKBOX : 0);
|
2016-12-01 23:06:35 +08:00
|
|
|
|
2021-04-13 03:57:02 +08:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-01 23:06:35 +08:00
|
|
|
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;
|
2016-11-16 05:11:47 +08:00
|
|
|
|
2020-07-31 03:27:23 +08:00
|
|
|
std::string filename;
|
|
|
|
while (!filenames.empty()) {
|
|
|
|
filename = filenames[0];
|
|
|
|
filenames.erase(filenames.begin());
|
|
|
|
|
2017-04-08 11:06:25 +08:00
|
|
|
std::unique_ptr<FileOp> fop(FileOp::createLoadDocumentOperation(context, filename, flags));
|
|
|
|
bool unrecent = false;
|
2012-01-06 06:45:03 +08:00
|
|
|
|
2017-04-08 11:06:25 +08:00
|
|
|
// Do nothing (the user cancelled or something like that)
|
|
|
|
if (!fop)
|
|
|
|
return;
|
2016-12-01 23:06:35 +08:00
|
|
|
|
2017-04-08 11:06:25 +08:00
|
|
|
if (fop->hasError()) {
|
|
|
|
console.printf(fop->error().c_str());
|
|
|
|
unrecent = true;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (fop->isSequence()) {
|
|
|
|
if (fop->sequenceFlags() & FILE_LOAD_SEQUENCE_YES) {
|
2021-04-13 03:57:02 +08:00
|
|
|
m_seqDecision = gen::SequenceDecision::YES;
|
2020-07-31 03:27:23 +08:00
|
|
|
flags &= ~FILE_LOAD_SEQUENCE_ASK;
|
|
|
|
flags |= FILE_LOAD_SEQUENCE_YES;
|
2017-04-08 11:06:25 +08:00
|
|
|
}
|
|
|
|
else if (fop->sequenceFlags() & FILE_LOAD_SEQUENCE_NONE) {
|
2021-04-13 03:57:02 +08:00
|
|
|
m_seqDecision = gen::SequenceDecision::NO;
|
2020-07-31 03:27:23 +08:00
|
|
|
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);
|
2017-04-08 11:06:25 +08:00
|
|
|
}
|
2012-01-06 06:45:03 +08:00
|
|
|
}
|
2017-04-08 11:06:25 +08:00
|
|
|
else {
|
2020-07-31 03:27:23 +08:00
|
|
|
auto fn = base::normalize_path(fop->filename());
|
|
|
|
m_usedFiles.push_back(fn);
|
2012-01-06 06:45:03 +08:00
|
|
|
}
|
|
|
|
|
2024-05-10 01:26:29 +08:00
|
|
|
OpenFileJob task(fop.get(), m_ui);
|
2017-04-08 11:06:25 +08:00
|
|
|
task.showProgressWindow();
|
2016-12-01 23:06:35 +08:00
|
|
|
|
2017-04-08 11:06:25 +08:00
|
|
|
// Post-load processing, it is called from the GUI because may require user intervention.
|
|
|
|
fop->postLoad();
|
2016-12-01 23:06:35 +08:00
|
|
|
|
2017-04-08 11:06:25 +08:00
|
|
|
// Show any error
|
|
|
|
if (fop->hasError() && !fop->isStop())
|
|
|
|
console.printf(fop->error().c_str());
|
2016-12-01 23:06:35 +08:00
|
|
|
|
2019-10-11 02:08:59 +08:00
|
|
|
Doc* doc = fop->document();
|
|
|
|
if (doc) {
|
|
|
|
if (context->isUIAvailable()) {
|
2017-04-08 11:06:25 +08:00
|
|
|
App::instance()->recentFiles()->addRecentFile(fop->filename().c_str());
|
2019-10-11 02:08:59 +08:00
|
|
|
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());
|
|
|
|
}
|
|
|
|
}
|
2016-12-01 23:06:35 +08:00
|
|
|
|
2019-10-11 02:08:59 +08:00
|
|
|
doc->setContext(context);
|
2017-04-08 11:06:25 +08:00
|
|
|
}
|
|
|
|
else if (!fop->isStop())
|
|
|
|
unrecent = true;
|
2016-12-01 23:06:35 +08:00
|
|
|
}
|
|
|
|
|
2017-04-08 11:06:25 +08:00
|
|
|
// 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())
|
2018-03-14 23:08:02 +08:00
|
|
|
App::instance()->recentFiles()->removeRecentFile(m_filename);
|
2017-04-08 11:06:25 +08:00
|
|
|
}
|
2012-01-06 06:45:03 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-21 23:36:33 +08:00
|
|
|
std::string OpenFileCommand::onGetFriendlyName() const
|
|
|
|
{
|
|
|
|
// TO DO: would be better to show the last part of the path
|
|
|
|
// via text size hint instead of a fixed number of chars.
|
|
|
|
auto uiScale = Preferences::instance().general.uiScale();
|
|
|
|
auto scScale = Preferences::instance().general.screenScale();
|
|
|
|
int pos(68.0 / double(uiScale) / double(scScale));
|
|
|
|
return Command::onGetFriendlyName().append(
|
|
|
|
(m_filename.empty() ?
|
|
|
|
"" :
|
|
|
|
(": " + (m_filename.size() >= pos ? m_filename.substr(m_filename.size() - pos, pos) :
|
|
|
|
m_filename))));
|
|
|
|
}
|
|
|
|
|
2012-01-06 06:45:03 +08:00
|
|
|
Command* CommandFactory::createOpenFileCommand()
|
|
|
|
{
|
|
|
|
return new OpenFileCommand;
|
|
|
|
}
|
2013-08-06 08:20:19 +08:00
|
|
|
|
|
|
|
} // namespace app
|