mirror of
https://github.com/aseprite/aseprite.git
synced 2025-04-09 18:44:46 +00:00
1528 lines
46 KiB
C++
1528 lines
46 KiB
C++
/* ASEPRITE
|
|
* Copyright (C) 2001-2012 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 "base/compiler_specific.h"
|
|
#include "base/memory.h"
|
|
#include "commands/command.h"
|
|
#include "commands/commands.h"
|
|
#include "commands/params.h"
|
|
#include "console.h"
|
|
#include "document.h"
|
|
#include "document_wrappers.h"
|
|
#include "gfx/point.h"
|
|
#include "gfx/rect.h"
|
|
#include "modules/gfx.h"
|
|
#include "modules/gui.h"
|
|
#include "modules/rootmenu.h"
|
|
#include "raster/raster.h"
|
|
#include "skin/skin_theme.h"
|
|
#include "ui/gui.h"
|
|
#include "ui_context.h"
|
|
#include "undo/undo_history.h"
|
|
#include "undo_transaction.h"
|
|
#include "util/celmove.h"
|
|
#include "util/thmbnail.h"
|
|
|
|
#include <allegro.h>
|
|
|
|
using namespace gfx;
|
|
using namespace ui;
|
|
|
|
/*
|
|
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(this->getFont())*2 + 3 + 3)
|
|
|
|
// Width of the frames.
|
|
#define FRMSIZE (3 + THUMBSIZE + 3)
|
|
|
|
// Height of the layers.
|
|
#define LAYSIZE (3 + MAX(text_height(this->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 {
|
|
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
|
|
};
|
|
|
|
class AnimationEditor : public Widget
|
|
{
|
|
public:
|
|
enum State {
|
|
STATE_STANDBY,
|
|
STATE_SCROLLING,
|
|
STATE_MOVING_SEPARATOR,
|
|
STATE_MOVING_LAYER,
|
|
STATE_MOVING_CEL,
|
|
STATE_MOVING_FRAME,
|
|
};
|
|
|
|
AnimationEditor(const Document* document, const Sprite* sprite);
|
|
~AnimationEditor();
|
|
|
|
void centerCurrentCel();
|
|
State getState() const { return m_state; }
|
|
|
|
protected:
|
|
bool onProcessMessage(Message* msg) OVERRIDE;
|
|
|
|
private:
|
|
void setCursor(int x, int y);
|
|
void getDrawableLayers(JRect clip, int* first_layer, int* last_layer);
|
|
void getDrawableFrames(JRect clip, int* first_frame, int* last_frame);
|
|
void drawHeader(JRect clip);
|
|
void drawHeaderFrame(JRect clip, int frame);
|
|
void drawHeaderPart(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);
|
|
void drawSeparator(JRect clip);
|
|
void drawLayer(JRect clip, int layer_index);
|
|
void drawLayerPadding();
|
|
void drawCel(JRect clip, int layer_index, int frame, Cel* cel);
|
|
bool drawPart(int part, int layer, int frame);
|
|
void regenerateLayers();
|
|
void hotThis(int hot_part, int hot_layer, int hot_frame);
|
|
void centerCel(int layer, int frame);
|
|
void showCel(int layer, int frame);
|
|
void showCurrentCel();
|
|
void cleanClk();
|
|
void setScroll(int x, int y, bool use_refresh_region);
|
|
int getLayerIndex(const Layer* layer);
|
|
|
|
const Document* m_document;
|
|
const Sprite* m_sprite;
|
|
State m_state;
|
|
Layer** m_layers;
|
|
int m_nlayers;
|
|
int m_scroll_x;
|
|
int m_scroll_y;
|
|
int m_separator_x;
|
|
int m_separator_w;
|
|
// The 'hot' part is where the mouse is on top of
|
|
int m_hot_part;
|
|
int m_hot_layer;
|
|
int m_hot_frame;
|
|
// The 'clk' part is where the mouse's button was pressed (maybe for a drag & drop operation)
|
|
int m_clk_part;
|
|
int m_clk_layer;
|
|
int m_clk_frame;
|
|
// Keys
|
|
bool m_space_pressed;
|
|
};
|
|
|
|
static AnimationEditor* current_anieditor = NULL;
|
|
|
|
static void icon_rect(BITMAP* icon_normal, BITMAP* icon_selected, 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 &&
|
|
current_anieditor->getState() == AnimationEditor::STATE_MOVING_CEL;
|
|
}
|
|
|
|
// Shows the animation editor for the current sprite.
|
|
void switch_between_animation_and_sprite_editor()
|
|
{
|
|
const Document* document = UIContext::instance()->getActiveDocument();
|
|
const Sprite* sprite = document->getSprite();
|
|
|
|
// Create the window & the animation-editor
|
|
{
|
|
UniquePtr<Frame> window(new Frame(true, NULL));
|
|
AnimationEditor* anieditor = new AnimationEditor(document, sprite);
|
|
current_anieditor = anieditor;
|
|
|
|
window->addChild(anieditor);
|
|
window->remap_window();
|
|
|
|
anieditor->centerCurrentCel();
|
|
|
|
// Show the window
|
|
window->open_window_fg();
|
|
|
|
// No more animation editor
|
|
current_anieditor = NULL;
|
|
}
|
|
|
|
// Destroy thumbnails
|
|
destroy_thumbnails();
|
|
|
|
update_screen_for_document(document);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// The Animation Editor
|
|
|
|
AnimationEditor::AnimationEditor(const Document* document, const Sprite* sprite)
|
|
: Widget(JI_WIDGET)
|
|
{
|
|
m_document = document;
|
|
m_sprite = sprite;
|
|
m_state = STATE_STANDBY;
|
|
m_layers = NULL;
|
|
m_nlayers = 0;
|
|
m_scroll_x = 0;
|
|
m_scroll_y = 0;
|
|
m_separator_x = 100 * jguiscale();
|
|
m_separator_w = 1;
|
|
m_hot_part = A_PART_NOTHING;
|
|
m_clk_part = A_PART_NOTHING;
|
|
m_space_pressed = false;
|
|
|
|
this->setFocusStop(true);
|
|
|
|
regenerateLayers();
|
|
}
|
|
|
|
AnimationEditor::~AnimationEditor()
|
|
{
|
|
if (m_layers)
|
|
base_free(m_layers);
|
|
}
|
|
|
|
bool AnimationEditor::onProcessMessage(Message* msg)
|
|
{
|
|
switch (msg->type) {
|
|
|
|
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_DRAW: {
|
|
JRect clip = &msg->draw.rect;
|
|
int layer, first_layer, last_layer;
|
|
int frame, first_frame, last_frame;
|
|
|
|
getDrawableLayers(clip, &first_layer, &last_layer);
|
|
getDrawableFrames(clip, &first_frame, &last_frame);
|
|
|
|
// Draw the header for layers.
|
|
drawHeader(clip);
|
|
|
|
// Draw the header for each visible frame.
|
|
for (frame=first_frame; frame<=last_frame; frame++)
|
|
drawHeaderFrame(clip, frame);
|
|
|
|
// Draw the separator.
|
|
drawSeparator(clip);
|
|
|
|
// Draw each visible layer.
|
|
for (layer=first_layer; layer<=last_layer; layer++) {
|
|
drawLayer(clip, layer);
|
|
|
|
// Get the first CelIterator to be drawn (it is the first cel with cel->frame >= first_frame)
|
|
CelIterator it, end;
|
|
Layer* layerPtr = m_layers[layer];
|
|
if (layerPtr->is_image()) {
|
|
it = static_cast<LayerImage*>(layerPtr)->getCelBegin();
|
|
end = static_cast<LayerImage*>(layerPtr)->getCelEnd();
|
|
for (; it != end && (*it)->getFrame() < first_frame; ++it)
|
|
;
|
|
}
|
|
|
|
// Draw every visible cel for each layer.
|
|
for (frame=first_frame; frame<=last_frame; ++frame) {
|
|
Cel* cel = (layerPtr->is_image() && it != end && (*it)->getFrame() == frame ? *it: NULL);
|
|
|
|
drawCel(clip, layer, frame, cel);
|
|
|
|
if (cel)
|
|
++it; // Go to next cel
|
|
}
|
|
}
|
|
|
|
drawLayerPadding();
|
|
|
|
return true;
|
|
}
|
|
|
|
case JM_TIMER:
|
|
break;
|
|
|
|
case JM_MOUSEENTER:
|
|
if (key[KEY_SPACE]) m_space_pressed = true;
|
|
break;
|
|
|
|
case JM_MOUSELEAVE:
|
|
if (m_space_pressed) m_space_pressed = false;
|
|
break;
|
|
|
|
case JM_BUTTONPRESSED:
|
|
if (msg->mouse.middle || m_space_pressed) {
|
|
captureMouse();
|
|
m_state = STATE_SCROLLING;
|
|
return true;
|
|
}
|
|
|
|
// Clicked-part = hot-part.
|
|
m_clk_part = m_hot_part;
|
|
m_clk_layer = m_hot_layer;
|
|
m_clk_frame = m_hot_frame;
|
|
|
|
switch (m_hot_part) {
|
|
case A_PART_NOTHING:
|
|
// Do nothing.
|
|
break;
|
|
case A_PART_SEPARATOR:
|
|
captureMouse();
|
|
m_state = STATE_MOVING_SEPARATOR;
|
|
break;
|
|
case A_PART_HEADER_LAYER:
|
|
// Do nothing.
|
|
break;
|
|
case A_PART_HEADER_FRAME:
|
|
{
|
|
const DocumentReader document(const_cast<Document*>(m_document));
|
|
DocumentWriter document_writer(document);
|
|
document_writer->getSprite()->setCurrentFrame(m_clk_frame);
|
|
}
|
|
invalidate(); // TODO Replace this by redrawing old current frame and new current frame
|
|
captureMouse();
|
|
m_state = STATE_MOVING_FRAME;
|
|
break;
|
|
case A_PART_LAYER: {
|
|
const DocumentReader document(const_cast<Document*>(m_document));
|
|
const Sprite* sprite = m_sprite;
|
|
int old_layer = getLayerIndex(sprite->getCurrentLayer());
|
|
int frame = m_sprite->getCurrentFrame();
|
|
|
|
// Did the user select another layer?
|
|
if (old_layer != m_clk_layer) {
|
|
{
|
|
DocumentWriter document_writer(document);
|
|
Sprite* sprite_writer = const_cast<Sprite*>(m_sprite);
|
|
sprite_writer->setCurrentLayer(m_layers[m_clk_layer]);
|
|
}
|
|
|
|
jmouse_hide();
|
|
// Redraw the old & new selected cel.
|
|
drawPart(A_PART_CEL, old_layer, frame);
|
|
drawPart(A_PART_CEL, m_clk_layer, frame);
|
|
// Redraw the old selected layer.
|
|
drawPart(A_PART_LAYER, old_layer, frame);
|
|
jmouse_show();
|
|
}
|
|
|
|
// Change the scroll to show the new selected cel.
|
|
showCel(m_clk_layer, sprite->getCurrentFrame());
|
|
captureMouse();
|
|
m_state = STATE_MOVING_LAYER;
|
|
break;
|
|
}
|
|
case A_PART_LAYER_EYE_ICON:
|
|
captureMouse();
|
|
break;
|
|
case A_PART_LAYER_LOCK_ICON:
|
|
captureMouse();
|
|
break;
|
|
case A_PART_CEL: {
|
|
const DocumentReader document(const_cast<Document*>(m_document));
|
|
const Sprite* sprite = document->getSprite();
|
|
int old_layer = getLayerIndex(sprite->getCurrentLayer());
|
|
int old_frame = sprite->getCurrentFrame();
|
|
|
|
// Select the new clicked-part.
|
|
if (old_layer != m_clk_layer ||
|
|
old_frame != m_clk_frame) {
|
|
{
|
|
DocumentWriter document_writer(document);
|
|
Sprite* sprite_writer = document_writer->getSprite();
|
|
sprite_writer->setCurrentLayer(m_layers[m_clk_layer]);
|
|
sprite_writer->setCurrentFrame(m_clk_frame);
|
|
}
|
|
|
|
jmouse_hide();
|
|
// Redraw the old & new selected layer.
|
|
if (old_layer != m_clk_layer) {
|
|
drawPart(A_PART_LAYER, old_layer, old_frame);
|
|
drawPart(A_PART_LAYER,
|
|
m_clk_layer,
|
|
m_clk_frame);
|
|
}
|
|
// Redraw the old selected cel.
|
|
drawPart(A_PART_CEL, old_layer, old_frame);
|
|
jmouse_show();
|
|
}
|
|
|
|
// Change the scroll to show the new selected cel.
|
|
showCel(m_clk_layer, sprite->getCurrentFrame());
|
|
|
|
// Capture the mouse (to move the cel).
|
|
captureMouse();
|
|
m_state = STATE_MOVING_CEL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Redraw the new selected part (header, layer or cel).
|
|
jmouse_hide();
|
|
drawPart(m_clk_part,
|
|
m_clk_layer,
|
|
m_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 - rc->x1;
|
|
int my = msg->mouse.y - rc->y1;
|
|
|
|
if (hasCapture()) {
|
|
if (m_state == STATE_SCROLLING) {
|
|
setScroll(m_scroll_x+jmouse_x(1)-jmouse_x(0),
|
|
m_scroll_y+jmouse_y(1)-jmouse_y(0), true);
|
|
|
|
jmouse_control_infinite_scroll(getBounds());
|
|
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 (m_clk_part == A_PART_SEPARATOR) {
|
|
hot_part = m_clk_part;
|
|
m_separator_x = mx;
|
|
invalidate();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Is the mouse on the separator.
|
|
if (mx > m_separator_x-4 && mx < m_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 < m_separator_x)
|
|
hot_part = A_PART_HEADER_LAYER;
|
|
// Is on a frame header?
|
|
else {
|
|
hot_part = A_PART_HEADER_FRAME;
|
|
hot_frame = (mx
|
|
- m_separator_x
|
|
- m_separator_w
|
|
+ m_scroll_x) / FRMSIZE;
|
|
}
|
|
}
|
|
else {
|
|
hot_layer = (my
|
|
- HDRSIZE
|
|
+ m_scroll_y) / LAYSIZE;
|
|
|
|
// Is the mouse on a layer's label?
|
|
if (mx < m_separator_x) {
|
|
SkinTheme* theme = static_cast<SkinTheme*>(getTheme());
|
|
BITMAP* icon1 = theme->get_part(PART_LAYER_VISIBLE);
|
|
BITMAP* icon2 = theme->get_part(PART_LAYER_EDITABLE);
|
|
int x1, y1, x2, y2, y_mid;
|
|
|
|
x1 = 0;
|
|
y1 = HDRSIZE + LAYSIZE*hot_layer - m_scroll_y;
|
|
x2 = x1 + m_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
|
|
- m_separator_x
|
|
- m_separator_w
|
|
+ m_scroll_x) / FRMSIZE;
|
|
}
|
|
}
|
|
|
|
// Set the new 'hot' thing.
|
|
hotThis(hot_part, hot_layer, hot_frame);
|
|
return true;
|
|
}
|
|
|
|
case JM_BUTTONRELEASED:
|
|
if (hasCapture()) {
|
|
releaseMouse();
|
|
|
|
if (m_state == STATE_SCROLLING) {
|
|
m_state = STATE_STANDBY;
|
|
return true;
|
|
}
|
|
|
|
switch (m_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 (m_clk_frame == m_hot_frame) {
|
|
Menu* popup_menu = get_frame_popup_menu();
|
|
if (popup_menu != NULL) {
|
|
popup_menu->showPopup(msg->mouse.x, msg->mouse.y);
|
|
|
|
destroy_thumbnails();
|
|
invalidate();
|
|
}
|
|
}
|
|
}
|
|
// Show the frame's properties dialog.
|
|
else if (msg->mouse.left) {
|
|
if (m_clk_frame == m_hot_frame) {
|
|
// Execute FrameProperties command for current frame.
|
|
Command* command = CommandsModule::instance()
|
|
->getCommandByName(CommandId::FrameProperties);
|
|
Params params;
|
|
params.set("frame", "current");
|
|
|
|
UIContext::instance()->executeCommand(command, ¶ms);
|
|
}
|
|
else {
|
|
const DocumentReader document(const_cast<Document*>(m_document));
|
|
const Sprite* sprite = m_sprite;
|
|
|
|
if (m_hot_frame >= 0 &&
|
|
m_hot_frame < sprite->getTotalFrames() &&
|
|
m_hot_frame != m_clk_frame+1) {
|
|
{
|
|
DocumentWriter document_writer(document);
|
|
UndoTransaction undoTransaction(document_writer, "Move Frame");
|
|
undoTransaction.moveFrameBefore(m_clk_frame, m_hot_frame);
|
|
undoTransaction.commit();
|
|
}
|
|
invalidate();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case A_PART_LAYER:
|
|
// Show the layer pop-up menu.
|
|
if (msg->mouse.right) {
|
|
if (m_clk_layer == m_hot_layer) {
|
|
Menu* popup_menu = get_layer_popup_menu();
|
|
if (popup_menu != NULL) {
|
|
popup_menu->showPopup(msg->mouse.x, msg->mouse.y);
|
|
|
|
destroy_thumbnails();
|
|
invalidate();
|
|
regenerateLayers();
|
|
}
|
|
}
|
|
}
|
|
// Move a layer.
|
|
else if (msg->mouse.left) {
|
|
if (m_hot_layer >= 0 &&
|
|
m_hot_layer < m_nlayers &&
|
|
m_hot_layer != m_clk_layer &&
|
|
m_hot_layer != m_clk_layer+1) {
|
|
if (!m_layers[m_clk_layer]->is_background()) {
|
|
// move the clicked-layer after the hot-layer
|
|
try {
|
|
const DocumentReader document(const_cast<Document*>(m_document));
|
|
DocumentWriter document_writer(document);
|
|
Sprite* sprite_writer = const_cast<Sprite*>(m_sprite);
|
|
|
|
UndoTransaction undoTransaction(document_writer, "Move Layer");
|
|
undoTransaction.moveLayerAfter(m_layers[m_clk_layer],
|
|
m_layers[m_hot_layer]);
|
|
undoTransaction.commit();
|
|
|
|
// Select the new layer.
|
|
sprite_writer->setCurrentLayer(m_layers[m_clk_layer]);
|
|
}
|
|
catch (LockedDocumentException& e) {
|
|
Console::showException(e);
|
|
}
|
|
|
|
invalidate();
|
|
regenerateLayers();
|
|
}
|
|
else {
|
|
Alert::show(PACKAGE "<<You can't move the `Background' layer.||&OK");
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case A_PART_LAYER_EYE_ICON:
|
|
// Hide/show layer.
|
|
if (m_hot_layer == m_clk_layer &&
|
|
m_hot_layer >= 0 &&
|
|
m_hot_layer < m_nlayers) {
|
|
Layer* layer = m_layers[m_clk_layer];
|
|
ASSERT(layer != NULL);
|
|
layer->set_readable(!layer->is_readable());
|
|
}
|
|
break;
|
|
case A_PART_LAYER_LOCK_ICON:
|
|
// Lock/unlock layer.
|
|
if (m_hot_layer == m_clk_layer &&
|
|
m_hot_layer >= 0 &&
|
|
m_hot_layer < m_nlayers) {
|
|
Layer* layer = m_layers[m_clk_layer];
|
|
ASSERT(layer != NULL);
|
|
layer->set_writable(!layer->is_writable());
|
|
}
|
|
break;
|
|
case A_PART_CEL: {
|
|
bool movement =
|
|
m_clk_part == A_PART_CEL &&
|
|
m_hot_part == A_PART_CEL &&
|
|
(m_clk_layer != m_hot_layer ||
|
|
m_clk_frame != m_hot_frame);
|
|
|
|
if (movement) {
|
|
set_frame_to_handle
|
|
(// Source cel.
|
|
m_layers[m_clk_layer],
|
|
m_clk_frame,
|
|
// Destination cel.
|
|
m_layers[m_hot_layer],
|
|
m_hot_frame);
|
|
}
|
|
|
|
// Show the cel pop-up menu.
|
|
if (msg->mouse.right) {
|
|
Menu* popup_menu = movement ? get_cel_movement_popup_menu():
|
|
get_cel_popup_menu();
|
|
if (popup_menu != NULL) {
|
|
popup_menu->showPopup(msg->mouse.x, msg->mouse.y);
|
|
|
|
destroy_thumbnails();
|
|
regenerateLayers();
|
|
invalidate();
|
|
}
|
|
}
|
|
// Move the cel.
|
|
else if (msg->mouse.left) {
|
|
if (movement) {
|
|
{
|
|
const DocumentReader document(const_cast<Document*>(m_document));
|
|
DocumentWriter document_writer(document);
|
|
move_cel(document_writer);
|
|
}
|
|
|
|
destroy_thumbnails();
|
|
regenerateLayers();
|
|
invalidate();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Clean the clicked-part & redraw the hot-part.
|
|
jmouse_hide();
|
|
cleanClk();
|
|
drawPart(m_hot_part,
|
|
m_hot_layer,
|
|
m_hot_frame);
|
|
jmouse_show();
|
|
|
|
// Restore the cursor.
|
|
m_state = STATE_STANDBY;
|
|
setCursor(msg->mouse.x, msg->mouse.y);
|
|
return true;
|
|
}
|
|
break;
|
|
|
|
case JM_KEYPRESSED: {
|
|
Command* command = NULL;
|
|
Params* params = NULL;
|
|
get_command_from_key_message(msg, &command, ¶ms);
|
|
|
|
// Close animation editor.
|
|
if ((command && (strcmp(command->short_name(), CommandId::FilmEditor) == 0)) ||
|
|
(msg->key.scancode == KEY_ESC)) {
|
|
closeWindow();
|
|
return true;
|
|
}
|
|
|
|
// Undo.
|
|
if (command && strcmp(command->short_name(), CommandId::Undo) == 0) {
|
|
const DocumentReader document(const_cast<Document*>(m_document));
|
|
const undo::UndoHistory* undo = document->getUndoHistory();
|
|
|
|
if (undo->canUndo()) {
|
|
DocumentWriter document_writer(document);
|
|
undo::UndoHistory* undo_writer = document_writer->getUndoHistory();
|
|
|
|
undo_writer->doUndo();
|
|
|
|
destroy_thumbnails();
|
|
regenerateLayers();
|
|
showCurrentCel();
|
|
invalidate();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Redo.
|
|
if (command && strcmp(command->short_name(), CommandId::Redo) == 0) {
|
|
const DocumentReader document(const_cast<Document*>(m_document));
|
|
const undo::UndoHistory* undo = document->getUndoHistory();
|
|
|
|
if (undo->canRedo()) {
|
|
DocumentWriter document_writer(document);
|
|
undo::UndoHistory* undo_writer = document_writer->getUndoHistory();
|
|
|
|
undo_writer->doRedo();
|
|
|
|
destroy_thumbnails();
|
|
regenerateLayers();
|
|
showCurrentCel();
|
|
invalidate();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// New_frame, remove_frame, new_cel, remove_cel.
|
|
if (command != NULL) {
|
|
if (strcmp(command->short_name(), CommandId::NewFrame) == 0 ||
|
|
strcmp(command->short_name(), CommandId::RemoveCel) == 0 ||
|
|
strcmp(command->short_name(), CommandId::RemoveFrame) == 0 ||
|
|
strcmp(command->short_name(), CommandId::GotoFirstFrame) == 0 ||
|
|
strcmp(command->short_name(), CommandId::GotoPreviousFrame) == 0 ||
|
|
strcmp(command->short_name(), CommandId::GotoPreviousLayer) == 0 ||
|
|
strcmp(command->short_name(), CommandId::GotoNextFrame) == 0 ||
|
|
strcmp(command->short_name(), CommandId::GotoNextLayer) == 0 ||
|
|
strcmp(command->short_name(), CommandId::GotoLastFrame) == 0) {
|
|
// execute the command
|
|
UIContext::instance()->executeCommand(command, params);
|
|
|
|
showCurrentCel();
|
|
invalidate();
|
|
return true;
|
|
}
|
|
|
|
if (strcmp(command->short_name(), CommandId::NewLayer) == 0 ||
|
|
strcmp(command->short_name(), CommandId::RemoveLayer) == 0) {
|
|
// execute the command
|
|
UIContext::instance()->executeCommand(command);
|
|
|
|
regenerateLayers();
|
|
showCurrentCel();
|
|
invalidate();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
switch (msg->key.scancode) {
|
|
case KEY_SPACE:
|
|
m_space_pressed = true;
|
|
setCursor(jmouse_x(0), jmouse_y(0));
|
|
return true;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case JM_KEYRELEASED:
|
|
switch (msg->key.scancode) {
|
|
|
|
case KEY_SPACE:
|
|
if (m_space_pressed) {
|
|
// We have to clear all the KEY_SPACE in buffer.
|
|
clear_keybuf();
|
|
|
|
m_space_pressed = false;
|
|
setCursor(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;
|
|
}
|
|
|
|
setScroll(m_scroll_x+dx,
|
|
m_scroll_y+dy, true);
|
|
break;
|
|
}
|
|
|
|
case JM_SETCURSOR:
|
|
setCursor(msg->mouse.x, msg->mouse.y);
|
|
return true;
|
|
|
|
}
|
|
|
|
return Widget::onProcessMessage(msg);
|
|
}
|
|
|
|
void AnimationEditor::setCursor(int x, int y)
|
|
{
|
|
int mx = x - rc->x1;
|
|
//int my = y - this->rc->y1;
|
|
|
|
// Is the mouse in the separator.
|
|
if (mx > m_separator_x-2 && mx < m_separator_x+2) {
|
|
jmouse_set_cursor(JI_CURSOR_SIZE_L);
|
|
}
|
|
// Scrolling.
|
|
else if (m_state == STATE_SCROLLING ||
|
|
m_space_pressed) {
|
|
jmouse_set_cursor(JI_CURSOR_SCROLL);
|
|
}
|
|
// Moving a frame.
|
|
else if (m_state == STATE_MOVING_FRAME &&
|
|
m_clk_part == A_PART_HEADER_FRAME &&
|
|
m_hot_part == A_PART_HEADER_FRAME &&
|
|
m_clk_frame != m_hot_frame) {
|
|
jmouse_set_cursor(JI_CURSOR_MOVE);
|
|
}
|
|
// Moving a layer.
|
|
else if (m_state == STATE_MOVING_LAYER &&
|
|
m_clk_part == A_PART_LAYER &&
|
|
m_hot_part == A_PART_LAYER &&
|
|
m_clk_layer != m_hot_layer) {
|
|
if (m_layers[m_clk_layer]->is_background())
|
|
jmouse_set_cursor(JI_CURSOR_FORBIDDEN);
|
|
else
|
|
jmouse_set_cursor(JI_CURSOR_MOVE);
|
|
}
|
|
// Moving a cel.
|
|
else if (m_state == STATE_MOVING_CEL &&
|
|
m_clk_part == A_PART_CEL &&
|
|
m_hot_part == A_PART_CEL &&
|
|
(m_clk_frame != m_hot_frame ||
|
|
m_clk_layer != m_hot_layer)) {
|
|
jmouse_set_cursor(JI_CURSOR_MOVE);
|
|
}
|
|
// Normal state.
|
|
else {
|
|
jmouse_set_cursor(JI_CURSOR_NORMAL);
|
|
}
|
|
}
|
|
|
|
void AnimationEditor::getDrawableLayers(JRect clip, int *first_layer, int *last_layer)
|
|
{
|
|
*first_layer = 0;
|
|
*last_layer = m_nlayers-1;
|
|
}
|
|
|
|
void AnimationEditor::getDrawableFrames(JRect clip, int *first_frame, int *last_frame)
|
|
{
|
|
*first_frame = 0;
|
|
*last_frame = m_sprite->getTotalFrames()-1;
|
|
}
|
|
|
|
void AnimationEditor::drawHeader(JRect clip)
|
|
{
|
|
// bool is_hot = (m_hot_part == A_PART_HEADER_LAYER);
|
|
// bool is_clk = (m_clk_part == A_PART_HEADER_LAYER);
|
|
int x1, y1, x2, y2;
|
|
|
|
x1 = this->rc->x1;
|
|
y1 = this->rc->y1;
|
|
x2 = x1 + m_separator_x - 1;
|
|
y2 = y1 + HDRSIZE - 1;
|
|
|
|
// Draw the header for the layers.
|
|
drawHeaderPart(clip, x1, y1, x2, y2,
|
|
// is_hot, is_clk,
|
|
false, false,
|
|
"Frames >>", 1,
|
|
"Layers", -1);
|
|
}
|
|
|
|
void AnimationEditor::drawHeaderFrame(JRect clip, int frame)
|
|
{
|
|
bool is_hot = (m_hot_part == A_PART_HEADER_FRAME &&
|
|
m_hot_frame == frame);
|
|
bool is_clk = (m_clk_part == A_PART_HEADER_FRAME &&
|
|
m_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 = this->rc->x1 + m_separator_x + m_separator_w
|
|
+ FRMSIZE*frame - m_scroll_x;
|
|
y1 = this->rc->y1;
|
|
x2 = x1 + FRMSIZE - 1;
|
|
y2 = y1 + HDRSIZE - 1;
|
|
|
|
add_clip_rect(ji_screen,
|
|
this->rc->x1 + m_separator_x + m_separator_w,
|
|
y1, this->rc->x2-1, y2);
|
|
|
|
// Draw the header for the layers.
|
|
usprintf(buf1, "%d", frame+1);
|
|
usprintf(buf2, "%d", m_sprite->getFrameDuration(frame));
|
|
drawHeaderPart(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 &&
|
|
m_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 == m_sprite->getTotalFrames()-1) {
|
|
if (x2+1 <= this->rc->x2-1) {
|
|
// Right side.
|
|
vline(ji_screen, x2+1, y1, y2, ji_color_foreground());
|
|
if (x2+2 <= this->rc->x2-1)
|
|
rectfill(ji_screen, x2+2, y1, this->rc->x2-1, y2, ji_color_face());
|
|
}
|
|
}
|
|
|
|
set_clip_rect(ji_screen, cx1, cy1, cx2, cy2);
|
|
}
|
|
|
|
void AnimationEditor::drawHeaderPart(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(this->getFont(), line1)/2;
|
|
else
|
|
x = x2 - 3 - text_length(this->getFont(), line1);
|
|
|
|
jdraw_text(ji_screen, this->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(this->getFont(), line2)/2;
|
|
else
|
|
x = x2 - 3 - text_length(this->getFont(), line2);
|
|
|
|
jdraw_text(ji_screen, this->getFont(), line2,
|
|
x, y1+3+ji_font_get_size(this->getFont())+3,
|
|
fg, face, true, jguiscale());
|
|
}
|
|
}
|
|
|
|
void AnimationEditor::drawSeparator(JRect clip)
|
|
{
|
|
bool is_hot = (m_hot_part == A_PART_SEPARATOR);
|
|
int x1, y1, x2, y2;
|
|
|
|
x1 = this->rc->x1 + m_separator_x;
|
|
y1 = this->rc->y1;
|
|
x2 = this->rc->x1 + m_separator_x + m_separator_w - 1;
|
|
y2 = this->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());
|
|
}
|
|
|
|
void AnimationEditor::drawLayer(JRect clip, int layer_index)
|
|
{
|
|
Layer *layer = m_layers[layer_index];
|
|
SkinTheme* theme = static_cast<SkinTheme*>(this->getTheme());
|
|
bool selected_layer = (layer == m_sprite->getCurrentLayer());
|
|
bool is_hot = (m_hot_part == A_PART_LAYER && m_hot_layer == layer_index);
|
|
bool is_clk = (m_clk_part == A_PART_LAYER && m_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();
|
|
BITMAP* icon1 = theme->get_part(layer->is_readable() ? PART_LAYER_VISIBLE: PART_LAYER_HIDDEN);
|
|
BITMAP* icon2 = theme->get_part(layer->is_writable() ? PART_LAYER_EDITABLE: PART_LAYER_LOCKED);
|
|
BITMAP* icon1_selected = theme->get_part(layer->is_readable() ? PART_LAYER_VISIBLE_SELECTED: PART_LAYER_HIDDEN_SELECTED);
|
|
BITMAP* icon2_selected = theme->get_part(layer->is_writable() ? PART_LAYER_EDITABLE_SELECTED: PART_LAYER_LOCKED_SELECTED);
|
|
int x1, y1, x2, y2, y_mid;
|
|
int cx1, cy1, cx2, cy2;
|
|
int u;
|
|
|
|
get_clip_rect(ji_screen, &cx1, &cy1, &cx2, &cy2);
|
|
|
|
x1 = this->rc->x1;
|
|
y1 = this->rc->y1 + HDRSIZE + LAYSIZE*layer_index - m_scroll_y;
|
|
x2 = x1 + m_separator_x - 1;
|
|
y2 = y1 + LAYSIZE - 1;
|
|
y_mid = (y1+y2) / 2;
|
|
|
|
add_clip_rect(ji_screen,
|
|
this->rc->x1,
|
|
this->rc->y1 + HDRSIZE,
|
|
this->rc->x1 + m_separator_x - 1,
|
|
this->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 &&
|
|
m_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, icon1_selected,
|
|
u,
|
|
y_mid-icon1->h/2-ICONBORDER,
|
|
u+ICONBORDER+icon1->w+ICONBORDER-1,
|
|
y_mid-icon1->h/2+icon1->h+ICONBORDER-1,
|
|
selected_layer,
|
|
(m_hot_part == A_PART_LAYER_EYE_ICON &&
|
|
m_hot_layer == layer_index),
|
|
(m_clk_part == A_PART_LAYER_EYE_ICON &&
|
|
m_clk_layer == layer_index));
|
|
|
|
u += u+ICONBORDER+icon1->w+ICONBORDER;
|
|
|
|
// Draw the padlock (writable flag).
|
|
icon_rect(icon2, icon2_selected,
|
|
u,
|
|
y_mid-icon1->h/2-ICONBORDER,
|
|
u+ICONBORDER+icon2->w+ICONBORDER-1,
|
|
y_mid-icon1->h/2+icon1->h-1+ICONBORDER,
|
|
selected_layer,
|
|
(m_hot_part == A_PART_LAYER_LOCK_ICON &&
|
|
m_hot_layer == layer_index),
|
|
(m_clk_part == A_PART_LAYER_LOCK_ICON &&
|
|
m_clk_layer == layer_index));
|
|
|
|
u += ICONBORDER+icon2->w+ICONBORDER+ICONSEP;
|
|
|
|
// Draw the layer's name.
|
|
jdraw_text(ji_screen, this->getFont(), layer->getName().c_str(),
|
|
u, y_mid - ji_font_get_size(this->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(this->getFont())/2 + ji_font_get_size(this->getFont()) + 1,
|
|
u + text_length(this->getFont(), layer->getName().c_str()),
|
|
fg);
|
|
}
|
|
|
|
set_clip_rect(ji_screen, cx1, cy1, cx2, cy2);
|
|
}
|
|
|
|
void AnimationEditor::drawLayerPadding()
|
|
{
|
|
SkinTheme* theme = static_cast<SkinTheme*>(this->getTheme());
|
|
int layer_index = m_nlayers-1;
|
|
int x1, y1, x2, y2;
|
|
|
|
x1 = this->rc->x1;
|
|
y1 = this->rc->y1 + HDRSIZE + LAYSIZE*layer_index - m_scroll_y;
|
|
x2 = x1 + m_separator_x - 1;
|
|
y2 = y1 + LAYSIZE - 1;
|
|
|
|
// Padding in the bottom side.
|
|
if (y2+1 <= this->rc->y2-1) {
|
|
rectfill(ji_screen, x1, y2+1, x2, this->rc->y2-1,
|
|
theme->get_editor_face_color());
|
|
rectfill(ji_screen,
|
|
x2+1+m_separator_w, y2+1,
|
|
this->rc->x2-1, this->rc->y2-1,
|
|
theme->get_editor_face_color());
|
|
}
|
|
}
|
|
|
|
void AnimationEditor::drawCel(JRect clip, int layer_index, int frame, Cel* cel)
|
|
{
|
|
SkinTheme* theme = static_cast<SkinTheme*>(this->getTheme());
|
|
Layer *layer = m_layers[layer_index];
|
|
bool selected_layer = (layer == m_sprite->getCurrentLayer());
|
|
bool is_hot = (m_hot_part == A_PART_CEL &&
|
|
m_hot_layer == layer_index &&
|
|
m_hot_frame == frame);
|
|
bool is_clk = (m_clk_part == A_PART_CEL &&
|
|
m_clk_layer == layer_index &&
|
|
m_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;
|
|
|
|
get_clip_rect(ji_screen, &cx1, &cy1, &cx2, &cy2);
|
|
|
|
x1 = this->rc->x1 + m_separator_x + m_separator_w
|
|
+ FRMSIZE*frame - m_scroll_x;
|
|
y1 = this->rc->y1 + HDRSIZE
|
|
+ LAYSIZE*layer_index - m_scroll_y;
|
|
x2 = x1 + FRMSIZE - 1;
|
|
y2 = y1 + LAYSIZE - 1;
|
|
|
|
add_clip_rect(ji_screen,
|
|
this->rc->x1 + m_separator_x + m_separator_w,
|
|
this->rc->y1 + HDRSIZE,
|
|
this->rc->x2-1,
|
|
this->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 == m_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());
|
|
|
|
// Empty cel?.
|
|
if (cel == NULL ||
|
|
// TODO why a cel can't have an associated image?
|
|
m_sprite->getStock()->getImage(cel->getImage()) == NULL) {
|
|
|
|
jdraw_rectfill(thumbnail_rect, bg);
|
|
draw_emptyset_symbol(ji_screen, thumbnail_rect, ji_color_disabled());
|
|
}
|
|
else {
|
|
thumbnail = generate_thumbnail(layer, cel, m_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 &&
|
|
m_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 == m_sprite->getTotalFrames()-1) {
|
|
if (x2+1 <= this->rc->x2-1) {
|
|
// Right side.
|
|
vline(ji_screen, x2+1, y1, y2, ji_color_foreground());
|
|
if (x2+2 <= this->rc->x2-1)
|
|
rectfill(ji_screen, x2+2, y1, this->rc->x2-1, y2,
|
|
theme->get_editor_face_color());
|
|
}
|
|
}
|
|
|
|
set_clip_rect(ji_screen, cx1, cy1, cx2, cy2);
|
|
}
|
|
|
|
bool AnimationEditor::drawPart(int part, int layer, int frame)
|
|
{
|
|
switch (part) {
|
|
case A_PART_NOTHING:
|
|
// Do nothing.
|
|
return true;
|
|
case A_PART_SEPARATOR:
|
|
drawSeparator(this->rc);
|
|
return true;
|
|
case A_PART_HEADER_LAYER:
|
|
drawHeader(this->rc);
|
|
return true;
|
|
case A_PART_HEADER_FRAME:
|
|
if (frame >= 0 && frame < m_sprite->getTotalFrames()) {
|
|
drawHeaderFrame(this->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 < m_nlayers) {
|
|
drawLayer(this->rc, layer);
|
|
return true;
|
|
}
|
|
break;
|
|
case A_PART_CEL:
|
|
if (layer >= 0 && layer < m_nlayers &&
|
|
frame >= 0 && frame < m_sprite->getTotalFrames()) {
|
|
Cel* cel = (m_layers[layer]->is_image() ? static_cast<LayerImage*>(m_layers[layer])->getCel(frame): NULL);
|
|
|
|
drawCel(this->rc, layer, frame, cel);
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void AnimationEditor::regenerateLayers()
|
|
{
|
|
int c;
|
|
|
|
if (m_layers != NULL) {
|
|
base_free(m_layers);
|
|
m_layers = NULL;
|
|
}
|
|
|
|
m_nlayers = m_sprite->countLayers();
|
|
|
|
// Here we build an array with all the layers.
|
|
if (m_nlayers > 0) {
|
|
m_layers = (Layer**)base_malloc(sizeof(Layer*) * m_nlayers);
|
|
|
|
for (c=0; c<m_nlayers; c++)
|
|
m_layers[c] = (Layer*)m_sprite->indexToLayer(m_nlayers-c-1);
|
|
}
|
|
}
|
|
|
|
void AnimationEditor::hotThis(int hot_part, int hot_layer, int hot_frame)
|
|
{
|
|
int old_hot_part;
|
|
|
|
// If the part, layer or frame change.
|
|
if (hot_part != m_hot_part ||
|
|
hot_layer != m_hot_layer ||
|
|
hot_frame != m_hot_frame) {
|
|
jmouse_hide();
|
|
|
|
// Clean the old 'hot' thing.
|
|
old_hot_part = m_hot_part;
|
|
m_hot_part = A_PART_NOTHING;
|
|
|
|
drawPart(old_hot_part,
|
|
m_hot_layer,
|
|
m_hot_frame);
|
|
|
|
// Draw the new 'hot' thing.
|
|
m_hot_part = hot_part;
|
|
m_hot_layer = hot_layer;
|
|
m_hot_frame = hot_frame;
|
|
|
|
if (!drawPart(hot_part, hot_layer, hot_frame)) {
|
|
m_hot_part = A_PART_NOTHING;
|
|
}
|
|
|
|
jmouse_show();
|
|
}
|
|
}
|
|
|
|
void AnimationEditor::centerCel(int layer, int frame)
|
|
{
|
|
int target_x = (this->rc->x1 + m_separator_x + m_separator_w + this->rc->x2)/2 - FRMSIZE/2;
|
|
int target_y = (this->rc->y1 + HDRSIZE + this->rc->y2)/2 - LAYSIZE/2;
|
|
int scroll_x = this->rc->x1 + m_separator_x + m_separator_w + FRMSIZE*frame - target_x;
|
|
int scroll_y = this->rc->y1 + HDRSIZE + LAYSIZE*layer - target_y;
|
|
|
|
setScroll(scroll_x, scroll_y, false);
|
|
}
|
|
|
|
void AnimationEditor::showCel(int layer, int frame)
|
|
{
|
|
int scroll_x, scroll_y;
|
|
int x1, y1, x2, y2;
|
|
|
|
x1 = this->rc->x1 + m_separator_x + m_separator_w + FRMSIZE*frame - m_scroll_x;
|
|
y1 = this->rc->y1 + HDRSIZE + LAYSIZE*layer - m_scroll_y;
|
|
x2 = x1 + FRMSIZE - 1;
|
|
y2 = y1 + LAYSIZE - 1;
|
|
|
|
scroll_x = m_scroll_x;
|
|
scroll_y = m_scroll_y;
|
|
|
|
if (x1 < this->rc->x1 + m_separator_x + m_separator_w) {
|
|
scroll_x -= (this->rc->x1 + m_separator_x + m_separator_w) - (x1);
|
|
}
|
|
else if (x2 > this->rc->x2-1) {
|
|
scroll_x += (x2) - (this->rc->x2-1);
|
|
}
|
|
|
|
if (y1 < this->rc->y1 + HDRSIZE) {
|
|
scroll_y -= (this->rc->y1 + HDRSIZE) - (y1);
|
|
}
|
|
else if (y2 > this->rc->y2-1) {
|
|
scroll_y += (y2) - (this->rc->y2-1);
|
|
}
|
|
|
|
if (scroll_x != m_scroll_x ||
|
|
scroll_y != m_scroll_y)
|
|
setScroll(scroll_x, scroll_y, true);
|
|
}
|
|
|
|
void AnimationEditor::centerCurrentCel()
|
|
{
|
|
int layer = getLayerIndex(m_sprite->getCurrentLayer());
|
|
if (layer >= 0)
|
|
centerCel(layer, m_sprite->getCurrentFrame());
|
|
}
|
|
|
|
void AnimationEditor::showCurrentCel()
|
|
{
|
|
int layer = getLayerIndex(m_sprite->getCurrentLayer());
|
|
if (layer >= 0)
|
|
showCel(layer, m_sprite->getCurrentFrame());
|
|
}
|
|
|
|
void AnimationEditor::cleanClk()
|
|
{
|
|
int clk_part = m_clk_part;
|
|
m_clk_part = A_PART_NOTHING;
|
|
|
|
drawPart(clk_part,
|
|
m_clk_layer,
|
|
m_clk_frame);
|
|
}
|
|
|
|
void AnimationEditor::setScroll(int x, int y, bool use_refresh_region)
|
|
{
|
|
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(this, JI_GDR_CUTTOPWINDOWS);
|
|
old_scroll_x = m_scroll_x;
|
|
old_scroll_y = m_scroll_y;
|
|
}
|
|
|
|
max_scroll_x = m_sprite->getTotalFrames() * FRMSIZE - jrect_w(this->rc)/2;
|
|
max_scroll_y = m_nlayers * LAYSIZE - jrect_h(this->rc)/2;
|
|
max_scroll_x = MAX(0, max_scroll_x);
|
|
max_scroll_y = MAX(0, max_scroll_y);
|
|
|
|
m_scroll_x = MID(0, x, max_scroll_x);
|
|
m_scroll_y = MID(0, y, max_scroll_y);
|
|
|
|
if (use_refresh_region) {
|
|
int new_scroll_x = m_scroll_x;
|
|
int new_scroll_y = m_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,
|
|
this->rc->x1,
|
|
this->rc->y1 + HDRSIZE,
|
|
this->rc->x1 + m_separator_x,
|
|
this->rc->y2);
|
|
jregion_reset(reg2, rect2);
|
|
jregion_copy(reg1, region);
|
|
jregion_intersect(reg1, reg1, reg2);
|
|
this->scrollRegion(reg1, 0, dy);
|
|
|
|
// Scroll header-frame.
|
|
jrect_replace(rect2,
|
|
this->rc->x1 + m_separator_x + m_separator_w,
|
|
this->rc->y1,
|
|
this->rc->x2,
|
|
this->rc->y1 + HDRSIZE);
|
|
jregion_reset(reg2, rect2);
|
|
jregion_copy(reg1, region);
|
|
jregion_intersect(reg1, reg1, reg2);
|
|
this->scrollRegion(reg1, dx, 0);
|
|
|
|
// Scroll cels.
|
|
jrect_replace(rect2,
|
|
this->rc->x1 + m_separator_x + m_separator_w,
|
|
this->rc->y1 + HDRSIZE,
|
|
this->rc->x2,
|
|
this->rc->y2);
|
|
jregion_reset(reg2, rect2);
|
|
jregion_copy(reg1, region);
|
|
jregion_intersect(reg1, reg1, reg2);
|
|
this->scrollRegion(reg1, dx, dy);
|
|
|
|
jmouse_show();
|
|
|
|
jregion_free(region);
|
|
jregion_free(reg1);
|
|
jregion_free(reg2);
|
|
jrect_free(rect2);
|
|
}
|
|
}
|
|
|
|
int AnimationEditor::getLayerIndex(const Layer* layer)
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<m_nlayers; i++)
|
|
if (m_layers[i] == layer)
|
|
return i;
|
|
|
|
return -1;
|
|
}
|
|
|
|
// Auxiliary routine to draw an icon in the layer-part.
|
|
static void icon_rect(BITMAP* icon_normal, BITMAP* icon_selected, 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_normal->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());
|
|
}
|
|
|
|
set_alpha_blender();
|
|
if (is_selected)
|
|
draw_trans_sprite(ji_screen, icon_selected, icon_x, icon_y);
|
|
else
|
|
draw_trans_sprite(ji_screen, icon_normal, icon_x, icon_y);
|
|
}
|