Implement issue #265 - command/keyboard shortcut for "last export" operation

- Add doc::ExportData with information about the last export operation.
- Add RepeatLastExport command.
- Add SaveFileBaseCommand as base Command class, to accept "filename"
  param.
- Add parameters to ExportSpriteSheet so we can call it without UI
  from "repeat last export".
This commit is contained in:
David Capello 2014-03-29 16:58:35 -03:00
parent 395be62b03
commit b16bf981d0
12 changed files with 606 additions and 280 deletions

View File

@ -14,6 +14,7 @@
<key command="SaveFileCopyAs" shortcut="Ctrl+Shift+C" /> <key command="SaveFileCopyAs" shortcut="Ctrl+Shift+C" />
<key command="CloseFile" shortcut="Ctrl+W" /> <key command="CloseFile" shortcut="Ctrl+W" />
<key command="CloseAllFiles" shortcut="Ctrl+Shift+W" /> <key command="CloseAllFiles" shortcut="Ctrl+Shift+W" />
<key command="RepeatLastExport" shortcut="Ctrl+Shift+X" />
<key command="AdvancedMode" shortcut="F11" /> <key command="AdvancedMode" shortcut="F11" />
<key command="DeveloperConsole" shortcut="F12" /> <key command="DeveloperConsole" shortcut="F12" />
<key command="Exit" shortcut="Alt+F4" /> <key command="Exit" shortcut="Alt+F4" />
@ -229,6 +230,7 @@
<separator /> <separator />
<item command="ImportSpriteSheet" text="&amp;Import Sprite Sheet" /> <item command="ImportSpriteSheet" text="&amp;Import Sprite Sheet" />
<item command="ExportSpriteSheet" text="&amp;Export Sprite Sheet" /> <item command="ExportSpriteSheet" text="&amp;Export Sprite Sheet" />
<item command="RepeatLastExport" text="Repeat &amp;Last Export" />
<separator /> <separator />
<item command="Exit" text="E&amp;xit" /> <item command="Exit" text="E&amp;xit" />
</menu> </menu>

View File

