mirror of
https://github.com/aseprite/aseprite.git
synced 2024-09-02 04:59:24 +00:00
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:
parent
395be62b03
commit
b16bf981d0
@ -14,6 +14,7 @@
|
||||
<key command="SaveFileCopyAs" shortcut="Ctrl+Shift+C" />
|
||||
<key command="CloseFile" shortcut="Ctrl+W" />
|
||||
<key command="CloseAllFiles" shortcut="Ctrl+Shift+W" />
|
||||
<key command="RepeatLastExport" shortcut="Ctrl+Shift+X" />
|
||||
<key command="AdvancedMode" shortcut="F11" />
|
||||
<key command="DeveloperConsole" shortcut="F12" />
|
||||
<key command="Exit" shortcut="Alt+F4" />
|
||||
@ -229,6 +230,7 @@
|
||||
<separator />
|
||||
<item command="ImportSpriteSheet" text="&Import Sprite Sheet" />
|
||||
<item command="ExportSpriteSheet" text="&Export Sprite Sheet" />
|
||||
<item command="RepeatLastExport" text="Repeat &Last Export" />
|
||||
<separator />
|
||||
<item command="Exit" text="E&xit" />
|
||||
</menu>
|
||||
|
@ -68,6 +68,7 @@ add_library(app-lib
|
||||
commands/cmd_remove_cel.cpp
|
||||
commands/cmd_remove_frame.cpp
|
||||
commands/cmd_remove_layer.cpp
|
||||
commands/cmd_repeat_last_export.cpp
|
||||
commands/cmd_reselect_mask.cpp
|
||||
commands/cmd_rotate_canvas.cpp
|
||||
commands/cmd_save_file.cpp
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* 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
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -20,8 +20,12 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/commands/cmd_export_sprite_sheet.h"
|
||||
|
||||
#include "app/commands/cmd_save_file.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 "app/document.h"
|
||||
@ -50,20 +54,19 @@ namespace app {
|
||||
using namespace ui;
|
||||
|
||||
class ExportSpriteSheetWindow : public Window {
|
||||
enum SpriteSheetType { HorizontalStrip, VerticalStrip, Matrix };
|
||||
enum ExportAction { SaveCopyAs, SaveAs, Save, DoNotSave };
|
||||
|
||||
public:
|
||||
typedef ExportSpriteSheetCommand::SpriteSheetType SpriteSheetType;
|
||||
typedef ExportSpriteSheetCommand::ExportAction ExportAction;
|
||||
|
||||
ExportSpriteSheetWindow(Context* context)
|
||||
: Window(WithTitleBar, "Export Sprite Sheet")
|
||||
, m_context(context)
|
||||
, m_document(context->getActiveDocument())
|
||||
, m_grid(4, false)
|
||||
, m_columnsLabel("Columns:")
|
||||
, m_columns(4, "4")
|
||||
, m_exportActionLabel("Export Action:")
|
||||
, m_export("Export")
|
||||
, m_cancel("Cancel")
|
||||
, m_export("&Export")
|
||||
, m_cancel("&Cancel")
|
||||
, m_ok(false)
|
||||
{
|
||||
m_sheetType.addItem("Horizontal Strip");
|
||||
m_sheetType.addItem("Vertical Strip");
|
||||
@ -100,8 +103,20 @@ public:
|
||||
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:
|
||||
@ -110,7 +125,7 @@ protected:
|
||||
{
|
||||
bool state = false;
|
||||
switch (m_sheetType.getSelectedItemIndex()) {
|
||||
case Matrix:
|
||||
case ExportSpriteSheetCommand::Matrix:
|
||||
state = true;
|
||||
break;
|
||||
}
|
||||
@ -125,170 +140,7 @@ protected:
|
||||
|
||||
void onExport()
|
||||
{
|
||||
Sprite* sprite = m_document->getSprite();
|
||||
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);
|
||||
}
|
||||
|
||||
m_ok = true;
|
||||
closeWindow(NULL);
|
||||
}
|
||||
|
||||
@ -298,8 +150,6 @@ protected:
|
||||
}
|
||||
|
||||
private:
|
||||
Context* m_context;
|
||||
Document* m_document;
|
||||
Grid m_grid;
|
||||
ComboBox m_sheetType;
|
||||
Label m_columnsLabel;
|
||||
@ -308,22 +158,14 @@ private:
|
||||
ComboBox m_exportAction;
|
||||
Button m_export;
|
||||
Button m_cancel;
|
||||
};
|
||||
|
||||
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;
|
||||
bool m_ok;
|
||||
};
|
||||
|
||||
ExportSpriteSheetCommand::ExportSpriteSheetCommand()
|
||||
: Command("ExportSpriteSheet",
|
||||
"Export Sprite Sheet",
|
||||
CmdRecordableFlag)
|
||||
, m_useUI(true)
|
||||
{
|
||||
}
|
||||
|
||||
@ -334,8 +176,203 @@ bool ExportSpriteSheetCommand::onEnabled(Context* context)
|
||||
|
||||
void ExportSpriteSheetCommand::onExecute(Context* context)
|
||||
{
|
||||
base::UniquePtr<Window> window(new ExportSpriteSheetWindow(context));
|
||||
window->openWindowInForeground();
|
||||
if (m_useUI) {
|
||||
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, ¶ms);
|
||||
|
||||
// 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, ¶ms);
|
||||
|
||||
// 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, ¶ms);
|
||||
|
||||
// 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()
|
||||
|
60
src/app/commands/cmd_export_sprite_sheet.h
Normal file
60
src/app/commands/cmd_export_sprite_sheet.h
Normal 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
|
98
src/app/commands/cmd_repeat_last_export.cpp
Normal file
98
src/app/commands/cmd_repeat_last_export.cpp
Normal 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
|
@ -22,6 +22,7 @@
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/commands/command.h"
|
||||
#include "app/commands/params.h"
|
||||
#include "app/console.h"
|
||||
#include "app/context_access.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)
|
||||
{
|
||||
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());
|
||||
|
||||
if (ret == 1) {
|
||||
base::remove_readonly_attr(filename);
|
||||
return true;
|
||||
class SaveFileBaseCommand : public Command {
|
||||
public:
|
||||
SaveFileBaseCommand(const char* short_name, const char* friendly_name, CommandFlags flags)
|
||||
: Command(short_name, friendly_name, flags) {
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
static void save_as_dialog(const ContextReader& reader, const char* dlg_title, bool mark_as_saved)
|
||||
{
|
||||
const Document* document = reader.document();
|
||||
char exts[4096];
|
||||
base::string filename;
|
||||
base::string newfilename;
|
||||
int ret;
|
||||
protected:
|
||||
void onLoadParams(Params* params) OVERRIDE {
|
||||
m_filename = params->get("filename");
|
||||
}
|
||||
|
||||
filename = document->getFilename();
|
||||
get_writable_extensions(exts, sizeof(exts));
|
||||
// Returns true if there is a current sprite to save.
|
||||
// [main thread]
|
||||
bool onEnabled(Context* context) OVERRIDE {
|
||||
return context->checkFlags(ContextFlags::ActiveDocumentIsWritable);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
newfilename = app::show_file_selector(dlg_title, filename, exts);
|
||||
if (newfilename.empty())
|
||||
return;
|
||||
void saveAsDialog(const ContextReader& reader, const char* dlgTitle, bool markAsSaved)
|
||||
{
|
||||
const Document* document = reader.document();
|
||||
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.
|
||||
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());
|
||||
char exts[4096];
|
||||
get_writable_extensions(exts, sizeof(exts));
|
||||
|
||||
// Check for read-only attribute.
|
||||
if (ret == 1) {
|
||||
if (!confirm_readonly(filename))
|
||||
ret = 2; // Select file again.
|
||||
for (;;) {
|
||||
base::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::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
|
||||
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) {
|
||||
break;
|
||||
{
|
||||
ContextWriter writer(reader);
|
||||
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);
|
||||
Document* documentWriter = writer.document();
|
||||
if (!base::has_readonly_attr(filename))
|
||||
return true;
|
||||
|
||||
// Change the document file name
|
||||
documentWriter->setFilename(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());
|
||||
|
||||
// Save the document
|
||||
save_document_in_background(documentWriter, mark_as_saved);
|
||||
|
||||
update_screen_for_document(documentWriter);
|
||||
if (ret == 1) {
|
||||
base::remove_readonly_attr(filename);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class SaveFileCommand : public Command {
|
||||
std::string m_filename;
|
||||
std::string m_selectedFilename;
|
||||
};
|
||||
|
||||
class SaveFileCommand : public SaveFileBaseCommand {
|
||||
public:
|
||||
SaveFileCommand();
|
||||
Command* clone() { return new SaveFileCommand(*this); }
|
||||
|
||||
protected:
|
||||
bool onEnabled(Context* context);
|
||||
void onExecute(Context* context);
|
||||
};
|
||||
|
||||
SaveFileCommand::SaveFileCommand()
|
||||
: Command("SaveFile",
|
||||
"Save File",
|
||||
CmdRecordableFlag)
|
||||
: SaveFileBaseCommand("SaveFile", "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.
|
||||
// [main thread]
|
||||
void SaveFileCommand::onExecute(Context* context)
|
||||
@ -213,7 +231,7 @@ void SaveFileCommand::onExecute(Context* context)
|
||||
ContextWriter writer(reader);
|
||||
Document* documentWriter = writer.document();
|
||||
|
||||
if (!confirm_readonly(documentWriter->getFilename()))
|
||||
if (!confirmReadonly(documentWriter->getFilename()))
|
||||
return;
|
||||
|
||||
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
|
||||
// for this document.
|
||||
else {
|
||||
save_as_dialog(reader, "Save File", true);
|
||||
saveAsDialog(reader, "Save File", true);
|
||||
}
|
||||
}
|
||||
|
||||
class SaveFileAsCommand : public Command {
|
||||
class SaveFileAsCommand : public SaveFileBaseCommand {
|
||||
public:
|
||||
SaveFileAsCommand();
|
||||
Command* clone() { return new SaveFileAsCommand(*this); }
|
||||
|
||||
protected:
|
||||
bool onEnabled(Context* context);
|
||||
void onExecute(Context* context);
|
||||
};
|
||||
|
||||
SaveFileAsCommand::SaveFileAsCommand()
|
||||
: Command("SaveFileAs",
|
||||
"Save File As",
|
||||
CmdRecordableFlag)
|
||||
: SaveFileBaseCommand("SaveFileAs", "Save File As", CmdRecordableFlag)
|
||||
{
|
||||
}
|
||||
|
||||
bool SaveFileAsCommand::onEnabled(Context* context)
|
||||
{
|
||||
return context->checkFlags(ContextFlags::ActiveDocumentIsWritable);
|
||||
}
|
||||
|
||||
void SaveFileAsCommand::onExecute(Context* 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:
|
||||
SaveFileCopyAsCommand();
|
||||
Command* clone() { return new SaveFileCopyAsCommand(*this); }
|
||||
|
||||
protected:
|
||||
bool onEnabled(Context* context);
|
||||
void onExecute(Context* context);
|
||||
};
|
||||
|
||||
SaveFileCopyAsCommand::SaveFileCopyAsCommand()
|
||||
: Command("SaveFileCopyAs",
|
||||
"Save File Copy As",
|
||||
CmdRecordableFlag)
|
||||
: SaveFileBaseCommand("SaveFileCopyAs", "Save File Copy As", CmdRecordableFlag)
|
||||
{
|
||||
}
|
||||
|
||||
bool SaveFileCopyAsCommand::onEnabled(Context* context)
|
||||
{
|
||||
return context->checkFlags(ContextFlags::ActiveDocumentIsWritable);
|
||||
}
|
||||
|
||||
void SaveFileCopyAsCommand::onExecute(Context* context)
|
||||
{
|
||||
const ContextReader reader(context);
|
||||
@ -284,7 +286,7 @@ void SaveFileCopyAsCommand::onExecute(Context* context)
|
||||
base::string old_filename = document->getFilename();
|
||||
|
||||
// show "Save As" dialog
|
||||
save_as_dialog(reader, "Save Copy As", false);
|
||||
saveAsDialog(reader, "Save Copy As", false);
|
||||
|
||||
// Restore the file name.
|
||||
{
|
||||
|
52
src/app/commands/cmd_save_file.h
Normal file
52
src/app/commands/cmd_save_file.h
Normal 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
|
@ -87,6 +87,7 @@ FOR_EACH_COMMAND(Refresh)
|
||||
FOR_EACH_COMMAND(RemoveCel)
|
||||
FOR_EACH_COMMAND(RemoveFrame)
|
||||
FOR_EACH_COMMAND(RemoveLayer)
|
||||
FOR_EACH_COMMAND(RepeatLastExport)
|
||||
FOR_EACH_COMMAND(ReplaceColor)
|
||||
FOR_EACH_COMMAND(ReselectMask)
|
||||
FOR_EACH_COMMAND(RotateCanvas)
|
||||
|
@ -98,6 +98,17 @@ namespace app {
|
||||
|
||||
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
|
||||
|
||||
|
@ -10,6 +10,8 @@
|
||||
|
||||
#include "doc/document.h"
|
||||
|
||||
#include "doc/export_data.h"
|
||||
|
||||
namespace doc {
|
||||
|
||||
Document::Document()
|
||||
@ -21,4 +23,9 @@ void Document::setFilename(const std::string& filename)
|
||||
m_filename = filename;
|
||||
}
|
||||
|
||||
void Document::setExportData(const ExportDataPtr& data)
|
||||
{
|
||||
m_exportData = data;
|
||||
}
|
||||
|
||||
} // namespace doc
|
||||
|
@ -10,21 +10,28 @@
|
||||
#include <string>
|
||||
|
||||
#include "doc/object.h"
|
||||
#include "doc/export_data.h"
|
||||
|
||||
namespace doc {
|
||||
|
||||
class ExportData;
|
||||
|
||||
class Document : public Object {
|
||||
public:
|
||||
Document();
|
||||
|
||||
const std::string& filename() const { return m_filename; }
|
||||
ExportDataPtr exportData() const { return m_exportData; }
|
||||
|
||||
void setFilename(const std::string& filename);
|
||||
void setExportData(const ExportDataPtr& data);
|
||||
|
||||
private:
|
||||
// Document's file name. From where it was loaded, where it is
|
||||
// saved.
|
||||
std::string m_filename;
|
||||
|
||||
ExportDataPtr m_exportData;
|
||||
};
|
||||
|
||||
} // namespace doc
|
||||
|
48
src/doc/export_data.h
Normal file
48
src/doc/export_data.h
Normal 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
|
Loading…
Reference in New Issue
Block a user