mirror of
https://github.com/aseprite/aseprite.git
synced 2025-04-16 23:42:57 +00:00
In this way we know at the moment of the creation of the window if it will need the close button. Issue #280. https://github.com/aseprite/aseprite/pull/6
376 lines
11 KiB
C++
376 lines
11 KiB
C++
/* 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/command.h"
|
|
#include "app/commands/commands.h"
|
|
#include "app/commands/params.h"
|
|
#include "app/context.h"
|
|
#include "app/context_access.h"
|
|
#include "app/document_access.h"
|
|
#include "app/document_api.h"
|
|
#include "app/ini_file.h"
|
|
#include "app/modules/editors.h"
|
|
#include "app/modules/gui.h"
|
|
#include "app/modules/palettes.h"
|
|
#include "app/ui/drop_down_button.h"
|
|
#include "app/ui/editor/editor.h"
|
|
#include "app/ui/editor/editor_decorator.h"
|
|
#include "app/ui/editor/select_box_state.h"
|
|
#include "app/ui/editor/standby_state.h"
|
|
#include "app/ui/workspace.h"
|
|
#include "app/undo_transaction.h"
|
|
#include "base/bind.h"
|
|
#include "raster/cel.h"
|
|
#include "raster/image.h"
|
|
#include "raster/layer.h"
|
|
#include "raster/palette.h"
|
|
#include "raster/primitives.h"
|
|
#include "raster/sprite.h"
|
|
#include "raster/stock.h"
|
|
#include "ui/ui.h"
|
|
|
|
#include <allegro/color.h>
|
|
|
|
namespace app {
|
|
|
|
using namespace ui;
|
|
|
|
class ImportSpriteSheetWindow : public Window,
|
|
public SelectBoxDelegate {
|
|
public:
|
|
ImportSpriteSheetWindow(Context* context)
|
|
: Window(WithTitleBar, "Import Sprite Sheet")
|
|
, m_context(context)
|
|
, m_document(NULL)
|
|
, m_grid(4, false)
|
|
, m_selectFile("Select File")
|
|
, m_x(4, "0")
|
|
, m_y(4, "0")
|
|
, m_width(4, "16")
|
|
, m_height(4, "16")
|
|
, m_import("Import")
|
|
, m_cancel("Cancel")
|
|
, m_editor(NULL)
|
|
, m_fileOpened(false)
|
|
{
|
|
addChild(&m_grid);
|
|
m_grid.addChildInCell(&m_selectFile, 4, 1, 0);
|
|
m_grid.addChildInCell(new Label("X"), 1, 1, 0);
|
|
m_grid.addChildInCell(&m_x, 1, 1, 0);
|
|
m_grid.addChildInCell(new Label("Width"), 1, 1, 0);
|
|
m_grid.addChildInCell(&m_width, 1, 1, 0);
|
|
m_grid.addChildInCell(new Label("Y"), 1, 1, 0);
|
|
m_grid.addChildInCell(&m_y, 1, 1, 0);
|
|
m_grid.addChildInCell(new Label("Height"), 1, 1, 0);
|
|
m_grid.addChildInCell(&m_height, 1, 1, 0);
|
|
|
|
{
|
|
Box* hbox1 = new Box(JI_HORIZONTAL);
|
|
Box* hbox2 = new Box(JI_HORIZONTAL | JI_HOMOGENEOUS);
|
|
hbox1->addChild(new BoxFiller);
|
|
hbox1->addChild(hbox2);
|
|
hbox2->addChild(&m_import);
|
|
hbox2->addChild(&m_cancel);
|
|
jwidget_set_min_size(&m_import, 60, 0);
|
|
m_grid.addChildInCell(hbox1, 4, 1, 0);
|
|
}
|
|
|
|
m_x.EntryChange.connect(Bind<void>(&ImportSpriteSheetWindow::onEntriesChange, this));
|
|
m_y.EntryChange.connect(Bind<void>(&ImportSpriteSheetWindow::onEntriesChange, this));
|
|
m_width.EntryChange.connect(Bind<void>(&ImportSpriteSheetWindow::onEntriesChange, this));
|
|
m_height.EntryChange.connect(Bind<void>(&ImportSpriteSheetWindow::onEntriesChange, this));
|
|
|
|
m_selectFile.Click.connect(&ImportSpriteSheetWindow::onSelectFile, this);
|
|
m_selectFile.DropDownClick.connect(&ImportSpriteSheetWindow::onDropDown, this);
|
|
m_import.Click.connect(Bind<void>(&ImportSpriteSheetWindow::onImport, this));
|
|
m_cancel.Click.connect(Bind<void>(&ImportSpriteSheetWindow::onCancel, this));
|
|
}
|
|
|
|
~ImportSpriteSheetWindow()
|
|
{
|
|
releaseEditor();
|
|
}
|
|
|
|
protected:
|
|
|
|
void onSelectFile()
|
|
{
|
|
Document* oldActiveDocument = m_context->getActiveDocument();
|
|
Command* openFile = CommandsModule::instance()->getCommandByName(CommandId::OpenFile);
|
|
Params params;
|
|
params.set("filename", "");
|
|
openFile->loadParams(¶ms);
|
|
openFile->execute(m_context);
|
|
|
|
// The user have selected another document.
|
|
if (oldActiveDocument != m_context->getActiveDocument()) {
|
|
selectActiveDocument();
|
|
m_fileOpened = true;
|
|
}
|
|
}
|
|
|
|
void onDropDown()
|
|
{
|
|
SharedPtr<Menu> menu(new Menu());
|
|
MenuItem* item = new MenuItem("Use Current Sprite");
|
|
item->Click.connect(&ImportSpriteSheetWindow::onUseCurrentSprite, this);
|
|
|
|
if (m_editor || !current_editor || current_editor->getDocument() == NULL)
|
|
item->setEnabled(false);
|
|
|
|
menu->addChild(item);
|
|
|
|
const gfx::Rect& bounds = m_selectFile.getBounds();
|
|
menu->showPopup(bounds.x, bounds.y+bounds.h);
|
|
}
|
|
|
|
void onUseCurrentSprite()
|
|
{
|
|
selectActiveDocument();
|
|
m_fileOpened = false;
|
|
}
|
|
|
|
void onImport()
|
|
{
|
|
// The user don't select a sheet yet.
|
|
if (!m_document) {
|
|
Alert::show("Import Sprite Sheet<<Select a sprite first.||&Close");
|
|
return;
|
|
}
|
|
|
|
// The list of frames imported from the sheet
|
|
std::vector<Image*> animation;
|
|
|
|
try {
|
|
Sprite* sprite = m_document->getSprite();
|
|
FrameNumber currentFrame = m_context->getActiveLocation().frame();
|
|
|
|
// As first step, we cut each tile and add them into "animation" list.
|
|
for (int y=m_rect.y; y<sprite->getHeight(); y += m_rect.h) {
|
|
for (int x=m_rect.x; x<sprite->getWidth(); x += m_rect.w) {
|
|
base::UniquePtr<Image> resultImage(Image::create(sprite->getPixelFormat(), m_rect.w, m_rect.h));
|
|
|
|
// Clear the image with mask color.
|
|
raster::clear_image(resultImage, 0);
|
|
|
|
// Render the portion of sheet.
|
|
sprite->render(resultImage, -x, -y, currentFrame);
|
|
animation.push_back(resultImage);
|
|
resultImage.release();
|
|
}
|
|
}
|
|
|
|
if (animation.size() == 0) {
|
|
Alert::show("Import Sprite Sheet"
|
|
"<<The specified rectangle does not create any tile."
|
|
"<<Select a rectangle inside the sprite region."
|
|
"||&OK");
|
|
return;
|
|
}
|
|
|
|
// The following steps modify the sprite, so we wrap all
|
|
// operations in a undo-transaction.
|
|
ContextWriter writer(m_context);
|
|
UndoTransaction undoTransaction(writer.context(), "Import Sprite Sheet", undo::ModifyDocument);
|
|
DocumentApi api = m_document->getApi();
|
|
|
|
// Add the layer in the sprite.
|
|
LayerImage* resultLayer = api.newLayer(sprite);
|
|
|
|
// Add all frames+cels to the new layer
|
|
for (size_t i=0; i<animation.size(); ++i) {
|
|
int indexInStock;
|
|
|
|
// Add the image into the sprite's stock
|
|
indexInStock = api.addImageInStock(sprite, animation[i]);
|
|
animation[i] = NULL;
|
|
|
|
// Create the cel.
|
|
base::UniquePtr<Cel> resultCel(new Cel(FrameNumber(i), 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
|
|
api.setTotalFrames(sprite, FrameNumber(animation.size()));
|
|
|
|
// Set the size of the sprite to the tile size.
|
|
api.setSpriteSize(sprite, m_rect.w, m_rect.h);
|
|
|
|
undoTransaction.commit();
|
|
}
|
|
catch (...) {
|
|
for (size_t i=0; i<animation.size(); ++i)
|
|
delete animation[i];
|
|
throw;
|
|
}
|
|
|
|
update_screen_for_document(m_document);
|
|
closeWindow(NULL);
|
|
}
|
|
|
|
void onCancel()
|
|
{
|
|
closeWindow(NULL);
|
|
}
|
|
|
|
gfx::Rect getRectFromEntries() const
|
|
{
|
|
int w = m_width.getTextInt();
|
|
int h = m_height.getTextInt();
|
|
|
|
return gfx::Rect(m_x.getTextInt(),
|
|
m_y.getTextInt(),
|
|
std::max<int>(1, w),
|
|
std::max<int>(1, h));
|
|
}
|
|
|
|
void onEntriesChange()
|
|
{
|
|
m_rect = getRectFromEntries();
|
|
|
|
// Redraw new rulers position
|
|
if (m_editor) {
|
|
EditorStatePtr state = m_editor->getState();
|
|
if (SelectBoxState* boxState = dynamic_cast<SelectBoxState*>(state.get())) {
|
|
boxState->setBoxBounds(m_rect);
|
|
m_editor->invalidate();
|
|
}
|
|
}
|
|
}
|
|
|
|
virtual void onBroadcastMouseMessage(WidgetsList& targets) OVERRIDE
|
|
{
|
|
Window::onBroadcastMouseMessage(targets);
|
|
|
|
// Add the editor as receptor of mouse events too.
|
|
if (m_editor)
|
|
targets.push_back(View::getView(m_editor));
|
|
}
|
|
|
|
// SelectBoxDelegate impleentation
|
|
virtual void onChangeRectangle(const gfx::Rect& rect) OVERRIDE
|
|
{
|
|
m_rect = rect;
|
|
|
|
m_x.setTextf("%d", m_rect.x);
|
|
m_y.setTextf("%d", m_rect.y);
|
|
m_width.setTextf("%d", m_rect.w);
|
|
m_height.setTextf("%d", m_rect.h);
|
|
}
|
|
|
|
private:
|
|
void selectActiveDocument()
|
|
{
|
|
Document* oldDocument = m_document;
|
|
m_document = m_context->getActiveDocument();
|
|
|
|
// If the user already have selected a file, we have to destroy
|
|
// that file in order to select the new one.
|
|
if (oldDocument && m_fileOpened) {
|
|
releaseEditor();
|
|
|
|
DocumentDestroyer destroyer(m_context, oldDocument);
|
|
destroyer.destroyDocument();
|
|
}
|
|
|
|
captureEditor();
|
|
}
|
|
|
|
void captureEditor()
|
|
{
|
|
ASSERT(m_editor == NULL);
|
|
|
|
if (m_document && !m_editor) {
|
|
m_rect = getRectFromEntries();
|
|
m_editor = current_editor;
|
|
|
|
EditorStatePtr newState(new SelectBoxState(this, m_rect,
|
|
SelectBoxState::PaintRulers |
|
|
SelectBoxState::PaintGrid));
|
|
m_editor->setState(newState);
|
|
}
|
|
}
|
|
|
|
void releaseEditor()
|
|
{
|
|
if (m_editor) {
|
|
m_editor->backToPreviousState();
|
|
m_editor = NULL;
|
|
}
|
|
}
|
|
|
|
Context* m_context;
|
|
Document* m_document;
|
|
Grid m_grid;
|
|
DropDownButton m_selectFile;
|
|
Entry m_x, m_y, m_width, m_height;
|
|
Button m_import;
|
|
Button m_cancel;
|
|
Editor* m_editor;
|
|
gfx::Rect m_rect;
|
|
|
|
// True if the user has been opened the file (instead of selecting
|
|
// the current document).
|
|
bool m_fileOpened;
|
|
};
|
|
|
|
class ImportSpriteSheetCommand : public Command {
|
|
public:
|
|
ImportSpriteSheetCommand();
|
|
Command* clone() { return new ImportSpriteSheetCommand(*this); }
|
|
|
|
protected:
|
|
virtual void onExecute(Context* context) OVERRIDE;
|
|
};
|
|
|
|
ImportSpriteSheetCommand::ImportSpriteSheetCommand()
|
|
: Command("ImportSpriteSheet",
|
|
"Import Sprite Sheet",
|
|
CmdRecordableFlag)
|
|
{
|
|
}
|
|
|
|
void ImportSpriteSheetCommand::onExecute(Context* context)
|
|
{
|
|
base::UniquePtr<Window> window(new ImportSpriteSheetWindow(context));
|
|
window->openWindowInForeground();
|
|
}
|
|
|
|
Command* CommandFactory::createImportSpriteSheetCommand()
|
|
{
|
|
return new ImportSpriteSheetCommand;
|
|
}
|
|
|
|
} // namespace app
|