mirror of
https://github.com/aseprite/aseprite.git
synced 2025-04-01 01:20:25 +00:00
1521 lines
45 KiB
C++
1521 lines
45 KiB
C++
/* ASE - Allegro Sprite Editor
|
|
* Copyright (C) 2001-2010 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
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <allegro.h>
|
|
#include <assert.h>
|
|
|
|
#include "jinete/jinete.h"
|
|
|
|
#include "Vaca/Rect.h"
|
|
#include "Vaca/Point.h"
|
|
|
|
#include "commands/commands.h"
|
|
#include "commands/command.h"
|
|
#include "modules/gfx.h"
|
|
#include "modules/gui.h"
|
|
#include "modules/skinneable_theme.h"
|
|
#include "modules/rootmenu.h"
|
|
#include "raster/raster.h"
|
|
#include "undoable.h"
|
|
#include "util/celmove.h"
|
|
#include "util/thmbnail.h"
|
|
#include "sprite_wrappers.h"
|
|
#include "ui_context.h"
|
|
|
|
using Vaca::Rect;
|
|
using Vaca::Point;
|
|
|
|
/*
|
|
Animator Editor
|
|
|
|
Frames ...
|
|
|
|
--------------------+-----+-----+-----+---
|
|
Layers |msecs|msecs|msecs|...
|
|
--------------------+-----+-----+-----+---
|
|
[1] [2] Layer 1 | Cel | Cel | Cel |...
|
|
--------------------+-----+-----+-----+---
|
|
[1] [2] Background | Cel | Cel | Cel |...
|
|
--------------------+-----+-----+-----+---
|
|
|
|
[1] Eye-icon
|
|
[2] Padlock-icon
|
|
*/
|
|
|
|
/* size of the thumbnail in the screen (width x height), the really
|
|
size of the thumbnail bitmap is specified in the
|
|
'generate_thumbnail' routine */
|
|
#define THUMBSIZE (32*jguiscale())
|
|
|
|
/* height of the headers */
|
|
#define HDRSIZE (3 + text_height(widget->getFont())*2 + 3 + 3)
|
|
|
|
/* width of the frames */
|
|
#define FRMSIZE (3 + THUMBSIZE + 3)
|
|
|
|
/* height of the layers */
|
|
#define LAYSIZE (3 + MAX(text_height(widget->getFont()), THUMBSIZE) + 4)
|
|
|
|
/* space between icons and other information in the layer */
|
|
#define ICONSEP (2*jguiscale())
|
|
|
|
/* space between the icon-bitmap and the edge of the surrounding button */
|
|
#define ICONBORDER (4*jguiscale())
|
|
|
|
enum {
|
|
STATE_STANDBY,
|
|
STATE_SCROLLING,
|
|
STATE_MOVING_SEPARATOR,
|
|
STATE_MOVING_LAYER,
|
|
STATE_MOVING_CEL,
|
|
STATE_MOVING_FRAME,
|
|
};
|
|
|
|
enum {
|
|
A_PART_NOTHING,
|
|
A_PART_SEPARATOR,
|
|
A_PART_HEADER_LAYER,
|
|
A_PART_HEADER_FRAME,
|
|
A_PART_LAYER,
|
|
A_PART_LAYER_EYE_ICON,
|
|
A_PART_LAYER_LOCK_ICON,
|
|
A_PART_CEL
|
|
};
|
|
|
|
struct AniEditor
|
|
{
|
|
const Sprite* sprite;
|
|
int state;
|
|
Layer** layers;
|
|
int nlayers;
|
|
int scroll_x;
|
|
int scroll_y;
|
|
int separator_x;
|
|
int separator_w;
|
|
/* the 'hot' part is where the mouse is on top of */
|
|
int hot_part;
|
|
int hot_layer;
|
|
int hot_frame;
|
|
/* the 'clk' part is where the mouse's button was pressed (maybe for
|
|
a drag & drop operation) */
|
|
int clk_part;
|
|
int clk_layer;
|
|
int clk_frame;
|
|
/* keys */
|
|
bool space_pressed;
|
|
};
|
|
|
|
static JWidget current_anieditor = NULL;
|
|
|
|
static JWidget anieditor_new(const Sprite* sprite);
|
|
static int anieditor_type();
|
|
static AniEditor* anieditor_data(JWidget widget);
|
|
static bool anieditor_msg_proc(JWidget widget, JMessage msg);
|
|
static void anieditor_setcursor(JWidget widget, int x, int y);
|
|
static void anieditor_get_drawable_layers(JWidget widget, JRect clip, int* first_layer, int* last_layer);
|
|
static void anieditor_get_drawable_frames(JWidget widget, JRect clip, int* first_frame, int* last_frame);
|
|
static void anieditor_draw_header(JWidget widget, JRect clip);
|
|
static void anieditor_draw_header_frame(JWidget widget, JRect clip, int frame);
|
|
static void anieditor_draw_header_part(JWidget widget, JRect clip, int x1, int y1, int x2, int y2,
|
|
bool is_hot, bool is_clk,
|
|
const char* line1, int align1,
|
|
const char* line2, int align2);
|
|
static void anieditor_draw_separator(JWidget widget, JRect clip);
|
|
static void anieditor_draw_layer(JWidget widget, JRect clip, int layer_index);
|
|
static void anieditor_draw_layer_padding(JWidget widget);
|
|
static void anieditor_draw_cel(JWidget widget, JRect clip, int layer_index, int frame);
|
|
static bool anieditor_draw_part(JWidget widget, int part, int layer, int frame);
|
|
static void anieditor_regenerate_layers(JWidget widget);
|
|
static void anieditor_hot_this(JWidget widget, int hot_part, int hot_layer, int hot_frame);
|
|
static void anieditor_center_cel(JWidget widget, int layer, int frame);
|
|
static void anieditor_show_cel(JWidget widget, int layer, int frame);
|
|
static void anieditor_show_current_cel(JWidget widget);
|
|
static void anieditor_clean_clk(JWidget widget);
|
|
static void anieditor_set_scroll(JWidget widget, int x, int y, bool use_refresh_region);
|
|
static int anieditor_get_layer_index(JWidget widget, const Layer* layer);
|
|
|
|
static void icon_rect(BITMAP* icon, int x1, int y1, int x2, int y2, bool is_selected, bool is_hot, bool is_clk);
|
|
|
|
bool animation_editor_is_movingcel()
|
|
{
|
|
return
|
|
current_anieditor != NULL &&
|
|
anieditor_data(current_anieditor)->state == STATE_MOVING_CEL;
|
|
}
|
|
|
|
/**
|
|
* Shows the animation editor for the current sprite.
|
|
*/
|
|
void switch_between_animation_and_sprite_editor()
|
|
{
|
|
const Sprite* sprite = UIContext::instance()->get_current_sprite();
|
|
|
|
/* create the window & the animation-editor */
|
|
Frame* window = new Frame(true, NULL);
|
|
Widget* anieditor = anieditor_new(sprite);
|
|
current_anieditor = anieditor;
|
|
|
|
jwidget_add_child(window, anieditor);
|
|
window->remap_window();
|
|
|
|
/* show the current cel */
|
|
int layer = anieditor_get_layer_index(anieditor, sprite->getCurrentLayer());
|
|
if (layer >= 0)
|
|
anieditor_center_cel(anieditor, layer, sprite->getCurrentFrame());
|
|
|
|
/* show the window */
|
|
window->open_window_fg();
|
|
|
|
/* destroy the window */
|
|
jwidget_free(window);
|
|
current_anieditor = NULL;
|
|
|
|
/* destroy thumbnails */
|
|
destroy_thumbnails();
|
|
|
|
update_screen_for_sprite(sprite);
|
|
}
|
|
|
|
/*********************************************************************
|
|
The Animation Editor
|
|
*********************************************************************/
|
|
|
|
static JWidget anieditor_new(const Sprite* sprite)
|
|
{
|
|
Widget* widget = new Widget(anieditor_type());
|
|
AniEditor* anieditor = new AniEditor;
|
|
|
|
anieditor->sprite = sprite;
|
|
anieditor->state = STATE_STANDBY;
|
|
anieditor->layers = NULL;
|
|
anieditor->nlayers = 0;
|
|
anieditor->scroll_x = 0;
|
|
anieditor->scroll_y = 0;
|
|
anieditor->separator_x = 100 * jguiscale();
|
|
anieditor->separator_w = 1;
|
|
anieditor->hot_part = A_PART_NOTHING;
|
|
anieditor->clk_part = A_PART_NOTHING;
|
|
anieditor->space_pressed = false;
|
|
|
|
jwidget_add_hook(widget, anieditor_type(), anieditor_msg_proc, anieditor);
|
|
jwidget_focusrest(widget, true);
|
|
|
|
anieditor_regenerate_layers(widget);
|
|
|
|
return widget;
|
|
}
|
|
|
|
static int anieditor_type()
|
|
{
|
|
static int type = 0;
|
|
if (!type)
|
|
type = ji_register_widget_type();
|
|
return type;
|
|
}
|
|
|
|
static AniEditor* anieditor_data(JWidget widget)
|
|
{
|
|
return reinterpret_cast<AniEditor*>(jwidget_get_data(widget, anieditor_type()));
|
|
}
|
|
|
|
static bool anieditor_msg_proc(JWidget widget, JMessage msg)
|
|
{
|
|
AniEditor* anieditor = anieditor_data(widget);
|
|
|
|
switch (msg->type) {
|
|
|
|
case JM_DESTROY:
|
|
if (anieditor->layers)
|
|
jfree(anieditor->layers);
|
|
delete anieditor;
|
|
break;
|
|
|
|
case JM_REQSIZE:
|
|
/* this doesn't matter, the AniEditor'll use the entire screen
|
|
anyway */
|
|
msg->reqsize.w = 32;
|
|
msg->reqsize.h = 32;
|
|
return true;
|
|
|
|
/* case JM_CLOSE: */
|
|
/* break; */
|
|
|
|
case JM_DRAW: {
|
|
JRect clip = &msg->draw.rect;
|
|
int layer, first_layer, last_layer;
|
|
int frame, first_frame, last_frame;
|
|
|
|
anieditor_get_drawable_layers(widget, clip, &first_layer, &last_layer);
|
|
anieditor_get_drawable_frames(widget, clip, &first_frame, &last_frame);
|
|
|
|
/* draw the header for layers */
|
|
anieditor_draw_header(widget, clip);
|
|
|
|
/* draw the header for each visible frame */
|
|
for (frame=first_frame; frame<=last_frame; frame++)
|
|
anieditor_draw_header_frame(widget, clip, frame);
|
|
|
|
/* draw the separator */
|
|
anieditor_draw_separator(widget, clip);
|
|
|
|
/* draw each visible layer */
|
|
for (layer=first_layer; layer<=last_layer; layer++) {
|
|
anieditor_draw_layer(widget, clip, layer);
|
|
|
|
/* draw every visible cel for each layer */
|
|
for (frame=first_frame; frame<=last_frame; frame++)
|
|
anieditor_draw_cel(widget, clip, layer, frame);
|
|
}
|
|
|
|
anieditor_draw_layer_padding(widget);
|
|
|
|
return true;
|
|
}
|
|
|
|
case JM_TIMER:
|
|
break;
|
|
|
|
case JM_MOUSEENTER:
|
|
if (key[KEY_SPACE]) anieditor->space_pressed = true;
|
|
break;
|
|
|
|
case JM_MOUSELEAVE:
|
|
if (anieditor->space_pressed) anieditor->space_pressed = false;
|
|
break;
|
|
|
|
case JM_BUTTONPRESSED:
|
|
if (msg->mouse.middle || anieditor->space_pressed) {
|
|
jwidget_hard_capture_mouse(widget);
|
|
anieditor->state = STATE_SCROLLING;
|
|
return true;
|
|
}
|
|
|
|
/* clicked-part = hot-part */
|
|
anieditor->clk_part = anieditor->hot_part;
|
|
anieditor->clk_layer = anieditor->hot_layer;
|
|
anieditor->clk_frame = anieditor->hot_frame;
|
|
|
|
switch (anieditor->hot_part) {
|
|
case A_PART_NOTHING:
|
|
/* do nothing */
|
|
break;
|
|
case A_PART_SEPARATOR:
|
|
jwidget_hard_capture_mouse(widget);
|
|
anieditor->state = STATE_MOVING_SEPARATOR;
|
|
break;
|
|
case A_PART_HEADER_LAYER:
|
|
/* do nothing */
|
|
break;
|
|
case A_PART_HEADER_FRAME:
|
|
{
|
|
const SpriteReader sprite((Sprite*)anieditor->sprite);
|
|
SpriteWriter sprite_writer(sprite);
|
|
sprite_writer->setCurrentFrame(anieditor->clk_frame);
|
|
}
|
|
jwidget_dirty(widget); /* TODO replace this by redrawing old current frame and new current frame */
|
|
jwidget_hard_capture_mouse(widget);
|
|
anieditor->state = STATE_MOVING_FRAME;
|
|
break;
|
|
case A_PART_LAYER: {
|
|
const SpriteReader sprite((Sprite*)anieditor->sprite);
|
|
int old_layer = anieditor_get_layer_index(widget, sprite->getCurrentLayer());
|
|
int frame = anieditor->sprite->getCurrentFrame();
|
|
|
|
/* did the user select another layer? */
|
|
if (old_layer != anieditor->clk_layer) {
|
|
{
|
|
SpriteWriter sprite_writer(sprite);
|
|
sprite_writer->setCurrentLayer(anieditor->layers[anieditor->clk_layer]);
|
|
}
|
|
|
|
jmouse_hide();
|
|
/* redraw the old & new selected cel */
|
|
anieditor_draw_part(widget, A_PART_CEL, old_layer, frame);
|
|
anieditor_draw_part(widget, A_PART_CEL, anieditor->clk_layer, frame);
|
|
/* redraw the old selected layer */
|
|
anieditor_draw_part(widget, A_PART_LAYER, old_layer, frame);
|
|
jmouse_show();
|
|
}
|
|
|
|
/* change the scroll to show the new selected cel */
|
|
anieditor_show_cel(widget, anieditor->clk_layer, sprite->getCurrentFrame());
|
|
jwidget_hard_capture_mouse(widget);
|
|
anieditor->state = STATE_MOVING_LAYER;
|
|
break;
|
|
}
|
|
case A_PART_LAYER_EYE_ICON:
|
|
jwidget_hard_capture_mouse(widget);
|
|
break;
|
|
case A_PART_LAYER_LOCK_ICON:
|
|
jwidget_hard_capture_mouse(widget);
|
|
break;
|
|
case A_PART_CEL: {
|
|
const SpriteReader sprite((Sprite*)anieditor->sprite);
|
|
int old_layer = anieditor_get_layer_index(widget, sprite->getCurrentLayer());
|
|
int old_frame = sprite->getCurrentFrame();
|
|
|
|
/* select the new clicked-part */
|
|
if (old_layer != anieditor->clk_layer ||
|
|
old_frame != anieditor->clk_frame) {
|
|
{
|
|
SpriteWriter sprite_writer(sprite);
|
|
sprite_writer->setCurrentLayer(anieditor->layers[anieditor->clk_layer]);
|
|
sprite_writer->setCurrentFrame(anieditor->clk_frame);
|
|
}
|
|
|
|
jmouse_hide();
|
|
/* redraw the old & new selected layer */
|
|
if (old_layer != anieditor->clk_layer) {
|
|
anieditor_draw_part(widget, A_PART_LAYER, old_layer, old_frame);
|
|
anieditor_draw_part(widget, A_PART_LAYER,
|
|
anieditor->clk_layer,
|
|
anieditor->clk_frame);
|
|
}
|
|
/* redraw the old selected cel */
|
|
anieditor_draw_part(widget, A_PART_CEL, old_layer, old_frame);
|
|
jmouse_show();
|
|
}
|
|
|
|
/* change the scroll to show the new selected cel */
|
|
anieditor_show_cel(widget, anieditor->clk_layer, sprite->getCurrentFrame());
|
|
|
|
/* capture the mouse (to move the cel) */
|
|
jwidget_hard_capture_mouse(widget);
|
|
anieditor->state = STATE_MOVING_CEL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* redraw the new selected part (header, layer or cel) */
|
|
jmouse_hide();
|
|
anieditor_draw_part(widget,
|
|
anieditor->clk_part,
|
|
anieditor->clk_layer,
|
|
anieditor->clk_frame);
|
|
jmouse_show();
|
|
break;
|
|
|
|
case JM_MOTION: {
|
|
int hot_part = A_PART_NOTHING;
|
|
int hot_layer = -1;
|
|
int hot_frame = -1;
|
|
int mx = msg->mouse.x - widget->rc->x1;
|
|
int my = msg->mouse.y - widget->rc->y1;
|
|
|
|
if (jwidget_has_capture(widget)) {
|
|
if (anieditor->state == STATE_SCROLLING) {
|
|
anieditor_set_scroll(widget,
|
|
anieditor->scroll_x+jmouse_x(1)-jmouse_x(0),
|
|
anieditor->scroll_y+jmouse_y(1)-jmouse_y(0), true);
|
|
|
|
jmouse_control_infinite_scroll(widget->rc);
|
|
return true;
|
|
}
|
|
/* if the mouse pressed the mouse's button in the separator, we
|
|
shouldn't change the hot (so the separator can be tracked to
|
|
the mouse's released) */
|
|
else if (anieditor->clk_part == A_PART_SEPARATOR) {
|
|
hot_part = anieditor->clk_part;
|
|
anieditor->separator_x = mx;
|
|
jwidget_dirty(widget);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/* is the mouse on the separator= */
|
|
if (mx > anieditor->separator_x-4 && mx < anieditor->separator_x+4) {
|
|
hot_part = A_PART_SEPARATOR;
|
|
}
|
|
/* is the mouse on the headers? */
|
|
else if (my < HDRSIZE) {
|
|
/* is on the layers' header? */
|
|
if (mx < anieditor->separator_x)
|
|
hot_part = A_PART_HEADER_LAYER;
|
|
/* is on a frame header? */
|
|
else {
|
|
hot_part = A_PART_HEADER_FRAME;
|
|
hot_frame = (mx
|
|
- anieditor->separator_x
|
|
- anieditor->separator_w
|
|
+ anieditor->scroll_x) / FRMSIZE;
|
|
}
|
|
}
|
|
else {
|
|
hot_layer = (my
|
|
- HDRSIZE
|
|
+ anieditor->scroll_y) / LAYSIZE;
|
|
|
|
/* is the mouse on a layer's label? */
|
|
if (mx < anieditor->separator_x) {
|
|
BITMAP* icon1 = get_gfx(GFX_BOX_SHOW);
|
|
BITMAP* icon2 = get_gfx(GFX_BOX_UNLOCK);
|
|
int x1, y1, x2, y2, y_mid;
|
|
|
|
x1 = 0;
|
|
y1 = HDRSIZE + LAYSIZE*hot_layer - anieditor->scroll_y;
|
|
x2 = x1 + anieditor->separator_x - 1;
|
|
y2 = y1 + LAYSIZE - 1;
|
|
y_mid = (y1+y2) / 2;
|
|
|
|
if (mx >= x1+2 &&
|
|
mx <= x1+ICONSEP+ICONBORDER+icon1->w+ICONBORDER-1 &&
|
|
my >= y_mid-icon1->h/2-ICONBORDER &&
|
|
my <= y_mid+icon1->h/2+ICONBORDER) {
|
|
hot_part = A_PART_LAYER_EYE_ICON;
|
|
}
|
|
else if (mx >= x1+ICONSEP+ICONBORDER+icon1->w+ICONBORDER &&
|
|
mx <= x1+ICONSEP+ICONBORDER+icon1->w+ICONBORDER+ICONBORDER+icon2->w+ICONBORDER-1 &&
|
|
my >= y_mid-icon2->h/2-ICONBORDER &&
|
|
my <= y_mid+icon2->h/2+ICONBORDER) {
|
|
hot_part = A_PART_LAYER_LOCK_ICON;
|
|
}
|
|
else
|
|
hot_part = A_PART_LAYER;
|
|
}
|
|
else {
|
|
hot_part = A_PART_CEL;
|
|
hot_frame = (mx
|
|
- anieditor->separator_x
|
|
- anieditor->separator_w
|
|
+ anieditor->scroll_x) / FRMSIZE;
|
|
}
|
|
}
|
|
|
|
/* set the new 'hot' thing */
|
|
anieditor_hot_this(widget, hot_part, hot_layer, hot_frame);
|
|
return true;
|
|
}
|
|
|
|
case JM_BUTTONRELEASED:
|
|
if (jwidget_has_capture(widget)) {
|
|
jwidget_release_mouse(widget);
|
|
|
|
if (anieditor->state == STATE_SCROLLING) {
|
|
anieditor->state = STATE_STANDBY;
|
|
return true;
|
|
}
|
|
|
|
switch (anieditor->hot_part) {
|
|
case A_PART_NOTHING:
|
|
/* do nothing */
|
|
break;
|
|
case A_PART_SEPARATOR:
|
|
/* do nothing */
|
|
break;
|
|
case A_PART_HEADER_LAYER:
|
|
/* do nothing */
|
|
break;
|
|
case A_PART_HEADER_FRAME:
|
|
/* show the frame pop-up menu */
|
|
if (msg->mouse.right) {
|
|
if (anieditor->clk_frame == anieditor->hot_frame) {
|
|
JWidget popup_menu = get_frame_popup_menu();
|
|
if (popup_menu != NULL) {
|
|
jmenu_popup(popup_menu, msg->mouse.x, msg->mouse.y);
|
|
|
|
destroy_thumbnails();
|
|
jwidget_dirty(widget);
|
|
}
|
|
}
|
|
}
|
|
/* show the frame's properties dialog */
|
|
else if (msg->mouse.left) {
|
|
if (anieditor->clk_frame == anieditor->hot_frame) {
|
|
UIContext::instance()
|
|
->execute_command(CommandsModule::instance()
|
|
->get_command_by_name(CommandId::frame_properties));
|
|
}
|
|
else {
|
|
const SpriteReader sprite((Sprite*)anieditor->sprite);
|
|
|
|
if (anieditor->hot_frame >= 0 &&
|
|
anieditor->hot_frame < sprite->getTotalFrames() &&
|
|
anieditor->hot_frame != anieditor->clk_frame+1) {
|
|
{
|
|
SpriteWriter sprite_writer(sprite);
|
|
Undoable undoable(sprite_writer, "Move Frame");
|
|
undoable.move_frame_before(anieditor->clk_frame, anieditor->hot_frame);
|
|
undoable.commit();
|
|
}
|
|
jwidget_dirty(widget);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case A_PART_LAYER:
|
|
/* show the layer pop-up menu */
|
|
if (msg->mouse.right) {
|
|
if (anieditor->clk_layer == anieditor->hot_layer) {
|
|
JWidget popup_menu = get_layer_popup_menu();
|
|
if (popup_menu != NULL) {
|
|
jmenu_popup(popup_menu, msg->mouse.x, msg->mouse.y);
|
|
|
|
destroy_thumbnails();
|
|
jwidget_dirty(widget);
|
|
anieditor_regenerate_layers(widget);
|
|
}
|
|
}
|
|
}
|
|
/* move a layer */
|
|
else if (msg->mouse.left) {
|
|
if (anieditor->hot_layer >= 0 &&
|
|
anieditor->hot_layer < anieditor->nlayers &&
|
|
anieditor->hot_layer != anieditor->clk_layer &&
|
|
anieditor->hot_layer != anieditor->clk_layer+1) {
|
|
if (!anieditor->layers[anieditor->clk_layer]->is_background()) {
|
|
// move the clicked-layer after the hot-layer
|
|
try {
|
|
const SpriteReader sprite((Sprite*)anieditor->sprite);
|
|
SpriteWriter sprite_writer(sprite);
|
|
Undoable undoable(sprite_writer, "Move Layer");
|
|
undoable.move_layer_after(anieditor->layers[anieditor->clk_layer],
|
|
anieditor->layers[anieditor->hot_layer]);
|
|
undoable.commit();
|
|
|
|
/* select the new layer */
|
|
sprite_writer->setCurrentLayer(anieditor->layers[anieditor->clk_layer]);
|
|
}
|
|
catch (locked_sprite_exception& e) {
|
|
e.show();
|
|
}
|
|
|
|
jwidget_dirty(widget);
|
|
anieditor_regenerate_layers(widget);
|
|
}
|
|
else {
|
|
jalert(PACKAGE "<<You can't move the `Background' layer.||&OK");
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case A_PART_LAYER_EYE_ICON:
|
|
/* hide/show layer */
|
|
if (anieditor->hot_layer == anieditor->clk_layer &&
|
|
anieditor->hot_layer >= 0 &&
|
|
anieditor->hot_layer < anieditor->nlayers) {
|
|
Layer* layer = anieditor->layers[anieditor->clk_layer];
|
|
assert(layer != NULL);
|
|
layer->set_readable(!layer->is_readable());
|
|
}
|
|
break;
|
|
case A_PART_LAYER_LOCK_ICON:
|
|
/* lock/unlock layer */
|
|
if (anieditor->hot_layer == anieditor->clk_layer &&
|
|
anieditor->hot_layer >= 0 &&
|
|
anieditor->hot_layer < anieditor->nlayers) {
|
|
Layer* layer = anieditor->layers[anieditor->clk_layer];
|
|
assert(layer != NULL);
|
|
layer->set_writable(!layer->is_writable());
|
|
}
|
|
break;
|
|
case A_PART_CEL: {
|
|
bool movement =
|
|
anieditor->clk_part == A_PART_CEL &&
|
|
anieditor->hot_part == A_PART_CEL &&
|
|
(anieditor->clk_layer != anieditor->hot_layer ||
|
|
anieditor->clk_frame != anieditor->hot_frame);
|
|
|
|
if (movement) {
|
|
set_frame_to_handle
|
|
(/* source cel */
|
|
anieditor->layers[anieditor->clk_layer],
|
|
anieditor->clk_frame,
|
|
/* destination cel */
|
|
anieditor->layers[anieditor->hot_layer],
|
|
anieditor->hot_frame);
|
|
}
|
|
|
|
/* show the cel pop-up menu */
|
|
if (msg->mouse.right) {
|
|
JWidget popup_menu = movement ? get_cel_movement_popup_menu():
|
|
get_cel_popup_menu();
|
|
if (popup_menu != NULL) {
|
|
jmenu_popup(popup_menu, msg->mouse.x, msg->mouse.y);
|
|
|
|
destroy_thumbnails();
|
|
anieditor_regenerate_layers(widget);
|
|
jwidget_dirty(widget);
|
|
}
|
|
}
|
|
/* move the cel */
|
|
else if (msg->mouse.left) {
|
|
if (movement) {
|
|
{
|
|
const SpriteReader sprite((Sprite*)anieditor->sprite);
|
|
SpriteWriter sprite_writer(sprite);
|
|
move_cel(sprite_writer);
|
|
}
|
|
|
|
destroy_thumbnails();
|
|
anieditor_regenerate_layers(widget);
|
|
jwidget_dirty(widget);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* clean the clicked-part & redraw the hot-part */
|
|
jmouse_hide();
|
|
anieditor_clean_clk(widget);
|
|
anieditor_draw_part(widget,
|
|
anieditor->hot_part,
|
|
anieditor->hot_layer,
|
|
anieditor->hot_frame);
|
|
jmouse_show();
|
|
|
|
/* restore the cursor */
|
|
anieditor->state = STATE_STANDBY;
|
|
anieditor_setcursor(widget, msg->mouse.x, msg->mouse.y);
|
|
return true;
|
|
}
|
|
break;
|
|
|
|
case JM_KEYPRESSED: {
|
|
Command *command = get_command_from_key_message(msg);
|
|
|
|
/* close animation editor */
|
|
if ((command && (strcmp(command->short_name(), CommandId::film_editor) == 0)) ||
|
|
(msg->key.scancode == KEY_ESC)) {
|
|
jwidget_close_window(widget);
|
|
return true;
|
|
}
|
|
|
|
/* undo */
|
|
if (command && strcmp(command->short_name(), CommandId::undo) == 0) {
|
|
const SpriteReader sprite((Sprite*)anieditor->sprite);
|
|
if (undo_can_undo(sprite->getUndo())) {
|
|
SpriteWriter sprite_writer(sprite);
|
|
undo_do_undo(sprite_writer->getUndo());
|
|
|
|
destroy_thumbnails();
|
|
anieditor_regenerate_layers(widget);
|
|
anieditor_show_current_cel(widget);
|
|
jwidget_dirty(widget);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* redo */
|
|
if (command && strcmp(command->short_name(), CommandId::redo) == 0) {
|
|
const SpriteReader sprite((Sprite*)anieditor->sprite);
|
|
if (undo_can_redo(sprite->getUndo())) {
|
|
SpriteWriter sprite_writer(sprite);
|
|
undo_do_redo(sprite_writer->getUndo());
|
|
|
|
destroy_thumbnails();
|
|
anieditor_regenerate_layers(widget);
|
|
anieditor_show_current_cel(widget);
|
|
jwidget_dirty(widget);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* new_frame, remove_frame, new_cel, remove_cel */
|
|
if (command != NULL) {
|
|
if (strcmp(command->short_name(), CommandId::new_frame) == 0 ||
|
|
strcmp(command->short_name(), CommandId::remove_cel) == 0 ||
|
|
strcmp(command->short_name(), CommandId::remove_frame) == 0 ||
|
|
strcmp(command->short_name(), CommandId::goto_first_frame) == 0 ||
|
|
strcmp(command->short_name(), CommandId::goto_previous_frame) == 0 ||
|
|
strcmp(command->short_name(), CommandId::goto_previous_layer) == 0 ||
|
|
strcmp(command->short_name(), CommandId::goto_next_frame) == 0 ||
|
|
strcmp(command->short_name(), CommandId::goto_next_layer) == 0 ||
|
|
strcmp(command->short_name(), CommandId::goto_last_frame) == 0) {
|
|
// execute the command
|
|
UIContext::instance()->execute_command(command);
|
|
|
|
anieditor_show_current_cel(widget);
|
|
jwidget_dirty(widget);
|
|
return true;
|
|
}
|
|
|
|
if (strcmp(command->short_name(), CommandId::new_layer) == 0 ||
|
|
strcmp(command->short_name(), CommandId::remove_layer) == 0) {
|
|
// execute the command
|
|
UIContext::instance()->execute_command(command);
|
|
|
|
anieditor_regenerate_layers(widget);
|
|
anieditor_show_current_cel(widget);
|
|
jwidget_dirty(widget);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
switch (msg->key.scancode) {
|
|
case KEY_SPACE:
|
|
anieditor->space_pressed = true;
|
|
anieditor_setcursor(widget, jmouse_x(0), jmouse_y(0));
|
|
return true;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case JM_KEYRELEASED:
|
|
switch (msg->key.scancode) {
|
|
|
|
case KEY_SPACE:
|
|
if (anieditor->space_pressed) {
|
|
/* we have to clear all the KEY_SPACE in buffer */
|
|
clear_keybuf();
|
|
|
|
anieditor->space_pressed = false;
|
|
anieditor_setcursor(widget, jmouse_x(0), jmouse_y(0));
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case JM_WHEEL: {
|
|
int dz = jmouse_z(1) - jmouse_z(0);
|
|
int dx = 0;
|
|
int dy = 0;
|
|
|
|
if ((msg->any.shifts & KB_CTRL_FLAG) == KB_CTRL_FLAG)
|
|
dx = dz * FRMSIZE;
|
|
else
|
|
dy = dz * LAYSIZE;
|
|
|
|
if ((msg->any.shifts & KB_SHIFT_FLAG) == KB_SHIFT_FLAG) {
|
|
dx *= 3;
|
|
dy *= 3;
|
|
}
|
|
|
|
anieditor_set_scroll(widget,
|
|
anieditor->scroll_x+dx,
|
|
anieditor->scroll_y+dy, true);
|
|
break;
|
|
}
|
|
|
|
case JM_SETCURSOR:
|
|
anieditor_setcursor(widget, msg->mouse.x, msg->mouse.y);
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void anieditor_setcursor(JWidget widget, int x, int y)
|
|
{
|
|
AniEditor* anieditor = anieditor_data(widget);
|
|
int mx = x - widget->rc->x1;
|
|
//int my = y - widget->rc->y1;
|
|
|
|
/* is the mouse in the separator */
|
|
if (mx > anieditor->separator_x-2 && mx < anieditor->separator_x+2) {
|
|
jmouse_set_cursor(JI_CURSOR_SIZE_L);
|
|
}
|
|
/* scrolling */
|
|
else if (anieditor->state == STATE_SCROLLING ||
|
|
anieditor->space_pressed) {
|
|
jmouse_set_cursor(JI_CURSOR_SCROLL);
|
|
}
|
|
/* moving a frame */
|
|
else if (anieditor->state == STATE_MOVING_FRAME &&
|
|
anieditor->clk_part == A_PART_HEADER_FRAME &&
|
|
anieditor->hot_part == A_PART_HEADER_FRAME &&
|
|
anieditor->clk_frame != anieditor->hot_frame) {
|
|
jmouse_set_cursor(JI_CURSOR_MOVE);
|
|
}
|
|
/* moving a layer */
|
|
else if (anieditor->state == STATE_MOVING_LAYER &&
|
|
anieditor->clk_part == A_PART_LAYER &&
|
|
anieditor->hot_part == A_PART_LAYER &&
|
|
anieditor->clk_layer != anieditor->hot_layer) {
|
|
if (anieditor->layers[anieditor->clk_layer]->is_background())
|
|
jmouse_set_cursor(JI_CURSOR_FORBIDDEN);
|
|
else
|
|
jmouse_set_cursor(JI_CURSOR_MOVE);
|
|
}
|
|
/* moving a cel */
|
|
else if (anieditor->state == STATE_MOVING_CEL &&
|
|
anieditor->clk_part == A_PART_CEL &&
|
|
anieditor->hot_part == A_PART_CEL &&
|
|
(anieditor->clk_frame != anieditor->hot_frame ||
|
|
anieditor->clk_layer != anieditor->hot_layer)) {
|
|
jmouse_set_cursor(JI_CURSOR_MOVE);
|
|
}
|
|
/* normal state */
|
|
else {
|
|
jmouse_set_cursor(JI_CURSOR_NORMAL);
|
|
}
|
|
}
|
|
|
|
static void anieditor_get_drawable_layers(JWidget widget, JRect clip, int *first_layer, int *last_layer)
|
|
{
|
|
AniEditor* anieditor = anieditor_data(widget);
|
|
|
|
*first_layer = 0;
|
|
*last_layer = anieditor->nlayers-1;
|
|
}
|
|
|
|
static void anieditor_get_drawable_frames(JWidget widget, JRect clip, int *first_frame, int *last_frame)
|
|
{
|
|
AniEditor* anieditor = anieditor_data(widget);
|
|
|
|
*first_frame = 0;
|
|
*last_frame = anieditor->sprite->getTotalFrames()-1;
|
|
}
|
|
|
|
static void anieditor_draw_header(JWidget widget, JRect clip)
|
|
{
|
|
AniEditor* anieditor = anieditor_data(widget);
|
|
/* bool is_hot = (anieditor->hot_part == A_PART_HEADER_LAYER); */
|
|
/* bool is_clk = (anieditor->clk_part == A_PART_HEADER_LAYER); */
|
|
int x1, y1, x2, y2;
|
|
|
|
x1 = widget->rc->x1;
|
|
y1 = widget->rc->y1;
|
|
x2 = x1 + anieditor->separator_x - 1;
|
|
y2 = y1 + HDRSIZE - 1;
|
|
|
|
/* draw the header for the layers */
|
|
anieditor_draw_header_part(widget, clip, x1, y1, x2, y2,
|
|
/* is_hot, is_clk, */
|
|
false, false,
|
|
"Frames >>", 1,
|
|
"Layers", -1);
|
|
}
|
|
|
|
static void anieditor_draw_header_frame(JWidget widget, JRect clip, int frame)
|
|
{
|
|
AniEditor* anieditor = anieditor_data(widget);
|
|
bool is_hot = (anieditor->hot_part == A_PART_HEADER_FRAME &&
|
|
anieditor->hot_frame == frame);
|
|
bool is_clk = (anieditor->clk_part == A_PART_HEADER_FRAME &&
|
|
anieditor->clk_frame == frame);
|
|
int x1, y1, x2, y2;
|
|
int cx1, cy1, cx2, cy2;
|
|
char buf1[256];
|
|
char buf2[256];
|
|
|
|
get_clip_rect(ji_screen, &cx1, &cy1, &cx2, &cy2);
|
|
|
|
x1 = widget->rc->x1 + anieditor->separator_x + anieditor->separator_w
|
|
+ FRMSIZE*frame - anieditor->scroll_x;
|
|
y1 = widget->rc->y1;
|
|
x2 = x1 + FRMSIZE - 1;
|
|
y2 = y1 + HDRSIZE - 1;
|
|
|
|
add_clip_rect(ji_screen,
|
|
widget->rc->x1 + anieditor->separator_x + anieditor->separator_w,
|
|
y1, widget->rc->x2-1, y2);
|
|
|
|
/* draw the header for the layers */
|
|
usprintf(buf1, "%d", frame+1);
|
|
usprintf(buf2, "%d", anieditor->sprite->getFrameDuration(frame));
|
|
anieditor_draw_header_part(widget, clip, x1, y1, x2, y2,
|
|
is_hot, is_clk,
|
|
buf1, 0,
|
|
buf2, 0);
|
|
|
|
/* if this header wasn't clicked but there are another frame's
|
|
header clicked, we have to draw some indicators to show that the
|
|
user can move frames */
|
|
if (is_hot && !is_clk &&
|
|
anieditor->clk_part == A_PART_HEADER_FRAME) {
|
|
rectfill(ji_screen, x1+1, y1+1, x1+4, y2-1, ji_color_selected());
|
|
}
|
|
|
|
/* padding in the right side */
|
|
if (frame == anieditor->sprite->getTotalFrames()-1) {
|
|
if (x2+1 <= widget->rc->x2-1) {
|
|
/* right side */
|
|
vline(ji_screen, x2+1, y1, y2, ji_color_foreground());
|
|
if (x2+2 <= widget->rc->x2-1)
|
|
rectfill(ji_screen, x2+2, y1, widget->rc->x2-1, y2, ji_color_face());
|
|
}
|
|
}
|
|
|
|
set_clip_rect(ji_screen, cx1, cy1, cx2, cy2);
|
|
}
|
|
|
|
static void anieditor_draw_header_part(JWidget widget, JRect clip, int x1, int y1, int x2, int y2,
|
|
bool is_hot, bool is_clk,
|
|
const char *line1, int align1,
|
|
const char *line2, int align2)
|
|
{
|
|
int x, fg, face, facelight, faceshadow;
|
|
|
|
if ((x2 < clip->x1) || (x1 >= clip->x2) ||
|
|
(y2 < clip->y1) || (y1 >= clip->y2))
|
|
return;
|
|
|
|
fg = !is_hot && is_clk ? ji_color_background(): ji_color_foreground();
|
|
face = is_hot ? ji_color_hotface(): (is_clk ? ji_color_selected():
|
|
ji_color_face());
|
|
facelight = is_hot && is_clk ? ji_color_faceshadow(): ji_color_facelight();
|
|
faceshadow = is_hot && is_clk ? ji_color_facelight(): ji_color_faceshadow();
|
|
|
|
/* draw the border of this text */
|
|
jrectedge(ji_screen, x1, y1, x2, y2, facelight, faceshadow);
|
|
|
|
/* fill the background of the part */
|
|
rectfill(ji_screen, x1+1, y1+1, x2-1, y2-1, face);
|
|
|
|
/* draw the text inside this header */
|
|
if (line1 != NULL) {
|
|
if (align1 < 0)
|
|
x = x1+3;
|
|
else if (align1 == 0)
|
|
x = (x1+x2)/2 - text_length(widget->getFont(), line1)/2;
|
|
else
|
|
x = x2 - 3 - text_length(widget->getFont(), line1);
|
|
|
|
jdraw_text(widget->getFont(), line1, x, y1+3,
|
|
fg, face, true, jguiscale());
|
|
}
|
|
|
|
if (line2 != NULL) {
|
|
if (align2 < 0)
|
|
x = x1+3;
|
|
else if (align2 == 0)
|
|
x = (x1+x2)/2 - text_length(widget->getFont(), line2)/2;
|
|
else
|
|
x = x2 - 3 - text_length(widget->getFont(), line2);
|
|
|
|
jdraw_text(widget->getFont(), line2,
|
|
x, y1+3+ji_font_get_size(widget->getFont())+3,
|
|
fg, face, true, jguiscale());
|
|
}
|
|
}
|
|
|
|
static void anieditor_draw_separator(JWidget widget, JRect clip)
|
|
{
|
|
AniEditor* anieditor = anieditor_data(widget);
|
|
bool is_hot = (anieditor->hot_part == A_PART_SEPARATOR);
|
|
int x1, y1, x2, y2;
|
|
|
|
x1 = widget->rc->x1 + anieditor->separator_x;
|
|
y1 = widget->rc->y1;
|
|
x2 = widget->rc->x1 + anieditor->separator_x + anieditor->separator_w - 1;
|
|
y2 = widget->rc->y2 - 1;
|
|
|
|
if ((x2 < clip->x1) || (x1 >= clip->x2) ||
|
|
(y2 < clip->y1) || (y1 >= clip->y2))
|
|
return;
|
|
|
|
vline(ji_screen, x1, y1, y2, is_hot ? ji_color_selected():
|
|
ji_color_foreground());
|
|
}
|
|
|
|
static void anieditor_draw_layer(JWidget widget, JRect clip, int layer_index)
|
|
{
|
|
AniEditor* anieditor = anieditor_data(widget);
|
|
Layer *layer = anieditor->layers[layer_index];
|
|
BITMAP *icon1 = get_gfx(layer->is_readable() ? GFX_BOX_SHOW: GFX_BOX_HIDE);
|
|
BITMAP *icon2 = get_gfx(layer->is_writable() ? GFX_BOX_UNLOCK: GFX_BOX_LOCK);
|
|
bool selected_layer = (layer == anieditor->sprite->getCurrentLayer());
|
|
bool is_hot = (anieditor->hot_part == A_PART_LAYER && anieditor->hot_layer == layer_index);
|
|
bool is_clk = (anieditor->clk_part == A_PART_LAYER && anieditor->clk_layer == layer_index);
|
|
int bg = selected_layer ?
|
|
ji_color_selected(): (is_hot ? ji_color_hotface():
|
|
(is_clk ? ji_color_selected():
|
|
ji_color_face()));
|
|
int fg = selected_layer ? ji_color_background(): ji_color_foreground();
|
|
int x1, y1, x2, y2, y_mid;
|
|
int cx1, cy1, cx2, cy2;
|
|
int u;
|
|
|
|
get_clip_rect(ji_screen, &cx1, &cy1, &cx2, &cy2);
|
|
|
|
x1 = widget->rc->x1;
|
|
y1 = widget->rc->y1 + HDRSIZE + LAYSIZE*layer_index - anieditor->scroll_y;
|
|
x2 = x1 + anieditor->separator_x - 1;
|
|
y2 = y1 + LAYSIZE - 1;
|
|
y_mid = (y1+y2) / 2;
|
|
|
|
add_clip_rect(ji_screen,
|
|
widget->rc->x1,
|
|
widget->rc->y1 + HDRSIZE,
|
|
widget->rc->x1 + anieditor->separator_x - 1,
|
|
widget->rc->y2-1);
|
|
|
|
if (is_hot) {
|
|
jrectedge(ji_screen, x1, y1, x2, y2-1, ji_color_facelight(), ji_color_faceshadow());
|
|
rectfill(ji_screen, x1+1, y1+1, x2-1, y2-2, bg);
|
|
}
|
|
else {
|
|
rectfill(ji_screen, x1, y1, x2, y2-1, bg);
|
|
}
|
|
hline(ji_screen, x1, y2, x2, ji_color_foreground());
|
|
|
|
/* if this layer wasn't clicked but there are another layer clicked,
|
|
we have to draw some indicators to show that the user can move
|
|
layers */
|
|
if (is_hot && !is_clk &&
|
|
anieditor->clk_part == A_PART_LAYER) {
|
|
rectfill(ji_screen, x1+1, y1+1, x2-1, y1+5, ji_color_selected());
|
|
}
|
|
|
|
/* u = the position where to put the next element (like eye-icon, lock-icon, layer-name) */
|
|
u = x1+ICONSEP;
|
|
|
|
/* draw the eye (readable flag) */
|
|
icon_rect(icon1,
|
|
u,
|
|
y_mid-icon1->h/2-ICONBORDER,
|
|
u+ICONBORDER+icon1->w+ICONBORDER-1,
|
|
y_mid-icon1->h/2+icon1->h+ICONBORDER-1,
|
|
selected_layer,
|
|
(anieditor->hot_part == A_PART_LAYER_EYE_ICON &&
|
|
anieditor->hot_layer == layer_index),
|
|
(anieditor->clk_part == A_PART_LAYER_EYE_ICON &&
|
|
anieditor->clk_layer == layer_index));
|
|
|
|
u += u+ICONBORDER+icon1->w+ICONBORDER;
|
|
|
|
/* draw the padlock (writable flag) */
|
|
icon_rect(icon2,
|
|
u,
|
|
y_mid-icon1->h/2-ICONBORDER,
|
|
u+ICONBORDER+icon2->w+ICONBORDER-1,
|
|
y_mid-icon1->h/2+icon1->h-1+ICONBORDER,
|
|
selected_layer,
|
|
(anieditor->hot_part == A_PART_LAYER_LOCK_ICON &&
|
|
anieditor->hot_layer == layer_index),
|
|
(anieditor->clk_part == A_PART_LAYER_LOCK_ICON &&
|
|
anieditor->clk_layer == layer_index));
|
|
|
|
u += ICONBORDER+icon2->w+ICONBORDER+ICONSEP;
|
|
|
|
/* draw the layer's name */
|
|
jdraw_text(widget->getFont(), layer->get_name().c_str(),
|
|
u, y_mid - ji_font_get_size(widget->getFont())/2,
|
|
fg, bg, true, jguiscale());
|
|
|
|
/* the background should be underlined */
|
|
if (layer->is_background()) {
|
|
hline(ji_screen,
|
|
u,
|
|
y_mid - ji_font_get_size(widget->getFont())/2 + ji_font_get_size(widget->getFont()) + 1,
|
|
u + text_length(widget->getFont(), layer->get_name().c_str()),
|
|
fg);
|
|
}
|
|
|
|
set_clip_rect(ji_screen, cx1, cy1, cx2, cy2);
|
|
}
|
|
|
|
static void anieditor_draw_layer_padding(JWidget widget)
|
|
{
|
|
SkinneableTheme* theme = static_cast<SkinneableTheme*>(widget->theme);
|
|
AniEditor* anieditor = anieditor_data(widget);
|
|
int layer_index = anieditor->nlayers-1;
|
|
int x1, y1, x2, y2;
|
|
|
|
x1 = widget->rc->x1;
|
|
y1 = widget->rc->y1 + HDRSIZE + LAYSIZE*layer_index - anieditor->scroll_y;
|
|
x2 = x1 + anieditor->separator_x - 1;
|
|
y2 = y1 + LAYSIZE - 1;
|
|
|
|
/* padding in the bottom side */
|
|
if (y2+1 <= widget->rc->y2-1) {
|
|
rectfill(ji_screen, x1, y2+1, x2, widget->rc->y2-1,
|
|
theme->get_editor_face_color());
|
|
rectfill(ji_screen,
|
|
x2+1+anieditor->separator_w, y2+1,
|
|
widget->rc->x2-1, widget->rc->y2-1,
|
|
theme->get_editor_face_color());
|
|
}
|
|
}
|
|
|
|
static void anieditor_draw_cel(JWidget widget, JRect clip, int layer_index, int frame)
|
|
{
|
|
SkinneableTheme* theme = static_cast<SkinneableTheme*>(widget->theme);
|
|
AniEditor* anieditor = anieditor_data(widget);
|
|
Layer *layer = anieditor->layers[layer_index];
|
|
bool selected_layer = (layer == anieditor->sprite->getCurrentLayer());
|
|
bool is_hot = (anieditor->hot_part == A_PART_CEL &&
|
|
anieditor->hot_layer == layer_index &&
|
|
anieditor->hot_frame == frame);
|
|
bool is_clk = (anieditor->clk_part == A_PART_CEL &&
|
|
anieditor->clk_layer == layer_index &&
|
|
anieditor->clk_frame == frame);
|
|
int bg = is_hot ? ji_color_hotface():
|
|
ji_color_face();
|
|
int x1, y1, x2, y2;
|
|
int cx1, cy1, cx2, cy2;
|
|
BITMAP *thumbnail;
|
|
Cel *cel;
|
|
|
|
get_clip_rect(ji_screen, &cx1, &cy1, &cx2, &cy2);
|
|
|
|
x1 = widget->rc->x1 + anieditor->separator_x + anieditor->separator_w
|
|
+ FRMSIZE*frame - anieditor->scroll_x;
|
|
y1 = widget->rc->y1 + HDRSIZE
|
|
+ LAYSIZE*layer_index - anieditor->scroll_y;
|
|
x2 = x1 + FRMSIZE - 1;
|
|
y2 = y1 + LAYSIZE - 1;
|
|
|
|
add_clip_rect(ji_screen,
|
|
widget->rc->x1 + anieditor->separator_x + anieditor->separator_w,
|
|
widget->rc->y1 + HDRSIZE,
|
|
widget->rc->x2-1,
|
|
widget->rc->y2-1);
|
|
|
|
Rect thumbnail_rect(Point(x1+3, y1+3), Point(x2-2, y2-2));
|
|
|
|
/* draw the box for the cel */
|
|
if (selected_layer && frame == anieditor->sprite->getCurrentFrame()) {
|
|
/* current cel */
|
|
if (is_hot)
|
|
jrectedge(ji_screen, x1, y1, x2, y2-1,
|
|
ji_color_facelight(), ji_color_faceshadow());
|
|
else
|
|
rect(ji_screen, x1, y1, x2, y2-1, ji_color_selected());
|
|
rect(ji_screen, x1+1, y1+1, x2-1, y2-2, ji_color_selected());
|
|
rect(ji_screen, x1+2, y1+2, x2-2, y2-3, bg);
|
|
}
|
|
else {
|
|
if (is_hot) {
|
|
jrectedge(ji_screen, x1, y1, x2, y2-1,
|
|
ji_color_facelight(), ji_color_faceshadow());
|
|
rectfill(ji_screen, x1+1, y1+1, x2-1, y2-2, bg);
|
|
}
|
|
else {
|
|
rectfill(ji_screen, x1, y1, x2, y2-1, bg);
|
|
}
|
|
}
|
|
hline(ji_screen, x1, y2, x2, ji_color_foreground());
|
|
|
|
/* get the cel of this layer in this frame */
|
|
cel = layer->is_image() ? static_cast<LayerImage*>(layer)->get_cel(frame): NULL;
|
|
|
|
/* empty cel? */
|
|
if (cel == NULL ||
|
|
stock_get_image(anieditor->sprite->getStock(),
|
|
cel->image) == NULL) { /* TODO why a cel can't have an associated image? */
|
|
jdraw_rectfill(thumbnail_rect, bg);
|
|
draw_emptyset_symbol(thumbnail_rect, ji_color_disabled());
|
|
}
|
|
else {
|
|
thumbnail = generate_thumbnail(layer, cel, anieditor->sprite);
|
|
if (thumbnail != NULL) {
|
|
stretch_blit(thumbnail, ji_screen,
|
|
0, 0, thumbnail->w, thumbnail->h,
|
|
thumbnail_rect.x, thumbnail_rect.y,
|
|
thumbnail_rect.w, thumbnail_rect.h);
|
|
}
|
|
}
|
|
|
|
/* if this cel is hot and other cel was clicked, we have to draw
|
|
some indicators to show that the user can move cels */
|
|
if (is_hot && !is_clk &&
|
|
anieditor->clk_part == A_PART_CEL) {
|
|
rectfill(ji_screen, x1+1, y1+1, x1+FRMSIZE/3, y1+4, ji_color_selected());
|
|
rectfill(ji_screen, x1+1, y1+5, x1+4, y1+FRMSIZE/3, ji_color_selected());
|
|
|
|
rectfill(ji_screen, x2-FRMSIZE/3, y1+1, x2-1, y1+4, ji_color_selected());
|
|
rectfill(ji_screen, x2-4, y1+5, x2-1, y1+FRMSIZE/3, ji_color_selected());
|
|
|
|
rectfill(ji_screen, x1+1, y2-4, x1+FRMSIZE/3, y2-1, ji_color_selected());
|
|
rectfill(ji_screen, x1+1, y2-FRMSIZE/3, x1+4, y2-5, ji_color_selected());
|
|
|
|
rectfill(ji_screen, x2-FRMSIZE/3, y2-4, x2-1, y2-1, ji_color_selected());
|
|
rectfill(ji_screen, x2-4, y2-FRMSIZE/3, x2-1, y2-5, ji_color_selected());
|
|
}
|
|
|
|
/* padding in the right side */
|
|
if (frame == anieditor->sprite->getTotalFrames()-1) {
|
|
if (x2+1 <= widget->rc->x2-1) {
|
|
/* right side */
|
|
vline(ji_screen, x2+1, y1, y2, ji_color_foreground());
|
|
if (x2+2 <= widget->rc->x2-1)
|
|
rectfill(ji_screen, x2+2, y1, widget->rc->x2-1, y2,
|
|
theme->get_editor_face_color());
|
|
}
|
|
}
|
|
|
|
set_clip_rect(ji_screen, cx1, cy1, cx2, cy2);
|
|
}
|
|
|
|
static bool anieditor_draw_part(JWidget widget, int part, int layer, int frame)
|
|
{
|
|
AniEditor* anieditor = anieditor_data(widget);
|
|
|
|
switch (part) {
|
|
case A_PART_NOTHING:
|
|
/* do nothing */
|
|
return true;
|
|
case A_PART_SEPARATOR:
|
|
anieditor_draw_separator(widget, widget->rc);
|
|
return true;
|
|
case A_PART_HEADER_LAYER:
|
|
anieditor_draw_header(widget, widget->rc);
|
|
return true;
|
|
case A_PART_HEADER_FRAME:
|
|
if (frame >= 0 && frame < anieditor->sprite->getTotalFrames()) {
|
|
anieditor_draw_header_frame(widget, widget->rc, frame);
|
|
return true;
|
|
}
|
|
break;
|
|
case A_PART_LAYER:
|
|
case A_PART_LAYER_EYE_ICON:
|
|
case A_PART_LAYER_LOCK_ICON:
|
|
if (layer >= 0 && layer < anieditor->nlayers) {
|
|
anieditor_draw_layer(widget, widget->rc, layer);
|
|
return true;
|
|
}
|
|
break;
|
|
case A_PART_CEL:
|
|
if (layer >= 0 && layer < anieditor->nlayers &&
|
|
frame >= 0 && frame < anieditor->sprite->getTotalFrames()) {
|
|
anieditor_draw_cel(widget, widget->rc, layer, frame);
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void anieditor_regenerate_layers(JWidget widget)
|
|
{
|
|
AniEditor* anieditor = anieditor_data(widget);
|
|
int c;
|
|
|
|
if (anieditor->layers != NULL) {
|
|
jfree(anieditor->layers);
|
|
anieditor->layers = NULL;
|
|
}
|
|
|
|
anieditor->nlayers = anieditor->sprite->countLayers();
|
|
|
|
/* here we build an array with all the layers */
|
|
if (anieditor->nlayers > 0) {
|
|
anieditor->layers = (Layer**)jmalloc(sizeof(Layer*) * anieditor->nlayers);
|
|
|
|
for (c=0; c<anieditor->nlayers; c++)
|
|
anieditor->layers[c] = (Layer*)anieditor->sprite->indexToLayer(anieditor->nlayers-c-1);
|
|
}
|
|
}
|
|
|
|
static void anieditor_hot_this(JWidget widget, int hot_part, int hot_layer, int hot_frame)
|
|
{
|
|
AniEditor* anieditor = anieditor_data(widget);
|
|
int old_hot_part;
|
|
|
|
/* if the part, layer or frame change */
|
|
if (hot_part != anieditor->hot_part ||
|
|
hot_layer != anieditor->hot_layer ||
|
|
hot_frame != anieditor->hot_frame) {
|
|
jmouse_hide();
|
|
|
|
/* clean the old 'hot' thing */
|
|
old_hot_part = anieditor->hot_part;
|
|
anieditor->hot_part = A_PART_NOTHING;
|
|
|
|
anieditor_draw_part(widget,
|
|
old_hot_part,
|
|
anieditor->hot_layer,
|
|
anieditor->hot_frame);
|
|
|
|
/* draw the new 'hot' thing */
|
|
anieditor->hot_part = hot_part;
|
|
anieditor->hot_layer = hot_layer;
|
|
anieditor->hot_frame = hot_frame;
|
|
|
|
if (!anieditor_draw_part(widget, hot_part, hot_layer, hot_frame)) {
|
|
anieditor->hot_part = A_PART_NOTHING;
|
|
}
|
|
|
|
jmouse_show();
|
|
}
|
|
}
|
|
|
|
static void anieditor_center_cel(JWidget widget, int layer, int frame)
|
|
{
|
|
AniEditor* anieditor = anieditor_data(widget);
|
|
int target_x = (widget->rc->x1 + anieditor->separator_x + anieditor->separator_w + widget->rc->x2)/2 - FRMSIZE/2;
|
|
int target_y = (widget->rc->y1 + HDRSIZE + widget->rc->y2)/2 - LAYSIZE/2;
|
|
int scroll_x = widget->rc->x1 + anieditor->separator_x + anieditor->separator_w + FRMSIZE*frame - target_x;
|
|
int scroll_y = widget->rc->y1 + HDRSIZE + LAYSIZE*layer - target_y;
|
|
|
|
anieditor_set_scroll(widget, scroll_x, scroll_y, false);
|
|
}
|
|
|
|
static void anieditor_show_cel(JWidget widget, int layer, int frame)
|
|
{
|
|
AniEditor* anieditor = anieditor_data(widget);
|
|
int scroll_x, scroll_y;
|
|
int x1, y1, x2, y2;
|
|
|
|
x1 = widget->rc->x1 + anieditor->separator_x + anieditor->separator_w + FRMSIZE*frame - anieditor->scroll_x;
|
|
y1 = widget->rc->y1 + HDRSIZE + LAYSIZE*layer - anieditor->scroll_y;
|
|
x2 = x1 + FRMSIZE - 1;
|
|
y2 = y1 + LAYSIZE - 1;
|
|
|
|
scroll_x = anieditor->scroll_x;
|
|
scroll_y = anieditor->scroll_y;
|
|
|
|
if (x1 < widget->rc->x1 + anieditor->separator_x + anieditor->separator_w) {
|
|
scroll_x -= (widget->rc->x1 + anieditor->separator_x + anieditor->separator_w) - (x1);
|
|
}
|
|
else if (x2 > widget->rc->x2-1) {
|
|
scroll_x += (x2) - (widget->rc->x2-1);
|
|
}
|
|
|
|
if (y1 < widget->rc->y1 + HDRSIZE) {
|
|
scroll_y -= (widget->rc->y1 + HDRSIZE) - (y1);
|
|
}
|
|
else if (y2 > widget->rc->y2-1) {
|
|
scroll_y += (y2) - (widget->rc->y2-1);
|
|
}
|
|
|
|
if (scroll_x != anieditor->scroll_x ||
|
|
scroll_y != anieditor->scroll_y)
|
|
anieditor_set_scroll(widget, scroll_x, scroll_y, true);
|
|
}
|
|
|
|
static void anieditor_show_current_cel(JWidget widget)
|
|
{
|
|
AniEditor* anieditor = anieditor_data(widget);
|
|
int layer = anieditor_get_layer_index(widget, anieditor->sprite->getCurrentLayer());
|
|
if (layer >= 0)
|
|
anieditor_show_cel(widget, layer, anieditor->sprite->getCurrentFrame());
|
|
}
|
|
|
|
static void anieditor_clean_clk(JWidget widget)
|
|
{
|
|
AniEditor* anieditor = anieditor_data(widget);
|
|
int clk_part = anieditor->clk_part;
|
|
anieditor->clk_part = A_PART_NOTHING;
|
|
|
|
anieditor_draw_part(widget,
|
|
clk_part,
|
|
anieditor->clk_layer,
|
|
anieditor->clk_frame);
|
|
}
|
|
|
|
static void anieditor_set_scroll(JWidget widget, int x, int y, bool use_refresh_region)
|
|
{
|
|
AniEditor* anieditor = anieditor_data(widget);
|
|
int old_scroll_x = 0;
|
|
int old_scroll_y = 0;
|
|
int max_scroll_x;
|
|
int max_scroll_y;
|
|
JRegion region = NULL;
|
|
|
|
if (use_refresh_region) {
|
|
region = jwidget_get_drawable_region(widget, JI_GDR_CUTTOPWINDOWS);
|
|
old_scroll_x = anieditor->scroll_x;
|
|
old_scroll_y = anieditor->scroll_y;
|
|
}
|
|
|
|
max_scroll_x = anieditor->sprite->getTotalFrames() * FRMSIZE - jrect_w(widget->rc)/2;
|
|
max_scroll_y = anieditor->nlayers * LAYSIZE - jrect_h(widget->rc)/2;
|
|
max_scroll_x = MAX(0, max_scroll_x);
|
|
max_scroll_y = MAX(0, max_scroll_y);
|
|
|
|
anieditor->scroll_x = MID(0, x, max_scroll_x);
|
|
anieditor->scroll_y = MID(0, y, max_scroll_y);
|
|
|
|
if (use_refresh_region) {
|
|
int new_scroll_x = anieditor->scroll_x;
|
|
int new_scroll_y = anieditor->scroll_y;
|
|
int dx = old_scroll_x - new_scroll_x;
|
|
int dy = old_scroll_y - new_scroll_y;
|
|
JRegion reg1 = jregion_new(NULL, 0);
|
|
JRegion reg2 = jregion_new(NULL, 0);
|
|
JRect rect2 = jrect_new(0, 0, 0, 0);
|
|
|
|
jmouse_hide();
|
|
|
|
/* scroll layers */
|
|
jrect_replace(rect2,
|
|
widget->rc->x1,
|
|
widget->rc->y1 + HDRSIZE,
|
|
widget->rc->x1 + anieditor->separator_x,
|
|
widget->rc->y2);
|
|
jregion_reset(reg2, rect2);
|
|
jregion_copy(reg1, region);
|
|
jregion_intersect(reg1, reg1, reg2);
|
|
jwidget_scroll(widget, reg1, 0, dy);
|
|
|
|
/* scroll header-frame */
|
|
jrect_replace(rect2,
|
|
widget->rc->x1 + anieditor->separator_x + anieditor->separator_w,
|
|
widget->rc->y1,
|
|
widget->rc->x2,
|
|
widget->rc->y1 + HDRSIZE);
|
|
jregion_reset(reg2, rect2);
|
|
jregion_copy(reg1, region);
|
|
jregion_intersect(reg1, reg1, reg2);
|
|
jwidget_scroll(widget, reg1, dx, 0);
|
|
|
|
/* scroll cels */
|
|
jrect_replace(rect2,
|
|
widget->rc->x1 + anieditor->separator_x + anieditor->separator_w,
|
|
widget->rc->y1 + HDRSIZE,
|
|
widget->rc->x2,
|
|
widget->rc->y2);
|
|
jregion_reset(reg2, rect2);
|
|
jregion_copy(reg1, region);
|
|
jregion_intersect(reg1, reg1, reg2);
|
|
jwidget_scroll(widget, reg1, dx, dy);
|
|
|
|
jmouse_show();
|
|
|
|
jregion_free(region);
|
|
jregion_free(reg1);
|
|
jregion_free(reg2);
|
|
jrect_free(rect2);
|
|
}
|
|
}
|
|
|
|
static int anieditor_get_layer_index(JWidget widget, const Layer* layer)
|
|
{
|
|
AniEditor* anieditor = anieditor_data(widget);
|
|
int i;
|
|
|
|
for (i=0; i<anieditor->nlayers; i++)
|
|
if (anieditor->layers[i] == layer)
|
|
return i;
|
|
|
|
return -1;
|
|
}
|
|
|
|
/* auxiliary routine to draw an icon in the layer-part */
|
|
static void icon_rect(BITMAP *icon, int x1, int y1, int x2, int y2,
|
|
bool is_selected, bool is_hot, bool is_clk)
|
|
{
|
|
int icon_x = x1+ICONBORDER;
|
|
int icon_y = (y1+y2)/2-icon->h/2;
|
|
int facelight = is_hot && is_clk ? ji_color_faceshadow(): ji_color_facelight();
|
|
int faceshadow = is_hot && is_clk ? ji_color_facelight(): ji_color_faceshadow();
|
|
|
|
if (is_hot) {
|
|
jrectedge(ji_screen, x1, y1, x2, y2, facelight, faceshadow);
|
|
|
|
if (!is_selected)
|
|
rectfill(ji_screen,
|
|
x1+1, y1+1, x2-1, y2-1,
|
|
ji_color_hotface());
|
|
}
|
|
|
|
if (is_selected)
|
|
jdraw_inverted_sprite(ji_screen, icon, icon_x, icon_y);
|
|
else
|
|
draw_sprite(ji_screen, icon, icon_x, icon_y);
|
|
}
|