[aseprite] 111/134: Add --filename-format option (fix #519)
Tobias Hansen
thansen at moszumanska.debian.org
Sat Mar 14 17:10:14 UTC 2015
This is an automated email from the git hooks/post-receive script.
thansen pushed a commit to branch master
in repository aseprite.
commit 72e2040c58d6120c1fcd08970422ea35002c24b6
Author: David Capello <davidcapello at gmail.com>
Date: Sun Jan 25 22:36:32 2015 -0300
Add --filename-format option (fix #519)
---
src/app/CMakeLists.txt | 1 +
src/app/app.cpp | 48 ++++++----
src/app/app_options.cpp | 1 +
src/app/app_options.h | 2 +
src/app/commands/cmd_save_file.cpp | 178 ++++++++++++++++++-----------------
src/app/commands/cmd_save_file.h | 5 +-
src/app/document_exporter.cpp | 35 ++++---
src/app/document_exporter.h | 5 +
src/app/file/file.cpp | 67 +++++++++----
src/app/file/file.h | 2 +-
src/app/filename_formatter.cpp | 105 +++++++++++++++++++++
src/app/filename_formatter.h | 44 +++++++++
src/app/filename_formatter_tests.cpp | 142 ++++++++++++++++++++++++++++
src/base/CMakeLists.txt | 1 +
src/base/LICENSE.txt | 2 +-
src/base/README.md | 2 +-
src/base/replace_string.cpp | 33 +++++++
src/base/replace_string.h | 22 +++++
src/base/replace_string_tests.cpp | 31 ++++++
19 files changed, 586 insertions(+), 140 deletions(-)
diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt
index 65f79fb..9e7d050 100644
--- a/src/app/CMakeLists.txt
+++ b/src/app/CMakeLists.txt
@@ -154,6 +154,7 @@ add_library(app-lib
file/tga_format.cpp
file_selector.cpp
file_system.cpp
+ filename_formatter.cpp
flatten.cpp
gui_xml.cpp
handle_anidir.cpp
diff --git a/src/app/app.cpp b/src/app/app.cpp
index 62aaa2b..9119a06 100644
--- a/src/app/app.cpp
+++ b/src/app/app.cpp
@@ -37,6 +37,7 @@
#include "app/file/file_formats_manager.h"
#include "app/file/palette_file.h"
#include "app/file_system.h"
+#include "app/filename_formatter.h"
#include "app/find_widget.h"
#include "app/gui_xml.h"
#include "app/ini_file.h"
@@ -220,6 +221,7 @@ void App::initialize(const AppOptions& options)
bool splitLayersSaveAs = false;
std::string importLayer;
std::string importLayerSaveAs;
+ std::string filenameFormat;
for (const auto& value : options.values()) {
const AppOptions::Option* opt = value.option();
@@ -265,6 +267,10 @@ void App::initialize(const AppOptions& options)
else if (opt == &options.ignoreEmpty()) {
ignoreEmpty = true;
}
+ // --filename-format
+ else if (opt == &options.filenameFormat()) {
+ filenameFormat = value.value();
+ }
// --save-as <filename>
else if (opt == &options.saveAs()) {
Document* doc = NULL;
@@ -277,30 +283,35 @@ void App::initialize(const AppOptions& options)
else {
ctx->setActiveDocument(doc);
+ std::string format = filenameFormat;
+
Command* command = CommandsModule::instance()->getCommandByName(CommandId::SaveFileCopyAs);
if (splitLayersSaveAs) {
std::vector<Layer*> layers;
doc->sprite()->getLayersList(layers);
+ std::string fn, fmt;
+ if (format.empty()) {
+ if (doc->sprite()->totalFrames() > FrameNumber(1))
+ format = "{path}/{title} ({layer}) {frame}.{extension}";
+ else
+ format = "{path}/{title} ({layer}).{extension}";
+ }
+
// For each layer, hide other ones and save the sprite.
for (Layer* show : layers) {
for (Layer* hide : layers)
hide->setReadable(hide == show);
- std::string frameStr;
- if (doc->sprite()->totalFrames() > FrameNumber(1))
- frameStr += " 1";
-
- std::string fn = value.value();
- fn =
- base::join_path(
- base::get_file_path(fn),
- base::get_file_title(fn))
- + " (" + show->name() + ")" + frameStr + "." +
- base::get_file_extension(fn);
-
- static_cast<SaveFileBaseCommand*>(command)->setFilename(fn);
- ctx->executeCommand(command);
+ fn = filename_formatter(format,
+ value.value(), show->name());
+ fmt = filename_formatter(format,
+ value.value(), show->name(), -1, false);
+
+ Params params;
+ params.set("filename", fn.c_str());
+ params.set("filename-format", fmt.c_str());
+ ctx->executeCommand(command, ¶ms);
}
}
else {
@@ -313,8 +324,10 @@ void App::initialize(const AppOptions& options)
layer->setReadable(layer->name() == importLayerSaveAs);
}
- static_cast<SaveFileBaseCommand*>(command)->setFilename(value.value());
- ctx->executeCommand(command);
+ Params params;
+ params.set("filename", value.value().c_str());
+ params.set("filename-format", format.c_str());
+ ctx->executeCommand(command, ¶ms);
}
}
}
@@ -381,6 +394,9 @@ void App::initialize(const AppOptions& options)
splitLayers = false;
}
}
+
+ if (m_exporter && !filenameFormat.empty())
+ m_exporter->setFilenameFormat(filenameFormat);
}
// Export
diff --git a/src/app/app_options.cpp b/src/app/app_options.cpp
index 7f78773..0557450 100644
--- a/src/app/app_options.cpp
+++ b/src/app/app_options.cpp
@@ -49,6 +49,7 @@ AppOptions::AppOptions(int argc, const char* argv[])
, m_splitLayers(m_po.add("split-layers").description("Import each layer of the next given sprite as\na separated image in the sheet"))
, m_importLayer(m_po.add("import-layer").requiresValue("<name>").description("Import just one layer of the next given sprite"))
, m_ignoreEmpty(m_po.add("ignore-empty").description("Do not export empty frames/cels"))
+ , m_filenameFormat(m_po.add("filename-format").requiresValue("<fmt>").description("Special format to generate filenames"))
, m_verbose(m_po.add("verbose").description("Explain what is being done"))
, m_help(m_po.add("help").mnemonic('?').description("Display this help and exits"))
, m_version(m_po.add("version").description("Output version information and exit"))
diff --git a/src/app/app_options.h b/src/app/app_options.h
index 19901d8..a9c5b8b 100644
--- a/src/app/app_options.h
+++ b/src/app/app_options.h
@@ -57,6 +57,7 @@ public:
const Option& splitLayers() const { return m_splitLayers; }
const Option& importLayer() const { return m_importLayer; }
const Option& ignoreEmpty() const { return m_ignoreEmpty; }
+ const Option& filenameFormat() const { return m_filenameFormat; }
bool hasExporterParams() const;
@@ -84,6 +85,7 @@ private:
Option& m_splitLayers;
Option& m_importLayer;
Option& m_ignoreEmpty;
+ Option& m_filenameFormat;
Option& m_verbose;
Option& m_help;
diff --git a/src/app/commands/cmd_save_file.cpp b/src/app/commands/cmd_save_file.cpp
index 0defd78..8e66ec9 100644
--- a/src/app/commands/cmd_save_file.cpp
+++ b/src/app/commands/cmd_save_file.cpp
@@ -20,6 +20,8 @@
#include "config.h"
#endif
+#include "app/commands/cmd_save_file.h"
+
#include "app/app.h"
#include "app/commands/command.h"
#include "app/commands/params.h"
@@ -79,9 +81,11 @@ private:
FileOp* m_fop;
};
-static void save_document_in_background(Context* context, Document* document, bool mark_as_saved)
+static void save_document_in_background(Context* context, Document* document,
+ bool mark_as_saved, const std::string& fn_format)
{
- base::UniquePtr<FileOp> fop(fop_to_save_document(context, document));
+ base::UniquePtr<FileOp> fop(fop_to_save_document(context, document,
+ fn_format.c_str()));
if (!fop)
return;
@@ -113,111 +117,112 @@ static void save_document_in_background(Context* context, Document* document, bo
//////////////////////////////////////////////////////////////////////
-class SaveFileBaseCommand : public Command {
-public:
- SaveFileBaseCommand(const char* short_name, const char* friendly_name, CommandFlags flags)
- : Command(short_name, friendly_name, flags) {
- }
+SaveFileBaseCommand::SaveFileBaseCommand(const char* short_name, const char* friendly_name, CommandFlags flags)
+ : Command(short_name, friendly_name, flags)
+{
+}
-protected:
- void onLoadParams(Params* params) override {
- m_filename = params->get("filename");
- }
+void SaveFileBaseCommand::onLoadParams(Params* params)
+{
+ m_filename = params->get("filename");
+ m_filenameFormat = params->get("filename-format");
+}
- // Returns true if there is a current sprite to save.
- // [main thread]
- bool onEnabled(Context* context) override {
- return context->checkFlags(ContextFlags::ActiveDocumentIsWritable);
+// Returns true if there is a current sprite to save.
+// [main thread]
+bool SaveFileBaseCommand::onEnabled(Context* context)
+{
+ return context->checkFlags(ContextFlags::ActiveDocumentIsWritable);
+}
+
+void SaveFileBaseCommand::saveAsDialog(const ContextReader& reader, const char* dlgTitle, bool markAsSaved)
+{
+ const Document* document = reader.document();
+ std::string filename;
+
+ if (!m_filename.empty()) {
+ filename = m_filename;
}
+ else {
+ filename = document->filename();
- void saveAsDialog(const ContextReader& reader, const char* dlgTitle, bool markAsSaved)
- {
- const Document* document = reader.document();
- std::string filename;
+ char exts[4096];
+ get_writable_extensions(exts, sizeof(exts));
- if (!m_filename.empty()) {
- filename = m_filename;
- }
- else {
- filename = document->filename();
-
- char exts[4096];
- get_writable_extensions(exts, sizeof(exts));
-
- for (;;) {
- std::string newfilename = app::show_file_selector(dlgTitle, filename, exts);
- if (newfilename.empty())
- return;
-
- filename = newfilename;
-
- // Ask if the user wants overwrite the existent file.
- int ret = 0;
- if (base::is_file(filename)) {
- ret = ui::Alert::show("Warning<<The file already exists, overwrite it?<<%s||&Yes||&No||&Cancel",
- base::get_file_name(filename).c_str());
-
- // Check for read-only attribute.
- if (ret == 1) {
- if (!confirmReadonly(filename))
- ret = 2; // Select file again.
- else
- break;
- }
- }
- else
- break;
+ for (;;) {
+ std::string newfilename = app::show_file_selector(dlgTitle, filename, exts);
+ if (newfilename.empty())
+ return;
+
+ filename = newfilename;
+
+ // Ask if the user wants overwrite the existent file.
+ int ret = 0;
+ if (base::is_file(filename)) {
+ ret = ui::Alert::show("Warning<<The file already exists, overwrite it?<<%s||&Yes||&No||&Cancel",
+ base::get_file_name(filename).c_str());
- // "yes": we must continue with the operation...
+ // Check for read-only attribute.
if (ret == 1) {
- break;
+ if (!confirmReadonly(filename))
+ ret = 2; // Select file again.
+ else
+ break;
}
- // "cancel" or <esc> per example: we back doing nothing
- else if (ret != 2)
- return;
+ }
+ else
+ break;
- // "no": we must back to select other file-name
+ // "yes": we must continue with the operation...
+ if (ret == 1) {
+ break;
}
+ // "cancel" or <esc> per example: we back doing nothing
+ else if (ret != 2)
+ return;
+
+ // "no": we must back to select other file-name
}
+ }
- {
- ContextWriter writer(reader);
- Document* documentWriter = writer.document();
- std::string oldFilename = documentWriter->filename();
+ {
+ ContextWriter writer(reader);
+ Document* documentWriter = writer.document();
+ std::string oldFilename = documentWriter->filename();
- // Change the document file name
- documentWriter->setFilename(filename.c_str());
- m_selectedFilename = filename;
+ // Change the document file name
+ documentWriter->setFilename(filename.c_str());
+ m_selectedFilename = filename;
- // Save the document
- save_document_in_background(writer.context(), documentWriter, markAsSaved);
+ // Save the document
+ save_document_in_background(writer.context(), documentWriter,
+ markAsSaved, m_filenameFormat);
- if (documentWriter->isModified())
- documentWriter->setFilename(oldFilename);
+ if (documentWriter->isModified())
+ documentWriter->setFilename(oldFilename);
- update_screen_for_document(documentWriter);
- }
+ update_screen_for_document(documentWriter);
}
+}
- static bool confirmReadonly(const std::string& filename)
- {
- if (!base::has_readonly_attr(filename))
- return true;
+//static
+bool SaveFileBaseCommand::confirmReadonly(const std::string& filename)
+{
+ if (!base::has_readonly_attr(filename))
+ return true;
- int ret = ui::Alert::show("Warning<<The file is read-only, do you really want to overwrite it?<<%s||&Yes||&No",
- base::get_file_name(filename).c_str());
+ int ret = ui::Alert::show("Warning<<The file is read-only, do you really want to overwrite it?<<%s||&Yes||&No",
+ base::get_file_name(filename).c_str());
- if (ret == 1) {
- base::remove_readonly_attr(filename);
- return true;
- }
- else
- return false;
+ if (ret == 1) {
+ base::remove_readonly_attr(filename);
+ return true;
}
+ else
+ return false;
+}
- std::string m_filename;
- std::string m_selectedFilename;
-};
+//////////////////////////////////////////////////////////////////////
class SaveFileCommand : public SaveFileBaseCommand {
public:
@@ -249,7 +254,8 @@ void SaveFileCommand::onExecute(Context* context)
if (!confirmReadonly(documentWriter->filename()))
return;
- save_document_in_background(context, documentWriter, true);
+ save_document_in_background(context, documentWriter, true,
+ m_filenameFormat.c_str());
update_screen_for_document(documentWriter);
}
// If the document isn't associated to a file, we must to show the
diff --git a/src/app/commands/cmd_save_file.h b/src/app/commands/cmd_save_file.h
index f400979..f329dd1 100644
--- a/src/app/commands/cmd_save_file.h
+++ b/src/app/commands/cmd_save_file.h
@@ -35,10 +35,6 @@ namespace app {
return m_selectedFilename;
}
- void setFilename(const std::string& fn) {
- m_filename = fn;
- }
-
protected:
void onLoadParams(Params* params) override;
bool onEnabled(Context* context) override;
@@ -48,6 +44,7 @@ namespace app {
static bool confirmReadonly(const std::string& filename);
std::string m_filename;
+ std::string m_filenameFormat;
std::string m_selectedFilename;
};
diff --git a/src/app/document_exporter.cpp b/src/app/document_exporter.cpp
index e15d792..dcbfd20 100644
--- a/src/app/document_exporter.cpp
+++ b/src/app/document_exporter.cpp
@@ -26,6 +26,7 @@
#include "app/document.h"
#include "app/document_api.h"
#include "app/file/file.h"
+#include "app/filename_formatter.h"
#include "app/ui_context.h"
#include "base/convert_to.h"
#include "base/path.h"
@@ -273,24 +274,28 @@ void DocumentExporter::captureSamples(Samples& samples)
Document* doc = item.doc;
Sprite* sprite = doc->sprite();
Layer* layer = item.layer;
+ bool hasFrames = (doc->sprite()->totalFrames() > FrameNumber(1));
+ bool hasLayer = (layer != NULL);
+
+ std::string format = m_filenameFormat;
+ if (format.empty()) {
+ if (hasFrames && hasLayer)
+ format = "{title} ({layer}) {frame}.{extension}";
+ else if (hasFrames)
+ format = "{title} {frame}.{extension}";
+ else if (hasLayer)
+ format = "{title} ({layer}).{extension}";
+ else
+ format = "{fullname}";
+ }
for (FrameNumber frame=FrameNumber(0);
frame<sprite->totalFrames(); ++frame) {
- std::string filename = doc->filename();
-
- if (sprite->totalFrames() > FrameNumber(1)) {
- std::string path = base::get_file_path(filename);
- std::string title = base::get_file_title(filename);
- if (layer) {
- title += " (";
- title += layer->name();
- title += ") ";
- }
-
- filename = base::join_path(path, title +
- base::convert_to<std::string>((int)frame + 1)
- + "." + base::get_file_extension(filename));
- }
+ std::string filename =
+ filename_formatter(format,
+ doc->filename(),
+ layer ? layer->name(): "",
+ (sprite->totalFrames() > FrameNumber(1)) ? frame: FrameNumber(-1));
Sample sample(doc, sprite, layer, frame, filename);
diff --git a/src/app/document_exporter.h b/src/app/document_exporter.h
index 1224821..7fce95a 100644
--- a/src/app/document_exporter.h
+++ b/src/app/document_exporter.h
@@ -93,6 +93,10 @@ namespace app {
m_ignoreEmptyCels = ignore;
}
+ void setFilenameFormat(const std::string& format) {
+ m_filenameFormat = format;
+ }
+
void addDocument(Document* document, raster::Layer* layer = NULL) {
m_documents.push_back(Item(document, layer));
}
@@ -133,6 +137,7 @@ namespace app {
ScaleMode m_scaleMode;
bool m_ignoreEmptyCels;
Items m_documents;
+ std::string m_filenameFormat;
DISABLE_COPYING(DocumentExporter);
};
diff --git a/src/app/file/file.cpp b/src/app/file/file.cpp
index d61e27a..a9609df 100644
--- a/src/app/file/file.cpp
+++ b/src/app/file/file.cpp
@@ -28,6 +28,7 @@
#include "app/file/file_format.h"
#include "app/file/file_formats_manager.h"
#include "app/file/format_options.h"
+#include "app/filename_formatter.h"
#include "app/modules/gui.h"
#include "app/modules/palettes.h"
#include "app/ui/status_bar.h"
@@ -122,7 +123,8 @@ int save_document(Context* context, doc::Document* document)
ASSERT(dynamic_cast<app::Document*>(document));
int ret;
- FileOp* fop = fop_to_save_document(context, static_cast<app::Document*>(document));
+ FileOp* fop = fop_to_save_document(context,
+ static_cast<app::Document*>(document), "");
if (!fop)
return -1;
@@ -235,7 +237,7 @@ done:;
return fop;
}
-FileOp* fop_to_save_document(Context* context, Document* document)
+FileOp* fop_to_save_document(Context* context, Document* document, const char* fn_format_arg)
{
FileOp *fop;
bool fatal;
@@ -359,26 +361,59 @@ FileOp* fop_to_save_document(Context* context, Document* document)
if (fop->format->support(FILE_SUPPORT_SEQUENCES)) {
fop_prepare_for_sequence(fop);
- // To save one frame
+ std::string fn = fop->document->filename();
+ std::string fn_format = fn_format_arg;
+ bool default_format = false;
+
+ if (fn_format.empty()) {
+ if (fop->document->sprite()->totalFrames() == 1)
+ fn_format = "{fullname}";
+ else {
+ fn_format = "{path}/{title}{frame}.{extension}";
+ default_format = true;
+ }
+ }
+
+ // Save one frame
if (fop->document->sprite()->totalFrames() == 1) {
- fop->seq.filename_list.push_back(fop->document->filename());
+ fn = filename_formatter(fn_format, fn);
+ fop->seq.filename_list.push_back(fn);
}
- // To save more frames
+ // Save multiple frames
else {
- char left[256], right[256];
- int width, start_from;
-
- start_from = split_filename(fop->document->filename().c_str(), left, right, &width);
- if (start_from < 0) {
- start_from = 1;
- width = 1;
+ int width = 0;
+ int start_from = 0;
+
+ if (default_format) {
+ char left[256], right[256];
+ start_from = split_filename(fn.c_str(), left, right, &width);
+ if (start_from < 0) {
+ start_from = 1;
+ width = 1;
+ }
+ else {
+ fn = left;
+ fn += right;
+ }
}
+ std::vector<char> buf(32);
+ std::sprintf(&buf[0], "{frame%0*d}", width, 0);
+ if (default_format)
+ fn_format = set_frame_format(fn_format, &buf[0]);
+ else if (fop->document->sprite()->totalFrames() > 1)
+ fn_format = add_frame_format(fn_format, &buf[0]);
+
+ printf("start_from = %d\n", start_from);
+ printf("fn_format = %s\n", fn_format.c_str());
+ printf("fn = %s\n", fn.c_str());
+
for (FrameNumber frame(0); frame<fop->document->sprite()->totalFrames(); ++frame) {
- // Get the name for this frame
- char buf[4096];
- sprintf(buf, "%s%0*d%s", left, width, start_from+frame, right);
- fop->seq.filename_list.push_back(buf);
+ std::string frame_fn =
+ filename_formatter(fn_format, fn, "", start_from+frame);
+
+ printf("frame_fn = %s\n", frame_fn.c_str());
+ fop->seq.filename_list.push_back(frame_fn);
}
}
}
diff --git a/src/app/file/file.h b/src/app/file/file.h
index 35dd778..99ba0ba 100644
--- a/src/app/file/file.h
+++ b/src/app/file/file.h
@@ -133,7 +133,7 @@ namespace app {
// Low-level routines to load/save documents.
FileOp* fop_to_load_document(Context* context, const char* filename, int flags);
- FileOp* fop_to_save_document(Context* context, Document* document);
+ FileOp* fop_to_save_document(Context* context, Document* document, const char* fn_format);
void fop_operate(FileOp* fop, IFileOpProgress* progress);
void fop_done(FileOp* fop);
void fop_stop(FileOp* fop);
diff --git a/src/app/filename_formatter.cpp b/src/app/filename_formatter.cpp
new file mode 100644
index 0000000..028beb0
--- /dev/null
+++ b/src/app/filename_formatter.cpp
@@ -0,0 +1,105 @@
+/* Aseprite
+ * Copyright (C) 2001-2015 David Capello
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "app/filename_formatter.h"
+
+#include "base/convert_to.h"
+#include "base/path.h"
+#include "base/replace_string.h"
+
+#include <vector>
+
+namespace app {
+
+std::string filename_formatter(
+ const std::string& format,
+ const std::string& filename,
+ const std::string& layerName,
+ int frame, bool replaceFrame)
+{
+ std::string output = format;
+ base::replace_string(output, "{fullname}", filename);
+ base::replace_string(output, "{path}", base::get_file_path(filename));
+ base::replace_string(output, "{name}", base::get_file_name(filename));
+ base::replace_string(output, "{title}", base::get_file_title(filename));
+ base::replace_string(output, "{extension}", base::get_file_extension(filename));
+ base::replace_string(output, "{layer}", layerName);
+
+ if (replaceFrame) {
+ size_t i = output.find("{frame");
+ if (i != std::string::npos) {
+ size_t j = output.find("}", i+6);
+ if (j != std::string::npos) {
+ std::string from = output.substr(i, j - i + 1);
+ if (frame >= 0) {
+ std::vector<char> to(32);
+ int offset = std::strtol(from.c_str()+6, NULL, 10);
+
+ std::sprintf(&to[0], "%0*d", (int(j)-int(i+6)), frame + offset);
+ base::replace_string(output, from, &to[0]);
+ }
+ else
+ base::replace_string(output, from, "");
+ }
+ }
+ }
+
+ return output;
+}
+
+std::string set_frame_format(
+ const std::string& format,
+ const std::string& newFrameFormat)
+{
+ std::string output = format;
+
+ size_t i = output.find("{frame");
+ if (i != std::string::npos) {
+ size_t j = output.find("}", i+6);
+ if (j != std::string::npos) {
+ output.replace(i, j - i + 1, newFrameFormat);
+ }
+ }
+
+ return output;
+}
+
+std::string add_frame_format(
+ const std::string& format,
+ const std::string& newFrameFormat)
+{
+ std::string output = format;
+
+ size_t i = output.find("{frame");
+ if (i == std::string::npos) {
+ output =
+ base::join_path(
+ base::get_file_path(format),
+ base::get_file_title(format))
+ + newFrameFormat + "." +
+ base::get_file_extension(format);
+ }
+
+ return output;
+}
+
+} // namespace app
diff --git a/src/app/filename_formatter.h b/src/app/filename_formatter.h
new file mode 100644
index 0000000..b74602d
--- /dev/null
+++ b/src/app/filename_formatter.h
@@ -0,0 +1,44 @@
+/* Aseprite
+ * Copyright (C) 2001-2015 David Capello
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef APP_FILENAME_FORMATTER_H_INCLUDED
+#define APP_FILENAME_FORMATTER_H_INCLUDED
+#pragma once
+
+#include <string>
+
+namespace app {
+
+ std::string filename_formatter(
+ const std::string& format,
+ const std::string& filename,
+ const std::string& layerName = "",
+ int frame = -1,
+ bool replaceFrame = true);
+
+ std::string set_frame_format(
+ const std::string& format,
+ const std::string& newFrameFormat);
+
+ std::string add_frame_format(
+ const std::string& format,
+ const std::string& newFrameFormat);
+
+} // namespace app
+
+#endif
diff --git a/src/app/filename_formatter_tests.cpp b/src/app/filename_formatter_tests.cpp
new file mode 100644
index 0000000..e0004fe
--- /dev/null
+++ b/src/app/filename_formatter_tests.cpp
@@ -0,0 +1,142 @@
+/* Aseprite
+ * Copyright (C) 2001-2015 David Capello
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gtest/gtest.h>
+
+#include "app/filename_formatter.h"
+
+#include "base/path.h"
+
+using namespace app;
+
+TEST(FilenameFormatter, Basic)
+{
+ EXPECT_EQ("C:/temp/file.png",
+ filename_formatter("{fullname}",
+ "C:/temp/file.png"));
+
+ EXPECT_EQ("file.png",
+ filename_formatter("{name}",
+ "C:/temp/file.png"));
+
+ EXPECT_EQ("C:/temp/other.png",
+ filename_formatter("{path}/other.png",
+ "C:/temp/file.png"));
+}
+
+TEST(FilenameFormatter, WithoutFrame)
+{
+ EXPECT_EQ("C:/temp/file.png",
+ filename_formatter("{path}/{title}.png",
+ "C:/temp/file.ase"));
+
+ EXPECT_EQ("C:/temp/file.png",
+ filename_formatter("{path}/{title}{frame}.{extension}",
+ "C:/temp/file.png"));
+
+ EXPECT_EQ("C:/temp/file{frame}.png",
+ filename_formatter("{path}/{title}{frame}.{extension}",
+ "C:/temp/file.png", "", -1, false));
+
+ EXPECT_EQ("C:/temp/file (Background).png",
+ filename_formatter("{path}/{title} ({layer}).{extension}",
+ "C:/temp/file.png", "Background"));
+}
+
+TEST(FilenameFormatter, WithFrame)
+{
+ EXPECT_EQ("C:/temp/file0.png",
+ filename_formatter("{path}/{title}{frame}.{extension}",
+ "C:/temp/file.png", "", 0));
+
+ EXPECT_EQ("C:/temp/file1.png",
+ filename_formatter("{path}/{title}{frame}.{extension}",
+ "C:/temp/file.png", "", 1));
+
+ EXPECT_EQ("C:/temp/file10.png",
+ filename_formatter("{path}/{title}{frame}.{extension}",
+ "C:/temp/file.png", "", 10));
+
+ EXPECT_EQ("C:/temp/file0.png",
+ filename_formatter("{path}/{title}{frame0}.{extension}",
+ "C:/temp/file.png", "", 0));
+
+ EXPECT_EQ("C:/temp/file1.png",
+ filename_formatter("{path}/{title}{frame1}.{extension}",
+ "C:/temp/file.png", "", 0));
+
+ EXPECT_EQ("C:/temp/file2.png",
+ filename_formatter("{path}/{title}{frame1}.{extension}",
+ "C:/temp/file.png", "", 1));
+
+ EXPECT_EQ("C:/temp/file00.png",
+ filename_formatter("{path}/{title}{frame00}.{extension}",
+ "C:/temp/file.png", "", 0));
+
+ EXPECT_EQ("C:/temp/file01.png",
+ filename_formatter("{path}/{title}{frame01}.{extension}",
+ "C:/temp/file.png", "", 0));
+
+ EXPECT_EQ("C:/temp/file002.png",
+ filename_formatter("{path}/{title}{frame000}.{extension}",
+ "C:/temp/file.png", "", 2));
+
+ EXPECT_EQ("C:/temp/file0032.png",
+ filename_formatter("{path}/{title}{frame0032}.{extension}",
+ "C:/temp/file.png", "", 0));
+
+ EXPECT_EQ("C:/temp/file-Background-2.png",
+ filename_formatter("{path}/{title}-{layer}-{frame}.{extension}",
+ "C:/temp/file.png", "Background", 2));
+}
+
+TEST(SetFrameFormat, Tests)
+{
+ EXPECT_EQ("{path}/{title}{frame1}.{extension}",
+ set_frame_format("{path}/{title}{frame}.{extension}",
+ "{frame1}"));
+
+ EXPECT_EQ("{path}/{title}{frame01}.{extension}",
+ set_frame_format("{path}/{title}{frame}.{extension}",
+ "{frame01}"));
+
+ EXPECT_EQ("{path}/{title}{frame}.{extension}",
+ set_frame_format("{path}/{title}{frame01}.{extension}",
+ "{frame}"));
+}
+
+TEST(AddFrameFormat, Tests)
+{
+ EXPECT_EQ(base::fix_path_separators("{path}/{title}{frame001}.{extension}"),
+ add_frame_format("{path}/{title}.{extension}",
+ "{frame001}"));
+
+ EXPECT_EQ("{path}/{title}{frame1}.{extension}",
+ add_frame_format("{path}/{title}{frame1}.{extension}",
+ "{frame001}"));
+}
+
+int main(int argc, char** argv)
+{
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/src/base/CMakeLists.txt b/src/base/CMakeLists.txt
index 7f5f778..18b52f3 100644
--- a/src/base/CMakeLists.txt
+++ b/src/base/CMakeLists.txt
@@ -31,6 +31,7 @@ set(BASE_SOURCES
mutex.cpp
path.cpp
program_options.cpp
+ replace_string.cpp
serialization.cpp
sha1.cpp
sha1_rfc3174.c
diff --git a/src/base/LICENSE.txt b/src/base/LICENSE.txt
index 220ac0d..7ed8f20 100644
--- a/src/base/LICENSE.txt
+++ b/src/base/LICENSE.txt
@@ -1,4 +1,4 @@
-Copyright (c) 2001-2014 David Capello
+Copyright (c) 2001-2015 David Capello
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/src/base/README.md b/src/base/README.md
index 2c06880..08396a0 100644
--- a/src/base/README.md
+++ b/src/base/README.md
@@ -1,5 +1,5 @@
# Aseprite Base Library
-*Copyright (C) 2001-2013 David Capello*
+*Copyright (C) 2001-2015 David Capello*
> Distributed under [MIT license](LICENSE.txt)
diff --git a/src/base/replace_string.cpp b/src/base/replace_string.cpp
new file mode 100644
index 0000000..281e02a
--- /dev/null
+++ b/src/base/replace_string.cpp
@@ -0,0 +1,33 @@
+// Aseprite Base Library
+// Copyright (c) 2001-2015 David Capello
+//
+// This file is released under the terms of the MIT license.
+// Read LICENSE.txt for more information.
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "base/replace_string.h"
+
+namespace base {
+
+void replace_string(
+ std::string& subject,
+ const std::string& replace_this,
+ const std::string& with_that)
+{
+ if (replace_this.empty()) // Do nothing case
+ return;
+
+ size_t i = 0;
+ while (true) {
+ i = subject.find(replace_this, i);
+ if (i == std::string::npos)
+ break;
+ subject.replace(i, replace_this.size(), with_that);
+ i += with_that.size();
+ }
+}
+
+} // namespace base
diff --git a/src/base/replace_string.h b/src/base/replace_string.h
new file mode 100644
index 0000000..0076830
--- /dev/null
+++ b/src/base/replace_string.h
@@ -0,0 +1,22 @@
+// Aseprite Base Library
+// Copyright (c) 2001-2015 David Capello
+//
+// This file is released under the terms of the MIT license.
+// Read LICENSE.txt for more information.
+
+#ifndef BASE_REPLACE_STRING_H_INCLUDED
+#define BASE_REPLACE_STRING_H_INCLUDED
+#pragma once
+
+#include <string>
+
+namespace base {
+
+ void replace_string(
+ std::string& subject,
+ const std::string& replace_this,
+ const std::string& with_that);
+
+}
+
+#endif
diff --git a/src/base/replace_string_tests.cpp b/src/base/replace_string_tests.cpp
new file mode 100644
index 0000000..b93985f
--- /dev/null
+++ b/src/base/replace_string_tests.cpp
@@ -0,0 +1,31 @@
+// Aseprite Base Library
+// Copyright (c) 2001-2015 David Capello
+//
+// This file is released under the terms of the MIT license.
+// Read LICENSE.txt for more information.
+
+#include <gtest/gtest.h>
+
+#include "base/replace_string.h"
+
+inline std::string rs(const std::string& s, const std::string& a, const std::string& b) {
+ std::string res = s;
+ base::replace_string(res, a, b);
+ return res;
+}
+
+TEST(ReplaceString, Basic)
+{
+ EXPECT_EQ("", rs("", "", ""));
+ EXPECT_EQ("aa", rs("ab", "b", "a"));
+ EXPECT_EQ("abc", rs("accc", "cc", "b"));
+ EXPECT_EQ("abb", rs("acccc", "cc", "b"));
+ EXPECT_EQ("aabbbbaabbbb", rs("aabbaabb", "bb", "bbbb"));
+ EXPECT_EQ("123123123", rs("111", "1", "123"));
+}
+
+int main(int argc, char** argv)
+{
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-games/aseprite.git
More information about the Pkg-games-commits
mailing list