Added Undoable class.

This commit is contained in:
David Capello 2008-10-02 02:31:07 +00:00
parent c35c452a80
commit c327a740d7
20 changed files with 658 additions and 428 deletions

View File

@ -1,5 +1,26 @@
2008-10-01 David A. Capello <davidcapello@gmail.com>
* src/raster/undo.cpp: Renamed undo_undo and undo_redo to
undo_do_undo and undo_do_redo.
(undo_clear_redo): Added (used by Undoable when the final result
isn't committed).
* src/dialogs/aniedit.cpp (anieditor_msg_proc): Uses Undoable to
move frames, and move layers.
* src/commands/cmd_new_layer.cpp (cmd_new_layer_execute):
* src/commands/cmd_remove_cel.cpp (cmd_remove_cel_execute):
* src/commands/cmd_remove_frame.cpp (cmd_remove_frame_execute):
* src/commands/cmd_new_frame.cpp (cmd_new_frame_execute):
Changed to use Undoable.
* src/raster/undoable.h (class Undoable): Added.
2008-09-30 David A. Capello <davidcapello@gmail.com>
* src/raster/gfxobj.h (GfxObj): Converted to a class (and all the
hierarchy of graphics objects).
* src/tests/: Renamed src/test to src/tests
* makefile.lst: Project converted to C++

View File

@ -178,6 +178,10 @@ tests: $(TESTS)
runtests: $(AUTOTESTS)
@-$(foreach TEST, $(AUTOTESTS), \
echo ./$(TEST) "=====================" ; \
./$(TEST) ; \
echo Running ./$(TEST) ... ; \
if ./$(TEST) ; then \
echo " - OK" ; \
else \
echo " - *FAIL*" ; \
fi ; \
)

View File

@ -182,6 +182,7 @@ COMMON_SOURCES = \
src/raster/sprite.cpp \
src/raster/stock.cpp \
src/raster/undo.cpp \
src/raster/undoable.cpp \
src/util/autocrop.cpp \
src/util/boundary.cpp \
src/util/celmove.cpp \

View File

@ -31,11 +31,11 @@
#include "raster/image.h"
#include "raster/layer.h"
#include "raster/sprite.h"
#include "raster/stock.h"
#include "raster/undo.h"
#include "util/functions.h"
#include "raster/undoable.h"
#include "widgets/statebar.h"
#include <stdexcept>
static bool cmd_new_frame_enabled(const char *argument)
{
return
@ -48,18 +48,17 @@ static bool cmd_new_frame_enabled(const char *argument)
static void cmd_new_frame_execute(const char *argument)
{
Sprite *sprite = current_sprite;
if (undo_is_enabled(sprite->undo))
undo_set_label(sprite->undo, "New Frame");
NewFrame(sprite);
Sprite* sprite = current_sprite;
{
Undoable undoable(sprite, "New Frame");
undoable.new_frame();
undoable.commit();
}
update_screen_for_sprite(sprite);
statusbar_show_tip(app_get_statusbar(), 1000,
_("New frame %d/%d"),
sprite->frame+1, sprite->frames);
sprite->frame+1,
sprite->frames);
}
Command cmd_new_frame = {

View File

@ -26,7 +26,7 @@
#include "modules/sprites.h"
#include "raster/layer.h"
#include "raster/sprite.h"
#include "raster/undo.h"
#include "raster/undoable.h"
#include "util/functions.h"
static bool cmd_new_layer_enabled(const char *argument)
@ -37,7 +37,7 @@ static bool cmd_new_layer_enabled(const char *argument)
static void cmd_new_layer_execute(const char *argument)
{
JWidget window, name_widget;
Sprite *sprite = current_sprite; /* get current sprite */
Sprite* sprite = current_sprite; /* get current sprite */
/* load the window widget */
window = load_widget("newlay.jid", "new_layer");
@ -55,16 +55,12 @@ static void cmd_new_layer_execute(const char *argument)
jwindow_open_fg(window);
if (jwindow_get_killer(window) == jwidget_find_name(window, "ok")) {
const char *name = jwidget_get_text(jwidget_find_name(window, "name"));
Layer *layer;
if (undo_is_enabled(sprite->undo))
undo_set_label(sprite->undo, "New Layer");
layer = NewLayer(sprite);
if (!layer) {
jalert(_("Error<<Not enough memory||&Close"));
return;
const char* name = jwidget_get_text(jwidget_find_name(window, "name"));
Layer* layer;
{
Undoable undoable(sprite, "New Layer");
layer = undoable.new_layer();
undoable.commit();
}
layer_set_name(layer, name);
update_screen_for_sprite(sprite);

View File

@ -37,7 +37,7 @@ static void cmd_redo_execute(const char *argument)
_("Redid %s"),
undo_get_next_redo_label(current_sprite->undo));
undo_redo(current_sprite->undo);
undo_do_redo(current_sprite->undo);
sprite_generate_mask_boundaries(current_sprite);
update_screen_for_sprite(current_sprite);
}

View File

@ -23,9 +23,8 @@
#include "modules/sprites.h"
#include "raster/cel.h"
#include "raster/layer.h"
#include "raster/undo.h"
#include "raster/sprite.h"
#include "util/functions.h"
#include "raster/undoable.h"
static bool cmd_remove_cel_enabled(const char *argument)
{
@ -40,13 +39,13 @@ static bool cmd_remove_cel_enabled(const char *argument)
static void cmd_remove_cel_execute(const char *argument)
{
Sprite *sprite = current_sprite;
Cel *cel = layer_get_cel(sprite->layer, sprite->frame);
if (undo_is_enabled(sprite->undo))
undo_set_label(sprite->undo, "Remove Cel");
RemoveCel(sprite->layer, cel);
Sprite* sprite = current_sprite;
Cel* cel = layer_get_cel(sprite->layer, sprite->frame);
{
Undoable undoable(sprite, "Remove Cel");
undoable.remove_cel(sprite->layer, cel);
undoable.commit();
}
update_screen_for_sprite(sprite);
}

View File

@ -23,11 +23,8 @@
#include "commands/commands.h"
#include "modules/gui.h"
#include "modules/sprites.h"
#include "raster/cel.h"
#include "raster/layer.h"
#include "raster/sprite.h"
#include "raster/undo.h"
#include "util/functions.h"
#include "raster/undoable.h"
static bool cmd_remove_frame_enabled(const char *argument)
{
@ -39,13 +36,12 @@ static bool cmd_remove_frame_enabled(const char *argument)
static void cmd_remove_frame_execute(const char *argument)
{
Sprite *sprite = current_sprite;
if (undo_is_enabled(sprite->undo))
undo_set_label(sprite->undo, "Remove Frame");
RemoveFrame(sprite, sprite->frame);
update_screen_for_sprite(current_sprite);
{
Undoable undoable(sprite, "Remove Frame");
undoable.remove_frame(sprite->frame);
undoable.commit();
}
update_screen_for_sprite(sprite);
}
Command cmd_remove_frame = {

View File

@ -37,7 +37,7 @@ static void cmd_undo_execute(const char *argument)
_("Undid %s"),
undo_get_next_undo_label(current_sprite->undo));
undo_undo(current_sprite->undo);
undo_do_undo(current_sprite->undo);
sprite_generate_mask_boundaries(current_sprite);
update_screen_for_sprite(current_sprite);
}

View File

@ -21,6 +21,7 @@
/* #include <stdio.h> */
#include <string.h>
#include <allegro/unicode.h>
#include <exception>
#include "jinete/jinete.h"
@ -265,9 +266,17 @@ void command_execute(Command *command, const char *argument)
{
console_open();
if (command && command->execute &&
command_is_enabled(command, argument)) {
(*command->execute)(argument);
try {
if (command && command->execute &&
command_is_enabled(command, argument)) {
(*command->execute)(argument);
}
}
catch (std::exception& e) {
jalert("Error"
"<<Uncaught exception:"
"<<%s"
"||&OK", e.what());
}
console_close();

View File

@ -34,6 +34,7 @@
#include "modules/rootmenu.h"
#include "modules/sprites.h"
#include "raster/raster.h"
#include "raster/undoable.h"
#include "util/celmove.h"
#include "util/functions.h"
#include "util/thmbnail.h"
@ -535,11 +536,11 @@ static bool anieditor_msg_proc(JWidget widget, JMessage msg)
if (anieditor->hot_frame >= 0 &&
anieditor->hot_frame < anieditor->sprite->frames &&
anieditor->hot_frame != anieditor->clk_frame+1) {
if (undo_is_enabled(anieditor->sprite->undo))
undo_set_label(anieditor->sprite->undo, "Move Frame");
/* TODO warning MoveFrameBefore uses the current_sprite, not 'anieditor->sprite' */
MoveFrameBefore(anieditor->clk_frame, anieditor->hot_frame);
{
Undoable undoable(anieditor->sprite, "Move Frame");
undoable.move_frame_before(anieditor->clk_frame, anieditor->hot_frame);
undoable.commit();
}
jwidget_dirty(widget);
}
}
@ -566,12 +567,13 @@ static bool anieditor_msg_proc(JWidget widget, JMessage msg)
anieditor->hot_layer != anieditor->clk_layer &&
anieditor->hot_layer != anieditor->clk_layer+1) {
if (!layer_is_background(anieditor->layers[anieditor->clk_layer])) {
if (undo_is_enabled(anieditor->sprite->undo))
undo_set_label(anieditor->sprite->undo, "Move Layer");
/* move the clicked-layer after the hot-layer */
MoveLayerAfter(anieditor->layers[anieditor->clk_layer],
anieditor->layers[anieditor->hot_layer]);
// move the clicked-layer after the hot-layer
{
Undoable undoable(anieditor->sprite, "Move Layer");
undoable.move_layer_after(anieditor->layers[anieditor->clk_layer],
anieditor->layers[anieditor->hot_layer]);
undoable.commit();
}
/* select the new layer */
anieditor->sprite->layer = anieditor->layers[anieditor->clk_layer];
@ -678,7 +680,7 @@ static bool anieditor_msg_proc(JWidget widget, JMessage msg)
/* undo */
if (command && strcmp(command->name, CMD_UNDO) == 0) {
if (undo_can_undo(sprite->undo)) {
undo_undo(sprite->undo);
undo_do_undo(sprite->undo);
destroy_thumbnails();
anieditor_regenerate_layers(widget);
@ -691,7 +693,7 @@ static bool anieditor_msg_proc(JWidget widget, JMessage msg)
/* redo */
if (command && strcmp(command->name, CMD_REDO) == 0) {
if (undo_can_redo(sprite->undo)) {
undo_redo(sprite->undo);
undo_do_redo(sprite->undo);
destroy_thumbnails();
anieditor_regenerate_layers(widget);

View File

@ -23,6 +23,7 @@
/* #include <allegro/color.h> */
/* #include <allegro/gfx.h> */
#include <string.h>
#include <stdexcept>
#include "raster/algo.h"
#include "raster/blend.h"
@ -141,14 +142,10 @@ void image_merge(Image* dst, const Image* src, int x, int y, int opacity, int bl
Image* image_crop(const Image* image, int x, int y, int w, int h, int bgcolor)
{
Image* trim;
if (w < 1) throw std::invalid_argument("image_crop: Width is less than 1");
if (h < 1) throw std::invalid_argument("image_crop: Height is less than 1");
if ((w < 1) || (h < 1))
return NULL;
trim = image_new(image->imgtype, w, h);
if (!trim)
return NULL;
Image* trim = image_new(image->imgtype, w, h);
image_clear(trim, bgcolor);
image_copy(trim, image, -x, -y);

View File

@ -392,10 +392,16 @@ void layer_add_cel(Layer* layer, Cel* cel)
}
}
/**
* Removes the cel from the layer.
*
* It doesn't destroy the cel, you have to delete it after calling
* this routine.
*/
void layer_remove_cel(Layer* layer, Cel* cel)
{
if (layer_is_image(layer))
jlist_remove(layer->cels, cel);
assert(layer_is_image(layer));
jlist_remove(layer->cels, cel);
}
Cel* layer_get_cel(const Layer* layer, int frame)

View File

@ -113,7 +113,7 @@ typedef struct UndoAction
void (*invert)(UndoStream* stream, UndoChunk* chunk, int state);
} UndoAction;
static void run_undo(Undo* undo, int state, int discard);
static void run_undo(Undo* undo, int state, bool discard);
static int count_undo_groups(UndoStream* undo_stream);
static void update_undo(Undo* undo);
@ -263,11 +263,13 @@ Undo* undo_new(Sprite *sprite)
void undo_free(Undo* undo)
{
assert(undo);
delete undo;
}
int undo_get_memsize(Undo* undo)
{
assert(undo);
return
undo->undo_stream->size +
undo->redo_stream->size;
@ -275,44 +277,61 @@ int undo_get_memsize(Undo* undo)
void undo_enable(Undo* undo)
{
assert(undo);
undo->enabled = TRUE;
}
void undo_disable(Undo* undo)
{
assert(undo);
undo->enabled = FALSE;
}
bool undo_is_enabled(Undo* undo)
{
assert(undo);
return undo->enabled ? TRUE: FALSE;
}
bool undo_is_disabled(Undo* undo)
{
assert(undo);
return !undo_is_enabled(undo);
}
bool undo_can_undo(Undo* undo)
{
assert(undo);
return !jlist_empty(undo->undo_stream->chunks);
}
bool undo_can_redo(Undo* undo)
{
assert(undo);
return !jlist_empty(undo->redo_stream->chunks);
}
void undo_undo(Undo* undo)
void undo_do_undo(Undo* undo)
{
assert(undo);
run_undo(undo, DO_UNDO, FALSE);
}
void undo_redo(Undo* undo)
void undo_do_redo(Undo* undo)
{
assert(undo);
run_undo(undo, DO_REDO, FALSE);
}
void undo_clear_redo(Undo* undo)
{
assert(undo);
if (!jlist_empty(undo->redo_stream->chunks)) {
undo_stream_free(undo->redo_stream);
undo->redo_stream = undo_stream_new(undo);
}
}
void undo_set_label(Undo* undo, const char *label)
{
undo->label = label;
@ -338,7 +357,7 @@ const char *undo_get_next_redo_label(Undo* undo)
return chunk->label;
}
static void run_undo(Undo* undo, int state, int discard_tail)
static void run_undo(Undo* undo, int state, bool discard_tail)
{
UndoStream* undo_stream = ((state == DO_UNDO)? undo->undo_stream:
undo->redo_stream);
@ -431,10 +450,7 @@ static void update_undo(Undo* undo)
undo->diff_count++;
/* reset the "redo" stream */
if (!jlist_empty(undo->redo_stream->chunks)) {
undo_stream_free(undo->redo_stream);
undo->redo_stream = undo_stream_new(undo);
}
undo_clear_redo(undo);
/* "undo" is too big? */
while (groups > 1 && undo->undo_stream->size > undo_size_limit) {

View File

@ -63,8 +63,10 @@ bool undo_is_disabled(Undo* undo);
bool undo_can_undo(Undo* undo);
bool undo_can_redo(Undo* undo);
void undo_undo(Undo* undo);
void undo_redo(Undo* undo);
void undo_do_undo(Undo* undo);
void undo_do_redo(Undo* undo);
void undo_clear_redo(Undo* undo);
void undo_set_label(Undo* undo, const char *label);
const char* undo_get_next_undo_label(Undo* undo);

450
src/raster/undoable.cpp Normal file
View File

@ -0,0 +1,450 @@
/* ASE - Allegro Sprite Editor
* Copyright (C) 2001-2008 David A. 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
*/
#include "config.h"
#include <cassert>
#include "jinete/jlist.h"
#include "raster/blend.h"
#include "raster/cel.h"
#include "raster/image.h"
#include "raster/layer.h"
#include "raster/sprite.h"
#include "raster/stock.h"
#include "raster/undo.h"
#include "raster/undoable.h"
/**
* Starts a undoable sequence of operations.
*
* All the operations will be grouped in the sprite's undo as an
* atomic operation.
*/
Undoable::Undoable(Sprite* sprite, const char* label)
{
assert(sprite);
assert(label);
this->sprite = sprite;
committed = false;
enabled_flag = undo_is_enabled(sprite->undo);
if (is_enabled()) {
undo_set_label(sprite->undo, label);
undo_open(sprite->undo);
}
}
Undoable::~Undoable()
{
if (is_enabled()) {
// close the undo information
undo_close(sprite->undo);
// if it isn't committed, we have to rollback all changes
if (!committed) {
// undo the group of operations
undo_do_undo(sprite->undo);
// clear the redo (sorry to the user, here we lost the old redo
// information)
undo_clear_redo(sprite->undo);
}
}
}
/**
* This must be called to commit all the changes, so the undo will be
* finally added in the sprite.
*
* If you don't use this routine, all the changes will be discarded
* (if the sprite's undo was enabled when the Undoable was created).
*/
void Undoable::commit()
{
committed = true;
}
void Undoable::set_number_of_frames(int frames)
{
assert(frames >= 1);
// increment frames counter in the sprite
if (is_enabled())
undo_set_frames(sprite->undo, sprite);
sprite_set_frames(sprite, frames);
}
void Undoable::set_current_frame(int frame)
{
assert(frame >= 0);
if (is_enabled())
undo_int(sprite->undo, sprite, &sprite->frame);
sprite_set_frame(sprite, frame);
}
void Undoable::set_current_layer(Layer* layer)
{
assert(layer);
if (is_enabled())
undo_set_layer(sprite->undo, sprite);
sprite_set_layer(sprite, layer);
}
/**
* Adds a new image in the stock.
*
* @return
* The image index in the stock.
*/
int Undoable::add_image_in_stock(Image* image)
{
assert(image);
// add the image in the stock
int image_index = stock_add_image(sprite->stock, image);
if (is_enabled())
undo_add_image(sprite->undo, sprite->stock, image_index);
return image_index;
}
/**
* Removes and destroys the specified image in the stock.
*/
void Undoable::remove_image_from_stock(int image_index)
{
assert(image_index >= 0);
Image* image = stock_get_image(sprite->stock, image_index);
assert(image);
if (is_enabled())
undo_remove_image(sprite->undo, sprite->stock, image_index);
stock_remove_image(sprite->stock, image);
image_free(image);
}
/**
* Creates a new transparent layer.
*/
Layer* Undoable::new_layer()
{
// new layer
Layer* layer = layer_new(sprite);
// configure layer name and blend mode
layer_set_blend_mode(layer, BLEND_MODE_NORMAL);
// add the layer in the sprite set
if (is_enabled())
undo_add_layer(sprite->undo, sprite->set, layer);
layer_add_layer(sprite->set, layer);
// select the new layer
set_current_layer(layer);
return layer;
}
void Undoable::move_layer_after(Layer* layer, Layer* after_this)
{
if (is_enabled())
undo_move_layer(sprite->undo, layer);
layer_move_layer(layer->parent_layer, layer, after_this);
}
void Undoable::new_frame()
{
// add a new cel to every layer
new_frame_for_layer(sprite->set,
sprite->frame+1);
// increment frames counter in the sprite
set_number_of_frames(sprite->frames+1);
// go to next frame (the new one)
set_current_frame(sprite->frame+1);
}
void Undoable::new_frame_for_layer(Layer* layer, int frame)
{
assert(layer);
assert(frame >= 0);
switch (layer->type) {
case GFXOBJ_LAYER_IMAGE:
// displace all cels in '>=frame' to the next frame
for (int c=sprite->frames-1; c>=frame; --c) {
Cel* cel = layer_get_cel(layer, c);
if (cel)
set_cel_frame_position(cel, cel->frame+1);
}
copy_previous_frame(layer, frame);
break;
case GFXOBJ_LAYER_SET: {
JLink link;
JI_LIST_FOR_EACH(layer->layers, link)
new_frame_for_layer(reinterpret_cast<Layer*>(link->data), frame);
break;
}
}
}
void Undoable::remove_frame(int frame)
{
assert(frame >= 0);
// remove cels from this frame (and displace one position backward
// all next frames)
remove_frame_of_layer(sprite->set, frame);
/* decrement frames counter in the sprite */
if (is_enabled())
undo_set_frames(sprite->undo, sprite);
sprite_set_frames(sprite, sprite->frames-1);
/* move backward if we are outside the range of frames */
if (sprite->frame >= sprite->frames) {
if (is_enabled())
undo_int(sprite->undo, sprite, &sprite->frame);
sprite_set_frame(sprite, sprite->frames-1);
}
}
void Undoable::remove_frame_of_layer(Layer* layer, int frame)
{
assert(layer);
assert(frame >= 0);
switch (layer->type) {
case GFXOBJ_LAYER_IMAGE:
if (Cel* cel = layer_get_cel(layer, frame))
remove_cel(layer, cel);
for (++frame; frame<sprite->frames; ++frame)
if (Cel* cel = layer_get_cel(layer, frame))
set_cel_frame_position(cel, cel->frame-1);
break;
case GFXOBJ_LAYER_SET: {
JLink link;
JI_LIST_FOR_EACH(layer->layers, link)
remove_frame_of_layer(reinterpret_cast<Layer*>(link->data), frame);
break;
}
}
}
/**
* Copies the previous cel of @a frame to @frame.
*/
void Undoable::copy_previous_frame(Layer* layer, int frame)
{
assert(layer);
assert(frame > 0);
// create a copy of the previous cel
Cel* src_cel = layer_get_cel(layer, frame-1);
Image* src_image = src_cel ? stock_get_image(sprite->stock,
src_cel->image):
NULL;
// nothing to copy, it will be a transparent cel
if (!src_image)
return;
// copy the image
Image* dst_image = image_new_copy(src_image);
int image_index = add_image_in_stock(dst_image);
// create the new cel
Cel* dst_cel = cel_new(frame, image_index);
if (src_cel) { // copy the data from the previous cel
cel_set_position(dst_cel, src_cel->x, src_cel->y);
cel_set_opacity(dst_cel, src_cel->opacity);
}
// add the cel in the layer
add_cel(layer, dst_cel);
}
void Undoable::add_cel(Layer* layer, Cel* cel)
{
assert(layer);
assert(cel);
assert(layer_is_image(layer));
if (is_enabled())
undo_add_cel(sprite->undo, layer, cel);
layer_add_cel(layer, cel);
}
void Undoable::remove_cel(Layer* layer, Cel* cel)
{
assert(layer);
assert(cel);
assert(layer_is_image(layer));
// find if the image that use the cel to remove, is used by
// another cels
bool used = false;
for (int frame=0; frame<sprite->frames; ++frame) {
Cel* it = layer_get_cel(layer, frame);
if (it && it != cel && it->image == cel->image) {
used = true;
break;
}
}
// if the image is only used by this cel,
// we can remove the image from the stock
if (!used)
remove_image_from_stock(cel->image);
if (is_enabled())
undo_remove_cel(sprite->undo, layer, cel);
// remove the cel from the layer
layer_remove_cel(layer, cel);
// and here we destroy the cel
cel_free(cel);
}
void Undoable::set_cel_frame_position(Cel* cel, int frame)
{
assert(cel);
assert(frame >= 0);
if (is_enabled())
undo_int(sprite->undo, cel, &cel->frame);
cel->frame = frame;
}
void Undoable::set_frame_length(int frame, int msecs)
{
if (is_enabled())
undo_set_frlen(sprite->undo, sprite, frame);
sprite_set_frlen(sprite, frame, msecs);
}
void Undoable::move_frame_before(int frame, int before_frame)
{
if (frame != before_frame &&
frame >= 0 &&
frame < sprite->frames &&
before_frame >= 0 &&
before_frame < sprite->frames) {
// change the frame-lengths...
int frlen_aux = sprite->frlens[frame];
// moving the frame to the future
if (frame < before_frame) {
frlen_aux = sprite->frlens[frame];
for (int c=frame; c<before_frame-1; c++)
set_frame_length(c, sprite->frlens[c+1]);
set_frame_length(before_frame-1, frlen_aux);
}
// moving the frame to the past
else if (before_frame < frame) {
frlen_aux = sprite->frlens[frame];
for (int c=frame; c>before_frame; c--)
set_frame_length(c, sprite->frlens[c-1]);
set_frame_length(before_frame, frlen_aux);
}
// change the cels of position...
move_frame_before_layer(sprite->set, frame, before_frame);
}
}
void Undoable::move_frame_before_layer(Layer* layer, int frame, int before_frame)
{
assert(layer);
switch (layer->type) {
case GFXOBJ_LAYER_IMAGE: {
JLink link;
JI_LIST_FOR_EACH(layer->cels, link) {
Cel* cel = reinterpret_cast<Cel*>(link->data);
int new_frame = cel->frame;
// moving the frame to the future
if (frame < before_frame) {
if (cel->frame == frame) {
new_frame = before_frame-1;
}
else if (cel->frame > frame &&
cel->frame < before_frame) {
new_frame--;
}
}
// moving the frame to the past
else if (before_frame < frame) {
if (cel->frame == frame) {
new_frame = before_frame;
}
else if (cel->frame >= before_frame &&
cel->frame < frame) {
new_frame++;
}
}
if (cel->frame != new_frame)
set_cel_frame_position(cel, new_frame);
}
break;
}
case GFXOBJ_LAYER_SET: {
JLink link;
JI_LIST_FOR_EACH(layer->layers, link)
move_frame_before_layer(reinterpret_cast<Layer*>(link->data), frame, before_frame);
break;
}
}
}

76
src/raster/undoable.h Normal file
View File

@ -0,0 +1,76 @@
/* ASE - Allegro Sprite Editor
* Copyright (C) 2001-2008 David A. 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 RASTER_UNDOABLE_H
#define RASTER_UNDOABLE_H
class Layer;
class Sprite;
/**
* Class with high-level set of routines to modify a sprite.
*
* In this class, all modifications in the sprite have an undo
* counterpart (if the sprite's undo is enabled).
*/
class Undoable
{
Sprite* sprite;
bool committed;
bool enabled_flag;
public:
Undoable(Sprite* sprite, const char* label);
virtual ~Undoable();
inline Sprite* get_sprite() const { return sprite; }
inline bool is_enabled() const { return enabled_flag; }
void commit();
// for sprite
void set_number_of_frames(int frames);
void set_current_frame(int frame);
void set_current_layer(Layer* layer);
// for images in stock
int add_image_in_stock(Image* image);
void remove_image_from_stock(int image_index);
// for layers
Layer* new_layer();
void move_layer_after(Layer *layer, Layer *after_this);
// for frames
void new_frame();
void new_frame_for_layer(Layer* layer, int frame);
void remove_frame(int frame);
void remove_frame_of_layer(Layer* layer, int frame);
void copy_previous_frame(Layer* layer, int frame);
void set_frame_length(int frame, int msecs);
void move_frame_before(int frame, int before_frame);
void move_frame_before_layer(Layer* layer, int frame, int before_frame);
// for cels
void add_cel(Layer* layer, Cel* cel);
void remove_cel(Layer* layer, Cel* cel);
void set_cel_frame_position(Cel* cel, int frame);
};
#endif /* RASTER_UNDOABLE_H */

View File

@ -55,7 +55,7 @@ void test_init()
int test_exit()
{
trace("OK\n");
allegro_exit();
return 0;
}
@ -74,8 +74,6 @@ JWidget test_init_gui()
int test_exit_gui(JWidget manager)
{
trace("OK\n");
jmanager_free(manager);
allegro_exit();
return 0;

View File

@ -220,45 +220,6 @@ static void displace_layers(Undo *undo, Layer *layer, int x, int y)
static int get_max_layer_num(Layer *layer);
/**
* Creates a new transparent layer.
*/
Layer *NewLayer(Sprite *sprite)
{
Layer *layer;
if (sprite == NULL) {
console_printf("NewLayer: No current sprite\n");
return NULL;
}
/* new layer */
layer = layer_new(sprite);
if (!layer) {
console_printf("NewLayer: Not enough memory\n");
return NULL;
}
/* configure layer name and blend mode */
layer_set_blend_mode(layer, BLEND_MODE_NORMAL);
/* undo stuff */
if (undo_is_enabled(sprite->undo)) {
undo_open(sprite->undo);
undo_add_layer(sprite->undo, sprite->set, layer);
undo_set_layer(sprite->undo, sprite);
undo_close(sprite->undo);
}
/* add the layer in the sprite set */
layer_add_layer(sprite->set, layer);
/* select the new layer */
sprite_set_layer(sprite, layer);
return layer;
}
/**
* Creates a new layer set with the "name" in the current sprite
*/
@ -684,14 +645,6 @@ void LayerFromBackground(Sprite *sprite)
layer_set_name(sprite->layer, "Layer 0");
}
void MoveLayerAfter(Layer *layer, Layer *after_this)
{
if (undo_is_enabled(layer->sprite->undo))
undo_move_layer(layer->sprite->undo, layer);
layer_move_layer(layer->parent_layer, layer, after_this);
}
/* internal routine */
static int get_max_layer_num(Layer *layer)
{
@ -711,289 +664,6 @@ static int get_max_layer_num(Layer *layer)
return max;
}
/* ======================================= */
/* Frame */
/* ======================================= */
static bool new_frame_for_layer(Sprite *sprite, Layer *layer, int frame);
static bool copy_cel_in_next_frame(Sprite *sprite, Layer *layer, int frame);
static void remove_frame_for_layer(Sprite *sprite, Layer *layer, int frame);
static void move_frame_before_layer(Undo *undo, Layer *layer, int frame, int before_frame);
void NewFrame(Sprite *sprite)
{
if (undo_is_enabled(sprite->undo))
undo_open(sprite->undo);
/* add a new cel to every layer */
new_frame_for_layer(sprite,
sprite->set,
sprite->frame+1);
/* increment frames counter in the sprite */
if (undo_is_enabled(sprite->undo))
undo_set_frames(sprite->undo, sprite);
sprite_set_frames(sprite, sprite->frames+1);
/* go to next frame (the new one) */
if (undo_is_enabled(sprite->undo))
undo_int(sprite->undo, sprite, &sprite->frame);
sprite_set_frame(sprite, sprite->frame+1);
/* close undo & refresh the screen */
if (undo_is_enabled(sprite->undo))
undo_close(sprite->undo);
}
void RemoveFrame(Sprite *sprite, int frame)
{
if (undo_is_enabled(sprite->undo))
undo_open(sprite->undo);
/* remove cels from this frame (and displace one position backward
all next frames) */
remove_frame_for_layer(sprite, sprite->set, frame);
/* decrement frames counter in the sprite */
if (undo_is_enabled(sprite->undo))
undo_set_frames(sprite->undo, sprite);
sprite_set_frames(sprite, sprite->frames-1);
/* move backward if we are outside the range of frames */
if (sprite->frame >= sprite->frames) {
if (undo_is_enabled(sprite->undo))
undo_int(sprite->undo, sprite, &sprite->frame);
sprite_set_frame(sprite, sprite->frames-1);
}
if (undo_is_enabled(sprite->undo))
undo_close(sprite->undo);
}
void SetFrameLength(Sprite *sprite, int frame, int msecs)
{
if (undo_is_enabled(sprite->undo))
undo_set_frlen(sprite->undo, sprite, frame);
sprite_set_frlen(sprite, frame, msecs);
}
void MoveFrameBefore(int frame, int before_frame)
{
Sprite *sprite = current_sprite;
int c, frlen_aux;
if (sprite &&
frame != before_frame &&
frame >= 0 &&
frame < sprite->frames &&
before_frame >= 0 &&
before_frame < sprite->frames) {
if (undo_is_enabled(sprite->undo))
undo_open(sprite->undo);
/* Change the frame-lengths... */
frlen_aux = sprite->frlens[frame];
/* moving the frame to the future */
if (frame < before_frame) {
frlen_aux = sprite->frlens[frame];
for (c=frame; c<before_frame-1; c++)
SetFrameLength(sprite, c, sprite->frlens[c+1]);
SetFrameLength(sprite, before_frame-1, frlen_aux);
}
/* moving the frame to the past */
else if (before_frame < frame) {
frlen_aux = sprite->frlens[frame];
for (c=frame; c>before_frame; c--)
SetFrameLength(sprite, c, sprite->frlens[c-1]);
SetFrameLength(sprite, before_frame, frlen_aux);
}
/* Change the cels of position... */
move_frame_before_layer(sprite->undo, sprite->set, frame, before_frame);
if (undo_is_enabled(sprite->undo))
undo_close(sprite->undo);
}
}
static bool new_frame_for_layer(Sprite *sprite, Layer *layer, int frame)
{
switch (layer->type) {
case GFXOBJ_LAYER_IMAGE: {
Cel *cel;
int c;
for (c=sprite->frames-1; c>=frame; --c) {
cel = layer_get_cel(layer, c);
if (cel) {
undo_int(sprite->undo, cel, &cel->frame);
cel->frame++;
}
}
if (!copy_cel_in_next_frame(sprite, layer, frame))
return FALSE;
break;
}
case GFXOBJ_LAYER_SET: {
JLink link;
JI_LIST_FOR_EACH(layer->layers, link) {
if (!new_frame_for_layer(sprite, reinterpret_cast<Layer*>(link->data), frame))
return FALSE;
}
break;
}
}
return TRUE;
}
/* makes a copy of the cel in 'frame-1' to a new cel in 'frame' */
static bool copy_cel_in_next_frame(Sprite *sprite, Layer *layer, int frame)
{
int image_index;
Image *src_image;
Image *dst_image;
Cel *src_cel;
Cel *dst_cel;
assert(frame > 0);
/* create a copy of the previous cel */
src_cel = layer_get_cel(layer, frame-1);
src_image = src_cel ? stock_get_image(sprite->stock,
src_cel->image):
NULL;
if (src_image == NULL) {
/* do nothing, it will be a transparent cel */
return TRUE;
}
else
dst_image = image_new_copy(src_image);
if (!dst_image) {
console_printf(_("Not enough memory.\n"));
return FALSE;
}
/* add the image in the stock */
image_index = stock_add_image(sprite->stock, dst_image);
if (undo_is_enabled(sprite->undo))
undo_add_image(sprite->undo, sprite->stock, image_index);
/* create the new cel */
dst_cel = cel_new(frame, image_index);
if (src_cel != NULL) {
cel_set_position(dst_cel, src_cel->x, src_cel->y);
cel_set_opacity(dst_cel, src_cel->opacity);
}
/* add the cel in the layer */
if (undo_is_enabled(sprite->undo))
undo_add_cel(sprite->undo, layer, dst_cel);
layer_add_cel(layer, dst_cel);
return TRUE;
}
static void remove_frame_for_layer(Sprite *sprite, Layer *layer, int frame)
{
switch (layer->type) {
case GFXOBJ_LAYER_IMAGE: {
Cel *cel = layer_get_cel(layer, frame);
if (cel)
RemoveCel(layer, cel);
for (++frame; frame<sprite->frames; ++frame) {
cel = layer_get_cel(layer, frame);
if (cel) {
undo_int(sprite->undo, cel, &cel->frame);
cel->frame--;
}
}
break;
}
case GFXOBJ_LAYER_SET: {
JLink link;
JI_LIST_FOR_EACH(layer->layers, link)
remove_frame_for_layer(sprite, reinterpret_cast<Layer*>(link->data), frame);
break;
}
}
}
static void move_frame_before_layer(Undo *undo, Layer *layer, int frame, int before_frame)
{
switch (layer->type) {
case GFXOBJ_LAYER_IMAGE: {
Cel *cel;
JLink link;
int new_frame;
JI_LIST_FOR_EACH(layer->cels, link) {
cel = reinterpret_cast<Cel*>(link->data);
new_frame = cel->frame;
/* moving the frame to the future */
if (frame < before_frame) {
if (cel->frame == frame) {
new_frame = before_frame-1;
}
else if (cel->frame > frame &&
cel->frame < before_frame) {
new_frame--;
}
}
/* moving the frame to the past */
else if (before_frame < frame) {
if (cel->frame == frame) {
new_frame = before_frame;
}
else if (cel->frame >= before_frame &&
cel->frame < frame) {
new_frame++;
}
}
if (cel->frame != new_frame) {
if (undo_is_enabled(undo))
undo_int(undo, (GfxObj *)cel, &cel->frame);
cel->frame = new_frame;
}
}
break;
}
case GFXOBJ_LAYER_SET: {
JLink link;
JI_LIST_FOR_EACH(layer->layers, link)
move_frame_before_layer(undo, reinterpret_cast<Layer*>(link->data), frame, before_frame);
break;
}
}
}
/* ======================================= */
/* Cel */
/* ======================================= */

View File

@ -39,7 +39,6 @@ void CropSprite(Sprite* sprite);
/* Layer */
/*===================================================================*/
Layer* NewLayer(Sprite* sprite);
Layer* NewLayerSet(Sprite* sprite);
void RemoveLayer(Sprite* sprite);
@ -52,17 +51,6 @@ void CropLayer(Layer* layer, int x, int y, int w, int h);
void BackgroundFromLayer(Sprite* sprite);
void LayerFromBackground(Sprite* sprite);
void MoveLayerAfter(Layer* layer, Layer* after_this);
/* ======================================= */
/* Frame */
/* ======================================= */
void NewFrame(Sprite* sprite);
void RemoveFrame(Sprite* sprite, int frame);
void SetFrameLength(Sprite* sprite, int frame, int msecs);
void MoveFrameBefore(int frame, int before_frame);
/* ======================================= */
/* Cel */
/* ======================================= */