@ -68,6 +68,7 @@ add_library(app-lib
commands/cmd_remove_cel.cpp commands/cmd_remove_cel.cpp
commands/cmd_remove_frame.cpp commands/cmd_remove_frame.cpp
commands/cmd_remove_layer.cpp commands/cmd_remove_layer.cpp
commands/cmd_repeat_last_export.cpp
commands/cmd_reselect_mask.cpp commands/cmd_reselect_mask.cpp
commands/cmd_rotate_canvas.cpp commands/cmd_rotate_canvas.cpp
commands/cmd_save_file.cpp commands/cmd_save_file.cpp

View File

@ -1,5 +1,5 @@
/* Aseprite /* Aseprite
* Copyright (C) 2001-2013 David Capello * Copyright (C) 2001-2014 David Capello
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -20,8 +20,12 @@
#include "config.h" #include "config.h"
#endif #endif
#include "app/commands/cmd_export_sprite_sheet.h"
#include "app/commands/cmd_save_file.h"
#include "app/commands/command.h" #include "app/commands/command.h"
#include "app/commands/commands.h" #include "app/commands/commands.h"
#include "app/commands/params.h"
#include "app/context.h" #include "app/context.h"
#include "app/context_access.h" #include "app/context_access.h"
#include "app/document.h" #include "app/document.h"
@ -50,20 +54,19 @@ namespace app {
using namespace ui; using namespace ui;
class ExportSpriteSheetWindow : public Window { class ExportSpriteSheetWindow : public Window {
enum SpriteSheetType { HorizontalStrip, VerticalStrip, Matrix };
enum ExportAction { SaveCopyAs, SaveAs, Save, DoNotSave };
public: public:
typedef ExportSpriteSheetCommand::SpriteSheetType SpriteSheetType;
typedef ExportSpriteSheetCommand::ExportAction ExportAction;
ExportSpriteSheetWindow(Context* context) ExportSpriteSheetWindow(Context* context)
: Window(WithTitleBar, "Export Sprite Sheet") : Window(WithTitleBar, "Export Sprite Sheet")
, m_context(context)
, m_document(context->getActiveDocument())
, m_grid(4, false) , m_grid(4, false)
, m_columnsLabel("Columns:") , m_columnsLabel("Columns:")
, m_columns(4, "4") , m_columns(4, "4")
, m_exportActionLabel("Export Action:") , m_exportActionLabel("Export Action:")
, m_export("Export") , m_export("&Export")
, m_cancel("Cancel") , m_cancel("&Cancel")
, m_ok(false)
{ {
m_sheetType.addItem("Horizontal Strip"); m_sheetType.addItem("Horizontal Strip");
m_sheetType.addItem("Vertical Strip"); m_sheetType.addItem("Vertical Strip");
@ -100,8 +103,20 @@ public:
onSheetTypeChange(); onSheetTypeChange();
} }
~ExportSpriteSheetWindow() bool ok() const {
{ return m_ok;
}
SpriteSheetType spriteSheetType() const {
return (SpriteSheetType)m_sheetType.getSelectedItemIndex();
}
ExportAction exportAction() const {
return (ExportAction)m_exportAction.getSelectedItemIndex();
}
int columns() const {
return m_columns.getTextInt();
} }
protected: protected:
@ -110,7 +125,7 @@ protected:
{ {
bool state = false; bool state = false;
switch (m_sheetType.getSelectedItemIndex()) { switch (m_sheetType.getSelectedItemIndex()) {
case Matrix: case ExportSpriteSheetCommand::Matrix:
state = true; state = true;
break; break;
} }
@ -125,170 +140,7 @@ protected:
void onExport() void onExport()
{ {
Sprite* sprite = m_document->getSprite(); m_ok = true;
FrameNumber nframes = sprite->getTotalFrames();
int columns;
switch (m_sheetType.getSelectedItemIndex()) {
case HorizontalStrip:
columns = nframes;
break;
case VerticalStrip:
columns = 1;
break;
case Matrix:
columns = m_columns.getTextInt();
break;
}
columns = MID(1, columns, nframes);
int sheet_w = sprite->getWidth()*columns;
int sheet_h = sprite->getHeight()*((nframes/columns)+((nframes%columns)>0?1:0));
base::UniquePtr<Image> resultImage(Image::create(sprite->getPixelFormat(), sheet_w, sheet_h));
base::UniquePtr<Image> tempImage(Image::create(sprite->getPixelFormat(), sprite->getWidth(), sprite->getHeight()));
raster::clear_image(resultImage, 0);
int column = 0, row = 0;
for (FrameNumber frame(0); frame<nframes; ++frame) {
// TODO "tempImage" could not be necessary if we could specify
// destination clipping bounds in Sprite::render() function.
tempImage->clear(0);
sprite->render(tempImage, 0, 0, frame);
resultImage->copy(tempImage, column*sprite->getWidth(), row*sprite->getHeight());
if (++column >= columns) {
column = 0;
++row;
}
}
// Store the frame in the current editor so we can restore it
// after change and restore the setTotalFrames() number.
FrameNumber oldSelectedFrame = (current_editor ? current_editor->getFrame():
FrameNumber(0));
{
// The following steps modify the sprite, so we wrap all
// operations in a undo-transaction.
ContextWriter writer(m_context);
UndoTransaction undoTransaction(writer.context(), "Export Sprite Sheet", undo::ModifyDocument);
DocumentApi api = m_document->getApi();
// Add the layer in the sprite.
LayerImage* resultLayer = api.newLayer(sprite);
// Add the image into the sprite's stock
int indexInStock = api.addImageInStock(sprite, resultImage);
resultImage.release();
// Create the cel.
base::UniquePtr<Cel> resultCel(new Cel(FrameNumber(0), indexInStock));
// Add the cel in the layer.
api.addCel(resultLayer, resultCel);
resultCel.release();
// Copy the list of layers (because we will modify it in the iteration).
LayerList layers = sprite->getFolder()->getLayersList();
// Remove all other layers
for (LayerIterator it=layers.begin(), end=layers.end(); it!=end; ++it) {
if (*it != resultLayer)
api.removeLayer(*it);
}
// Change the number of frames (just one, the sprite sheet). As
// we are using the observable API, all DocumentView will change
// its current frame to frame 1. We'll try to restore the
// selected frame for the current_editor later.
api.setTotalFrames(sprite, FrameNumber(1));
// Set the size of the sprite to the tile size.
api.setSpriteSize(sprite, sheet_w, sheet_h);
undoTransaction.commit();
// Draw the document with the new dimensions in the screen.
update_screen_for_document(m_document);
}
// This flag indicates if we've to undo the last action (so we
// back to the original sprite dimensions).
bool undo = false;
// Do the "Export Action"
switch (m_exportAction.getSelectedItemIndex()) {
case SaveCopyAs:
{
Command* command = CommandsModule::instance()
->getCommandByName(CommandId::SaveFileCopyAs);
m_context->executeCommand(command);
}
// Always go back, as we are using "Save Copy As", so the user
// wants to continue editing the original sprite.
undo = true;
break;
case SaveAs:
{
Command* command = CommandsModule::instance()
->getCommandByName(CommandId::SaveFileAs);
m_context->executeCommand(command);
}
// If the command was cancelled, we go back to the original
// state, if the sprite sheet was saved then we don't undo
// because the user wants to edit the sprite sheet.
undo = (m_document->isModified());
break;
case Save:
{
Command* command = CommandsModule::instance()
->getCommandByName(CommandId::SaveFile);
m_context->executeCommand(command);
}
// Same case as "Save As"
undo = (m_document->isModified());
break;
case DoNotSave:
// Do not undo as the user wants to edit the sprite sheet
// before to save the file.
undo = false;
break;
}
// Undo the sprite sheet conversion
if (undo) {
if (m_document->getUndo()->canUndo()) {
m_document->getUndo()->doUndo();
// We've to restore the previously selected frame. As we've
// called setTotalFrames(), all document observers
// (current_editor included) have changed its current frame to
// the first one (to a visible/editable frame). The "undo"
// action doesn't restore the previously selected frame in
// observers, so at least we can restore the current_editor's
// frame.
if (current_editor)
current_editor->setFrame(oldSelectedFrame);
}
m_document->generateMaskBoundaries();
m_document->destroyExtraCel(); // Regenerate extras
// Redraw the sprite.
update_screen_for_document(m_document);
}
closeWindow(NULL); closeWindow(NULL);
} }
@ -298,8 +150,6 @@ protected:
} }
private: private:
Context* m_context;
Document* m_document;
Grid m_grid; Grid m_grid;
ComboBox m_sheetType; ComboBox m_sheetType;
Label m_columnsLabel; Label m_columnsLabel;
@ -308,22 +158,14 @@ private:
ComboBox m_exportAction; ComboBox m_exportAction;
Button m_export; Button m_export;
Button m_cancel; Button m_cancel;
}; bool m_ok;
class ExportSpriteSheetCommand : public Command {
public:
ExportSpriteSheetCommand();
Command* clone() { return new ExportSpriteSheetCommand(*this); }
protected:
virtual bool onEnabled(Context* context) OVERRIDE;
virtual void onExecute(Context* context) OVERRIDE;
}; };
ExportSpriteSheetCommand::ExportSpriteSheetCommand() ExportSpriteSheetCommand::ExportSpriteSheetCommand()
: Command("ExportSpriteSheet", : Command("ExportSpriteSheet",
"Export Sprite Sheet", "Export Sprite Sheet",
CmdRecordableFlag) CmdRecordableFlag)
, m_useUI(true)
{ {
} }
@ -334,8 +176,203 @@ bool ExportSpriteSheetCommand::onEnabled(Context* context)
void ExportSpriteSheetCommand::onExecute(Context* context) void ExportSpriteSheetCommand::onExecute(Context* context)
{ {
base::UniquePtr<Window> window(new ExportSpriteSheetWindow(context)); if (m_useUI) {
window->openWindowInForeground(); base::UniquePtr<ExportSpriteSheetWindow> window(new ExportSpriteSheetWindow(context));
window->openWindowInForeground();
if (!window->ok())
return;
setType(window->spriteSheetType());
setAction(window->exportAction());
setColumns(window->columns());
}
Document* document(context->getActiveDocument());
Sprite* sprite = document->getSprite();
FrameNumber nframes = sprite->getTotalFrames();
int columns;
switch (m_type) {
case HorizontalStrip:
columns = nframes;
break;
case VerticalStrip:
columns = 1;
break;
case Matrix:
columns = m_columns;
break;
}
columns = MID(1, columns, nframes);
int sheet_w = sprite->getWidth()*columns;
int sheet_h = sprite->getHeight()*((nframes/columns)+((nframes%columns)>0?1:0));
base::UniquePtr<Image> resultImage(Image::create(sprite->getPixelFormat(), sheet_w, sheet_h));
base::UniquePtr<Image> tempImage(Image::create(sprite->getPixelFormat(), sprite->getWidth(), sprite->getHeight()));
raster::clear_image(resultImage, 0);
int column = 0, row = 0;
for (FrameNumber frame(0); frame<nframes; ++frame) {
// TODO "tempImage" could not be necessary if we could specify
// destination clipping bounds in Sprite::render() function.
tempImage->clear(0);
sprite->render(tempImage, 0, 0, frame);
resultImage->copy(tempImage, column*sprite->getWidth(), row*sprite->getHeight());
if (++column >= columns) {
column = 0;
++row;
}
}
// Store the frame in the current editor so we can restore it
// after change and restore the setTotalFrames() number.
FrameNumber oldSelectedFrame = (current_editor ? current_editor->getFrame():
FrameNumber(0));
{
// The following steps modify the sprite, so we wrap all
// operations in a undo-transaction.
ContextWriter writer(context);
UndoTransaction undoTransaction(writer.context(), "Export Sprite Sheet", undo::ModifyDocument);
DocumentApi api = document->getApi();
// Add the layer in the sprite.
LayerImage* resultLayer = api.newLayer(sprite);
// Add the image into the sprite's stock
int indexInStock = api.addImageInStock(sprite, resultImage);
resultImage.release();
// Create the cel.
base::UniquePtr<Cel> resultCel(new Cel(FrameNumber(0), indexInStock));
// Add the cel in the layer.
api.addCel(resultLayer, resultCel);
resultCel.release();
// Copy the list of layers (because we will modify it in the iteration).
LayerList layers = sprite->getFolder()->getLayersList();
// Remove all other layers
for (LayerIterator it=layers.begin(), end=layers.end(); it!=end; ++it) {
if (*it != resultLayer)
api.removeLayer(*it);
}
// Change the number of frames (just one, the sprite sheet). As
// we are using the observable API, all DocumentView will change
// its current frame to frame 1. We'll try to restore the
// selected frame for the current_editor later.
api.setTotalFrames(sprite, FrameNumber(1));
// Set the size of the sprite to the tile size.
api.setSpriteSize(sprite, sheet_w, sheet_h);
undoTransaction.commit();
// Draw the document with the new dimensions in the screen.
update_screen_for_document(document);
}
// This flag indicates if we've to undo the last action (so we
// back to the original sprite dimensions).
bool undo = false;
Params params;
if (!m_filename.empty())
params.set("filename", m_filename.c_str());
SaveFileBaseCommand* command = NULL;
// Do the "Export Action"
switch (m_action) {
case SaveCopyAs:
command = static_cast<SaveFileBaseCommand*>(CommandsModule::instance()
->getCommandByName(CommandId::SaveFileCopyAs));
context->executeCommand(command, &params);
// Always go back, as we are using "Save Copy As", so the user
// wants to continue editing the original sprite.
undo = true;
break;
case SaveAs:
command = static_cast<SaveFileBaseCommand*>(CommandsModule::instance()
->getCommandByName(CommandId::SaveFileAs));
context->executeCommand(command, &params);
// If the command was cancelled, we go back to the original
// state, if the sprite sheet was saved then we don't undo
// because the user wants to edit the sprite sheet.
undo = (document->isModified());
break;
case Save:
command = static_cast<SaveFileBaseCommand*>(CommandsModule::instance()
->getCommandByName(CommandId::SaveFile));
context->executeCommand(command, &params);
// Same case as "Save As"
undo = (document->isModified());
break;
case DoNotSave:
// Do not undo as the user wants to edit the sprite sheet
// before to save the file.
undo = false;
break;
}
// Set export data
if (command != NULL) {
doc::ExportData::Type type;
switch (m_type) {
case ExportSpriteSheetCommand::HorizontalStrip:
type = doc::ExportData::HorizontalStrip;
break;
case ExportSpriteSheetCommand::VerticalStrip:
type = doc::ExportData::VerticalStrip;
break;
case ExportSpriteSheetCommand::Matrix:
type = doc::ExportData::Matrix;
break;
}
doc::ExportDataPtr data(new doc::ExportData);
data->setType(type);
data->setColumns(columns);
data->setFilename(command->selectedFilename());
document->setExportData(data);
}
// Undo the sprite sheet conversion
if (undo) {
if (document->getUndo()->canUndo()) {
document->getUndo()->doUndo();
// We've to restore the previously selected frame. As we've
// called setTotalFrames(), all document observers
// (current_editor included) have changed its current frame to
// the first one (to a visible/editable frame). The "undo"
// action doesn't restore the previously selected frame in
// observers, so at least we can restore the current_editor's
// frame.
if (current_editor)
current_editor->setFrame(oldSelectedFrame);
}
document->generateMaskBoundaries();
document->destroyExtraCel(); // Regenerate extras
// Redraw the sprite.
update_screen_for_document(document);
}
} }
Command* CommandFactory::createExportSpriteSheetCommand() Command* CommandFactory::createExportSpriteSheetCommand()

View File

@ -0,0 +1,60 @@
/* Aseprite
* Copyright (C) 2001-2014 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_COMMANDS_CMD_EXPORT_SPRITE_SHEET_H_INCLUDED
#define APP_COMMANDS_CMD_EXPORT_SPRITE_SHEET_H_INCLUDED
#include "app/commands/command.h"
#include "base/compiler_specific.h"
#include <string>
namespace app {
class ExportSpriteSheetCommand : public Command {
public:
enum SpriteSheetType { HorizontalStrip, VerticalStrip, Matrix };
enum ExportAction { SaveCopyAs, SaveAs, Save, DoNotSave };
ExportSpriteSheetCommand();
Command* clone() const OVERRIDE { return new ExportSpriteSheetCommand(*this); }
SpriteSheetType type() const { return m_type; }
ExportAction action() const { return m_action; }
void setUseUI(bool useUI) { m_useUI = useUI; }
void setType(SpriteSheetType type) { m_type = type; }
void setAction(ExportAction action) { m_action = action; }
void setColumns(int columns) { m_columns = columns; }
void setFileName(const std::string& filename) { m_filename = filename; }
protected:
virtual bool onEnabled(Context* context) OVERRIDE;
virtual void onExecute(Context* context) OVERRIDE;
private:
bool m_useUI;
SpriteSheetType m_type;
ExportAction m_action;
int m_columns;
std::string m_filename;
};
} // namespace app
#endif

View File

@ -0,0 +1,98 @@
/* Aseprite
* Copyright (C) 2001-2013 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/commands/cmd_export_sprite_sheet.h"
#include "app/commands/command.h"
#include "app/commands/commands.h"
#include "app/commands/params.h"
#include "app/context.h"
#include "app/context_access.h"
#include "base/compiler_specific.h"
namespace app {
class RepeatLastExportCommand : public Command {
public:
RepeatLastExportCommand();
Command* clone() { return new RepeatLastExportCommand(*this); }
protected:
virtual bool onEnabled(Context* context) OVERRIDE;
virtual void onExecute(Context* context) OVERRIDE;
};
RepeatLastExportCommand::RepeatLastExportCommand()
: Command("RepeatLastExport",
"Repeat Last Export",
CmdRecordableFlag)
{
}
bool RepeatLastExportCommand::onEnabled(Context* context)
{
return context->checkFlags(ContextFlags::ActiveDocumentIsWritable);
}
void RepeatLastExportCommand::onExecute(Context* context)
{
base::UniquePtr<ExportSpriteSheetCommand> command(
static_cast<ExportSpriteSheetCommand*>(
CommandsModule::instance()->getCommandByName(CommandId::ExportSpriteSheet)->clone()));
{
const ContextReader reader(context);
const Document* document(reader.document());
doc::ExportDataPtr data = document->exportData();
if (data != NULL) {
command->setUseUI(false);
ExportSpriteSheetCommand::SpriteSheetType type;
switch (data->type()) {
case doc::ExportData::None: return;
case doc::ExportData::HorizontalStrip:
type = ExportSpriteSheetCommand::HorizontalStrip;
break;
case doc::ExportData::VerticalStrip:
type = ExportSpriteSheetCommand::VerticalStrip;
break;
case doc::ExportData::Matrix:
type = ExportSpriteSheetCommand::Matrix;
break;
}
command->setType(type);
command->setAction(ExportSpriteSheetCommand::SaveCopyAs);
command->setColumns(data->columns());
command->setFileName(data->filename());
}
}
context->executeCommand(command);
}
Command* CommandFactory::createRepeatLastExportCommand()
{
return new RepeatLastExportCommand;
}
} // namespace app

View File

@ -22,6 +22,7 @@
#include "app/app.h" #include "app/app.h"
#include "app/commands/command.h" #include "app/commands/command.h"
#include "app/commands/params.h"
#include "app/console.h" #include "app/console.h"
#include "app/context_access.h" #include "app/context_access.h"
#include "app/file/file.h" #include "app/file/file.h"
@ -101,105 +102,122 @@ static void save_document_in_background(Document* document, bool mark_as_saved)
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
static bool confirm_readonly(const base::string& filename) class SaveFileBaseCommand : public Command {
{ public:
if (!base::has_readonly_attr(filename)) SaveFileBaseCommand(const char* short_name, const char* friendly_name, CommandFlags flags)
return true; : Command(short_name, friendly_name, flags) {
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;
}
static void save_as_dialog(const ContextReader& reader, const char* dlg_title, bool mark_as_saved) protected:
{ void onLoadParams(Params* params) OVERRIDE {
const Document* document = reader.document(); m_filename = params->get("filename");
char exts[4096]; }
base::string filename;
base::string newfilename;
int ret;
filename = document->getFilename(); // Returns true if there is a current sprite to save.
get_writable_extensions(exts, sizeof(exts)); // [main thread]
bool onEnabled(Context* context) OVERRIDE {
return context->checkFlags(ContextFlags::ActiveDocumentIsWritable);
}
for (;;) { void saveAsDialog(const ContextReader& reader, const char* dlgTitle, bool markAsSaved)
newfilename = app::show_file_selector(dlg_title, filename, exts); {
if (newfilename.empty()) const Document* document = reader.document();
return; base::string filename;
filename = newfilename; if (!m_filename.empty()) {
filename = m_filename;
}
else {
filename = document->getFilename();
// Ask if the user wants overwrite the existent file. char exts[4096];
if (base::file_exists(filename)) { get_writable_extensions(exts, sizeof(exts));
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. for (;;) {
if (ret == 1) { base::string newfilename = app::show_file_selector(dlgTitle, filename, exts);
if (!confirm_readonly(filename)) if (newfilename.empty())
ret = 2; // Select file again. return;
filename = newfilename;
// Ask if the user wants overwrite the existent file.
int ret = 0;
if (base::file_exists(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 else
break; break;
// "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
} }
} }
else
break;
// "yes": we must continue with the operation... {
if (ret == 1) { ContextWriter writer(reader);
break; Document* documentWriter = writer.document();
// Change the document file name
documentWriter->setFilename(filename.c_str());
m_selectedFilename = filename;
// Save the document
save_document_in_background(documentWriter, markAsSaved);
update_screen_for_document(documentWriter);
} }
// "cancel" or <esc> per example: we back doing nothing
else if (ret != 2)
return;
// "no": we must back to select other file-name
} }
static bool confirmReadonly(const std::string& filename)
{ {
ContextWriter writer(reader); if (!base::has_readonly_attr(filename))
Document* documentWriter = writer.document(); return true;
// Change the document file name int ret = ui::Alert::show("Warning<<The file is read-only, do you really want to overwrite it?<<%s||&Yes||&No",
documentWriter->setFilename(filename.c_str()); base::get_file_name(filename).c_str());
// Save the document if (ret == 1) {
save_document_in_background(documentWriter, mark_as_saved); base::remove_readonly_attr(filename);
return true;
update_screen_for_document(documentWriter); }
else
return false;
} }
}
class SaveFileCommand : public Command { std::string m_filename;
std::string m_selectedFilename;
};
class SaveFileCommand : public SaveFileBaseCommand {
public: public:
SaveFileCommand(); SaveFileCommand();
Command* clone() { return new SaveFileCommand(*this); } Command* clone() { return new SaveFileCommand(*this); }
protected: protected:
bool onEnabled(Context* context);
void onExecute(Context* context); void onExecute(Context* context);
}; };
SaveFileCommand::SaveFileCommand() SaveFileCommand::SaveFileCommand()
: Command("SaveFile", : SaveFileBaseCommand("SaveFile", "Save File", CmdRecordableFlag)
"Save File",
CmdRecordableFlag)
{ {
} }
// Returns true if there is a current sprite to save.
// [main thread]
bool SaveFileCommand::onEnabled(Context* context)
{
return context->checkFlags(ContextFlags::ActiveDocumentIsWritable);
}
// Saves the active document in a file. // Saves the active document in a file.
// [main thread] // [main thread]
void SaveFileCommand::onExecute(Context* context) void SaveFileCommand::onExecute(Context* context)
@ -213,7 +231,7 @@ void SaveFileCommand::onExecute(Context* context)
ContextWriter writer(reader); ContextWriter writer(reader);
Document* documentWriter = writer.document(); Document* documentWriter = writer.document();
if (!confirm_readonly(documentWriter->getFilename())) if (!confirmReadonly(documentWriter->getFilename()))
return; return;
save_document_in_background(documentWriter, true); save_document_in_background(documentWriter, true);
@ -223,60 +241,44 @@ void SaveFileCommand::onExecute(Context* context)
// save-as dialog to the user to select for first time the file-name // save-as dialog to the user to select for first time the file-name
// for this document. // for this document.
else { else {
save_as_dialog(reader, "Save File", true); saveAsDialog(reader, "Save File", true);
} }
} }
class SaveFileAsCommand : public Command { class SaveFileAsCommand : public SaveFileBaseCommand {
public: public:
SaveFileAsCommand(); SaveFileAsCommand();
Command* clone() { return new SaveFileAsCommand(*this); } Command* clone() { return new SaveFileAsCommand(*this); }
protected: protected:
bool onEnabled(Context* context);
void onExecute(Context* context); void onExecute(Context* context);
}; };
SaveFileAsCommand::SaveFileAsCommand() SaveFileAsCommand::SaveFileAsCommand()
: Command("SaveFileAs", : SaveFileBaseCommand("SaveFileAs", "Save File As", CmdRecordableFlag)
"Save File As",
CmdRecordableFlag)
{ {
} }
bool SaveFileAsCommand::onEnabled(Context* context)
{
return context->checkFlags(ContextFlags::ActiveDocumentIsWritable);
}
void SaveFileAsCommand::onExecute(Context* context) void SaveFileAsCommand::onExecute(Context* context)
{ {
const ContextReader reader(context); const ContextReader reader(context);
save_as_dialog(reader, "Save As", true); saveAsDialog(reader, "Save As", true);
} }
class SaveFileCopyAsCommand : public Command { class SaveFileCopyAsCommand : public SaveFileBaseCommand {
public: public:
SaveFileCopyAsCommand(); SaveFileCopyAsCommand();
Command* clone() { return new SaveFileCopyAsCommand(*this); } Command* clone() { return new SaveFileCopyAsCommand(*this); }
protected: protected:
bool onEnabled(Context* context);
void onExecute(Context* context); void onExecute(Context* context);
}; };
SaveFileCopyAsCommand::SaveFileCopyAsCommand() SaveFileCopyAsCommand::SaveFileCopyAsCommand()
: Command("SaveFileCopyAs", : SaveFileBaseCommand("SaveFileCopyAs", "Save File Copy As", CmdRecordableFlag)
"Save File Copy As",
CmdRecordableFlag)
{ {
} }
bool SaveFileCopyAsCommand::onEnabled(Context* context)
{
return context->checkFlags(ContextFlags::ActiveDocumentIsWritable);
}
void SaveFileCopyAsCommand::onExecute(Context* context) void SaveFileCopyAsCommand::onExecute(Context* context)
{ {
const ContextReader reader(context); const ContextReader reader(context);
@ -284,7 +286,7 @@ void SaveFileCopyAsCommand::onExecute(Context* context)
base::string old_filename = document->getFilename(); base::string old_filename = document->getFilename();
// show "Save As" dialog // show "Save As" dialog
save_as_dialog(reader, "Save Copy As", false); saveAsDialog(reader, "Save Copy As", false);
// Restore the file name. // Restore the file name.
{ {

View File

@ -0,0 +1,52 @@
/* Aseprite
* Copyright (C) 2001-2014 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_COMMANDS_CMD_SAVE_FILE_H_INCLUDED
#define APP_COMMANDS_CMD_SAVE_FILE_H_INCLUDED
#include "app/commands/command.h"
#include "base/compiler_specific.h"
#include <string>
namespace app {
class ContextReader;
class SaveFileBaseCommand : public Command {
public:
SaveFileBaseCommand(const char* shortName, const char* friendlyName, CommandFlags flags);
std::string selectedFilename() const {
return m_selectedFilename;
}
protected:
void onLoadParams(Params* params) OVERRIDE;
bool onEnabled(Context* context) OVERRIDE;
void saveAsDialog(const ContextReader& reader, const char* dlgTitle, bool markAsSaved);
static bool confirmReadonly(const std::string& filename);
std::string m_filename;
std::string m_selectedFilename;
};
} // namespace app
#endif

View File

@ -87,6 +87,7 @@ FOR_EACH_COMMAND(Refresh)
FOR_EACH_COMMAND(RemoveCel) FOR_EACH_COMMAND(RemoveCel)
FOR_EACH_COMMAND(RemoveFrame) FOR_EACH_COMMAND(RemoveFrame)
FOR_EACH_COMMAND(RemoveLayer) FOR_EACH_COMMAND(RemoveLayer)
FOR_EACH_COMMAND(RepeatLastExport)
FOR_EACH_COMMAND(ReplaceColor) FOR_EACH_COMMAND(ReplaceColor)
FOR_EACH_COMMAND(ReselectMask) FOR_EACH_COMMAND(ReselectMask)
FOR_EACH_COMMAND(RotateCanvas) FOR_EACH_COMMAND(RotateCanvas)

View File

@ -98,6 +98,17 @@ namespace app {
void addSprite(Sprite* sprite); void addSprite(Sprite* sprite);
//////////////////////////////////////////////////////////////////////
// Export data
doc::ExportDataPtr exportData() const {
return m_document.exportData();
}
void setExportData(const doc::ExportDataPtr& data) {
return m_document.setExportData(data);
}
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Notifications // Notifications

View File

@ -10,6 +10,8 @@
#include "doc/document.h" #include "doc/document.h"
#include "doc/export_data.h"
namespace doc { namespace doc {
Document::Document() Document::Document()
@ -21,4 +23,9 @@ void Document::setFilename(const std::string& filename)
m_filename = filename; m_filename = filename;
} }
void Document::setExportData(const ExportDataPtr& data)
{
m_exportData = data;
}
} // namespace doc } // namespace doc

View File

@ -10,21 +10,28 @@
#include <string> #include <string>
#include "doc/object.h" #include "doc/object.h"
#include "doc/export_data.h"
namespace doc { namespace doc {
class ExportData;
class Document : public Object { class Document : public Object {
public: public:
Document(); Document();
const std::string& filename() const { return m_filename; } const std::string& filename() const { return m_filename; }
ExportDataPtr exportData() const { return m_exportData; }
void setFilename(const std::string& filename); void setFilename(const std::string& filename);
void setExportData(const ExportDataPtr& data);
private: private:
// Document's file name. From where it was loaded, where it is // Document's file name. From where it was loaded, where it is
// saved. // saved.
std::string m_filename; std::string m_filename;
ExportDataPtr m_exportData;
}; };
} // namespace doc } // namespace doc

48
src/doc/export_data.h Normal file
View File

@ -0,0 +1,48 @@
// Aseprite Document Library
// Copyright (c) 2014 David Capello
//
// This source file is distributed under the terms of the MIT license,
// please read LICENSE.txt for more information.
#ifndef DOC_EXPORT_DATA_H_INCLUDED
#define DOC_EXPORT_DATA_H_INCLUDED
#include "base/shared_ptr.h"
#include "gfx/rect.h"
#include <string>
namespace doc {
class ExportData {
public:
enum Type {
None,
HorizontalStrip,
VerticalStrip,
Matrix
};
ExportData() : m_type(None) {
}
Type type() const { return m_type; }
int columns() const { return m_columns; }
const std::string& filename() const { return m_filename; }
void setType(Type type) { m_type = type; }
void setColumns(int columns) { m_columns = columns; }
void setFilename(const std::string& filename) {
m_filename = filename;
}
private:
Type m_type;
int m_columns;
std::string m_filename;
};
typedef SharedPtr<ExportData> ExportDataPtr;
} // namespace doc
#endif