Improve handling of image sequences when dropping files or opening multiple files

Now we can repeat the same action (Agree/Skip the all sequence of
images) for all the dropped files/selected files in the "Open File"
dialog (as in the CLI new behavior).
This commit is contained in:
David Capello 2020-07-30 16:27:23 -03:00
parent 1255b17738
commit 8c0f81cbba
5 changed files with 104 additions and 36 deletions

View File

@ -617,40 +617,20 @@ bool CliProcessor::openFile(Context* ctx, CliOpenFile& cof)
m_delegate->beforeOpenFile(cof);
Doc* oldDoc = ctx->activeDocument();
auto openCommand = static_cast<OpenFileCommand*>(Commands::instance()->byId(CommandId::OpenFile()));
Params params;
params.set("filename", cof.filename.c_str());
if (cof.oneFrame)
params.set("oneframe", "true");
else {
switch (m_lastDecision) {
case OpenFileCommand::SequenceDecision::Ask:
params.set("sequence", "ask");
params.set("repeat_checkbox", "true");
break;
case OpenFileCommand::SequenceDecision::Skip:
params.set("sequence", "skip");
break;
case OpenFileCommand::SequenceDecision::Agree:
params.set("sequence", "agree");
break;
}
}
ctx->executeCommand(openCommand, params);
m_batch.open(ctx,
cof.filename,
cof.oneFrame);
// Mark used file names as "already processed" so we don't try to
// open then again
for (const auto& usedFn : openCommand->usedFiles()) {
for (const auto& usedFn : m_batch.usedFiles()) {
auto fn = base::normalize_path(usedFn);
m_usedFiles.insert(fn);
os::instance()->markCliFileAsProcessed(fn);
}
// Future decision for other files in the CLI
if (openCommand->seqDecision() != OpenFileCommand::SequenceDecision::Ask)
m_lastDecision = openCommand->seqDecision();
Doc* doc = ctx->activeDocument();
// If the active document is equal to the previous one, it
// means that we couldn't open this specific document.

View File

@ -11,8 +11,8 @@
#include "app/cli/cli_delegate.h"
#include "app/cli/cli_open_file.h"
#include "app/commands/cmd_open_file.h"
#include "app/doc_exporter.h"
#include "app/util/open_batch.h"
#include "doc/selected_layers.h"
#include <memory>
@ -64,7 +64,7 @@ namespace app {
// Files already used in the CLI processing (e.g. when used to
// load a sequence of files) so we don't ask for them again.
std::set<std::string> m_usedFiles;
OpenFileCommand::SequenceDecision m_lastDecision = OpenFileCommand::SequenceDecision::Ask;
OpenBatchOfFiles m_batch;
};
} // namespace app

View File

@ -123,6 +123,12 @@ void OpenFileCommand::onExecute(Context* context)
// 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
@ -153,7 +159,11 @@ void OpenFileCommand::onExecute(Context* context)
if (m_oneFrame)
flags |= FILE_LOAD_ONE_FRAME;
for (const auto& filename : filenames) {
std::string filename;
while (!filenames.empty()) {
filename = filenames[0];
filenames.erase(filenames.begin());
std::unique_ptr<FileOp> fop(
FileOp::createLoadDocumentOperation(
context, filename, flags));
@ -171,14 +181,27 @@ void OpenFileCommand::onExecute(Context* context)
if (fop->isSequence()) {
if (fop->sequenceFlags() & FILE_LOAD_SEQUENCE_YES) {
m_seqDecision = SequenceDecision::Agree;
flags &= ~FILE_LOAD_SEQUENCE_ASK;
flags |= FILE_LOAD_SEQUENCE_YES;
}
else if (fop->sequenceFlags() & FILE_LOAD_SEQUENCE_NONE) {
m_seqDecision = SequenceDecision::Skip;
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);
}
m_usedFiles = fop->filenames();
}
else {
m_usedFiles.push_back(fop->filename());
auto fn = base::normalize_path(fop->filename());
m_usedFiles.push_back(fn);
}
OpenFileJob task(fop.get());

View File

@ -36,6 +36,7 @@
#include "app/ui/status_bar.h"
#include "app/ui/toolbar.h"
#include "app/ui_context.h"
#include "app/util/open_batch.h"
#include "base/clamp.h"
#include "base/fs.h"
#include "base/memory.h"
@ -367,7 +368,9 @@ void defer_invalid_rect(const gfx::Rect& rc)
defered_invalid_region.createUnion(defered_invalid_region, gfx::Region(rc));
}
//////////////////////////////////////////////////////////////////////
// Manager event handler.
bool CustomizedGuiManager::onProcessMessage(Message* msg)
{
#ifdef ENABLE_STEAM
@ -392,6 +395,7 @@ bool CustomizedGuiManager::onProcessMessage(Message* msg)
if (getForegroundWindow() == App::instance()->mainWindow()) {
base::paths files = static_cast<DropFilesMessage*>(msg)->files();
UIContext* ctx = UIContext::instance();
OpenBatchOfFiles batch;
while (!files.empty()) {
auto fn = files.front();
@ -422,14 +426,11 @@ bool CustomizedGuiManager::onProcessMessage(Message* msg)
}
// Other extensions will be handled as an image/sprite
else {
OpenFileCommand cmd;
Params params;
params.set("filename", fn.c_str());
params.set("repeat_checkbox", "true");
ctx->executeCommandFromMenuOrShortcut(&cmd, params);
batch.open(ctx, fn,
false); // Open all frames
// Remove all used file names from the "dropped files"
for (const auto& usedFn : cmd.usedFiles()) {
for (const auto& usedFn : batch.usedFiles()) {
auto it = std::find(files.begin(), files.end(), usedFn);
if (it != files.end())
files.erase(it);

64
src/app/util/open_batch.h Normal file
View File

@ -0,0 +1,64 @@
// Aseprite
// Copyright (C) 2020 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_UTIL_OPEN_BATCH_H_INCLUDED
#define APP_UTIL_OPEN_BATCH_H_INCLUDED
#pragma once
#include "app/commands/cmd_open_file.h"
#include "app/context.h"
namespace app {
// Helper class to a batch of files and handle the loading of image
// sequences and repeat the selected action of the user (Agree/Skip
// to open the sequence, and Repeat the action for all other
// elements)
class OpenBatchOfFiles {
public:
void open(Context* ctx,
const std::string& fn,
const bool oneFrame) {
Params params;
params.set("filename", fn.c_str());
if (oneFrame)
params.set("oneframe", "true");
else {
switch (m_lastDecision) {
case OpenFileCommand::SequenceDecision::Ask:
params.set("sequence", "ask");
params.set("repeat_checkbox", "true");
break;
case OpenFileCommand::SequenceDecision::Skip:
params.set("sequence", "skip");
break;
case OpenFileCommand::SequenceDecision::Agree:
params.set("sequence", "agree");
break;
}
}
ctx->executeCommandFromMenuOrShortcut(&m_cmd, params);
// Future decision for other files in the CLI
auto d = m_cmd.seqDecision();
if (d != OpenFileCommand::SequenceDecision::Ask)
m_lastDecision = d;
}
const base::paths& usedFiles() const {
return m_cmd.usedFiles();
}
private:
OpenFileCommand m_cmd;
OpenFileCommand::SequenceDecision m_lastDecision = OpenFileCommand::SequenceDecision::Ask;
};
} // namespace app
#endif