view window/widget.cc @ 52:15a18fbe6f21

* Known bugs added to the README * Code cleaning (0 -> NULL when needed, indentation, spaces, ...)
author thib
date Sat, 18 Apr 2009 18:35:39 +0000
parents 5f548e5957a8
children ddbcbd000206
line wrap: on
line source

/*
 * Copyright (c) 2004-2006  Kazunori "jagarl" Ueno
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "widget.h"
#include <algorithm>
#include <map>
#include <string>

Rect DSurfaceRenderText(TextGlyphStream::iterator start, TextGlyphStream::iterator end, const Rect& srcrect,
	Surface* dst, const Rect& dstrect);
void DSurfaceFill(Surface* src, const Rect& rect, int r, int g, int b, int a = 0xff);
void DSurfaceMove(Surface* src, const Rect& srcrect, Surface* dst_o, const Rect& dstrect);

//static char* wdefault_font_orig = "msgothic.ttc;times.ttf;";
static std::map<int, XKFont::HorizLayout*> size_to_layout;
static const char* wdefault_font_orig = "times.ttf;msgothic.ttc";
static std::string wdefault_font = wdefault_font_orig;

void SetFont(const char* font) {
	if (font == 0) return;
	std::map<int,XKFont::HorizLayout*>::iterator it;
	for (it=size_to_layout.begin(); it != size_to_layout.end(); it++) {
		delete it->second;
	}
	size_to_layout.clear();
	wdefault_font = font;
}

// namespace Widget {
#define TimeCursor WidTimeCursor
#define MouseCursor WidMouseCursor
#define Button WidButton
#define Scale WidScale
#define TextButton WidTextButton
#define Text WidText
#define AnmTime WidAnmTime
#define AnmMove WidAnmMove
#define AnmAlpha WidAnmAlpha
#define AnmPtnSolid WidAnmPtnSolid
#define AnmPtnAlpha WidAnmPtnAlpha

XKFont::HorizLayout* DefaultLayout(int text_size) {
    if(text_size <= 0) abort();//FIXME: Find why the hell text_size can be <= 0
	if (size_to_layout.find(text_size) == size_to_layout.end()) {
		size_to_layout[text_size] = new XKFont::HorizLayout(wdefault_font.c_str(), text_size);
	}
	return size_to_layout[text_size];
}

void PicWidget::activate(void) {
}

void PicWidget::deactivate(void) {
}

void PicWidget::SetRegion(const Rect& apos) {
}

void PicWidget::show(void) {
	Pic()->show();
}

void PicWidget::hide(void) {
	Pic()->hide();
}

void PicWidget::show_all(void) {
	Pic()->show_all();
}

TimeCursor::TimeCursor(Event::Container& container, int _interval, PicContainer* parent, const char* s, int _sx, int _sy, int _sdx, int _sdy, int _nptn, const Rect& r) :
	Time(container) {
	interval = _interval;
	if (interval < 0) interval = 100;
	nptn = _nptn;
	if (nptn < 0) nptn = 1;
	count = 0; old_time = 0;

	x = _sx; y = _sy; dx = _sdx; dy = _sdy;
	SetPic(parent->create_leaf(r, PicBase::CACHE_BACK));
	Pic()->SetSurface(s, _sx, _sy);
};
void TimeCursor::Elapsed(unsigned int current_time) {
	int move = (current_time-old_time)/interval;
	if (move) {
		old_time += move*interval;
		count += move;
		count %= nptn;
		Pic()->SetSurfacePos(x + count*dx, y + count*dy);
	}
	if (current_time > old_time+interval) SetWakeup(current_time);
	else SetWakeup(old_time+interval);
}

MouseCursor::MouseCursor(Event::Container& _container, PicContainer* parent, const char* s, int x, int y, int w, int h) :
	Event::Video(_container), container(_container) {
	int sx, sy;
	_container.MousePos(sx, sy);
	SetPic(parent->create_leaf(Rect(sx, sy, sx+w, sy+h), 0));
	Pic()->SetSurface(s, x, y);
	x = 0; y = 0;
	container.RegisterGlobalMotionFunc(&Motionfunc, (void*)this);
}

MouseCursor::MouseCursor(Event::Container& _container, PicContainer* parent, Surface* s, int x, int y, int w, int h) :
	Event::Video(_container), container(_container) {
	int sx, sy;
	_container.MousePos(sx, sy);
	SetPic(parent->create_leaf(Rect(sx, sy, sx+w, sy+h), 0));
	Pic()->SetSurface(s, x, y);
	x = 0; y = 0;
	container.RegisterGlobalMotionFunc(&Motionfunc, (void*)this);
}

MouseCursor::~MouseCursor() {
	container.DeleteGlobalMotionFunc(&Motionfunc, (void*)this);
}

bool MouseCursor::Motionfunc(int x, int y, void* pointer) {
	MouseCursor* _this = (MouseCursor*)pointer;
	// 左上がカーソルポイントの場合
	// _this->Pic()->Move(x,y);
	// 左下がカーソルポイントの場合
	_this->Pic()->Move(x,y-_this->Pic()->Height());
	return true;
}

Button::Button(Event::Container& container, PicContainer* parent, const char* s, int _sx, int _sy, int _sdx, int _sdy, int _nptn, const Rect& r, int _z) : sx(_sx), sy(_sy), sdx(_sdx), sdy(_sdy), nptn(_nptn) ,Event::Video(container,r, _z) {
	SetPic(parent->create_leaf(r, 0));
	Pic()->SetSurface(s, _sx, _sy);
	show();
	is_in = false;
	is_toggled = false;
	press_func = NULL;
	press_pointer = NULL;
	drag_func = NULL;
	drag_pointer = NULL;
	is_toggle_switch = false;
}

Button::Button(Event::Container& container, PicContainer* parent, Surface* s, int _sx, int _sy, int _sdx, int _sdy, int _nptn, const Rect& r, int _z) : sx(_sx), sy(_sy), sdx(_sdx), sdy(_sdy), nptn(_nptn) ,Event::Video(container,r, _z) {
	SetPic(parent->create_leaf(r, 0));
	Pic()->SetSurface(s, _sx, _sy);
	show();
	is_in = false;
	is_toggled = false;
	press_func = NULL;
	press_pointer = NULL;
	drag_func = NULL;
	drag_pointer = NULL;
	is_toggle_switch = false;
}

Button::~Button() {
}

void Button::In(void) {
	is_in = true;
	if (nptn > 1)
		if (! is_toggled)
			Pic()->SetSurfacePos(sx+sdx, sy+sdy);
}

void Button::Out(void) {
	is_in = false;
	if (!is_toggled)
		Pic()->SetSurfacePos(sx, sy);
}

void Button::Toggle(bool new_toggle) {
	if (is_toggled == new_toggle) {
		return;
	}
	is_toggled = new_toggle;
	// if (is_in) return; // is_in に関わらずウィジットの表示を変更することにする
	if (is_toggled) {
		if (nptn > 2)
			Pic()->SetSurfacePos(sx+sdx*2, sy+sdy*2);
		else if (nptn > 1)
			Pic()->SetSurfacePos(sx+sdx, sy+sdy);
		else
			Pic()->SetSurfacePos(sx, sy);
	} else {
		Pic()->SetSurfacePos(sx, sy);
	}
}

void Button::Press(void) {
	is_in = true;
	if (is_toggled) ;
	else if (nptn > 2)
		Pic()->SetSurfacePos(sx+sdx*2, sy+sdy*2);
	else if (nptn > 1)
		Pic()->SetSurfacePos(sx+sdx, sy+sdy);
	if (press_func) press_func(press_pointer, this);
	if (is_toggle_switch) Toggle(!is_toggled);
}

void Button::Release(void) {
	if (is_toggled) ;
	else if (nptn > 1 && is_in)
		Pic()->SetSurfacePos(sx+sdx, sy+sdy);
	else if (nptn > 1)
		Pic()->SetSurfacePos(sx, sy);
}

void Button::Drag(int x_from, int y_from, int x_to, int y_to) {
	if (drag_func) drag_func(x_from,y_from,x_to, y_to,drag_pointer, this);
}

Scale::Scale(Event::Container& _container, PicContainer* _parent, const Rect& r_orig, const Color& _color, bool _is_vertical) :
	Event::Video(_container,Rect(0,0), 1),
	container(_container), parent(_parent), cursor_color(_color),
	mouse_x(0), mouse_y(0), max(0), min(0),
	value(0), value_add(0), value_dragstart(0), is_vertical(_is_vertical),
	change_func(0), change_pointer(0) {

	arrow_down = NULL;
	arrow_up = NULL;
	cursor = NULL;
	panel = NULL;

	Init(r_orig);
}

extern char* create_button(int number, int& width, int& height, int r, int g, int b);
extern char* create_box(int& width, int& height, int r, int g, int b);

void Scale::Init(Rect r_orig) {
	int r=cursor_color.r, g=cursor_color.g, b=cursor_color.b;
	// 矢印
	int arrow_width = -1;
	cursor_width = -1;
	char* button1;
	char* button2;
	if (is_vertical) {
		// 矢印に必要な領域確保
		int arrow_height = r_orig.width();
		button1 = create_button(2, arrow_height, arrow_width, r, g, b);
		button2 = create_button(3, arrow_height, arrow_width, r, g, b);
		if (r_orig.height() < arrow_width*4) {
			if (r_orig.height() < 8) r_orig.by = r_orig.ty + 8; // 小さすぎる場合は強制変更
			free( (void*)button1);
			free( (void*)button2);
			arrow_width = r_orig.height()/4;
			// 再割り当て
			button1 = create_button(2, arrow_height, arrow_width, r, g, b);
			button2 = create_button(3, arrow_height, arrow_width, r, g, b);
		}
		// 矢印ボタンの作成
		Surface* a1s = parent->Root().NewSurfaceFromRGBAData(arrow_height, arrow_width*3, button1, ALPHA_MASK);
		int x = r_orig.lx; int y = r_orig.ty;
		arrow_up = new Button(container, parent, a1s, 0, 0, 0, arrow_width, 3, Rect(x,y,x+arrow_height,y+arrow_width),1);
		arrow_up->Pic()->SetSurfaceFreeFlag();
		Surface* a2s = parent->Root().NewSurfaceFromRGBAData(arrow_height, arrow_width*3, button2, ALPHA_MASK);
		x = r_orig.rx - arrow_height; y = r_orig.by - arrow_width;
		arrow_down = new Button(container, parent, a2s, 0, 0, 0, arrow_width, 3, Rect(x,y,x+arrow_height,y+arrow_width),1);
		arrow_down->Pic()->SetSurfaceFreeFlag();
		// picture作成(ボタンの動く領域)
		Rect r = r_orig;
		r.ty += arrow_width;
		r.by -= arrow_width;
		panel = parent->create_node(r, 0);
		SetPic(panel);
		// ボタンの中心線を描画、設定
		Surface* s = parent->Root().NewSurface(r.width()/2, r.height(), ALPHA_MASK);
		DSurfaceFill(s, Rect(0,0,r.width()/2,r.height()), 0, 0, 0, 0xff);
		Pic()->SetSurface(s, -r.width()/4, 0, 0);
		Pic()->SetSurfaceFreeFlag();
	} else {
		// 矢印に必要な領域確保
		int arrow_height = r_orig.height();
		button1 = create_button(0, arrow_width, arrow_height, r, g, b);
		button2 = create_button(1, arrow_width, arrow_height, r, g, b);
		if (r_orig.width() < arrow_width*4) {
			if (r_orig.width() < 8) r_orig.rx = r_orig.lx + 8; // 小さすぎる場合は強制変更
			free( (void*)button1);
			free( (void*)button2);
			arrow_width = r_orig.width()/4;
			// 再割り当て
			button1 = create_button(2, arrow_width, arrow_height, r, g, b);
			button2 = create_button(3, arrow_width, arrow_height, r, g, b);
		}
		// 矢印ボタンの作成
		Surface* a1s = parent->Root().NewSurfaceFromRGBAData(arrow_width, arrow_height*3, button1, ALPHA_MASK);
		int x = r_orig.lx; int y = r_orig.ty;
		arrow_up = new Button(container, parent, a1s, 0, 0, 0, arrow_height, 3, Rect(x,y,x+arrow_width,y+arrow_height),1);
		arrow_up->Pic()->SetSurfaceFreeFlag();
		Surface* a2s = parent->Root().NewSurfaceFromRGBAData(arrow_width, arrow_height*3, button2, ALPHA_MASK);
		x = r_orig.rx - arrow_width; y = r_orig.by - arrow_height;
		arrow_down = new Button(container, parent, a2s, 0, 0, 0, arrow_height, 3, Rect(x,y,x+arrow_width,y+arrow_height),1);
		arrow_down->Pic()->SetSurfaceFreeFlag();
		// picture作成(ボタンの動く領域)
		Rect r = r_orig;
		r.lx += arrow_width;
		r.rx -= arrow_width;
		panel = parent->create_node(r, 0);
		SetPic(panel);
		// ボタンの中心線を描画、設定
		Surface* s = parent->Root().NewSurface(r.width(), r.height()/2, ALPHA_MASK);
		DSurfaceFill(s, Rect(0,0,r.width(),r.height()/2), 0, 0, 0, 0xff);
		Pic()->SetSurface(s, 0, -r.height()/4, 0);
		Pic()->SetSurfaceFreeFlag();
	}
	arrow_up->press_func = &Scale::PressArrowUp;
	arrow_up->press_pointer = (void*)this;
	arrow_down->press_func = &Scale::PressArrowDown;
	arrow_down->press_pointer = (void*)this;
	arrow_up->show();
	arrow_down->show();
	panel->show();
	InitCursor(0);
}

void Scale::InitCursor(int width_ratio) {
	int r=cursor_color.r, g=cursor_color.g, b=cursor_color.b;
	if (cursor != NULL) delete cursor;
	cursor = NULL;
	Rect region(0,0);
	if (width_ratio < 0) width_ratio = 0;
	else if (width_ratio > 1024) width_ratio = 1024;
	if (is_vertical) {
		if (width_ratio == 0) cursor_width = Pic()->Width() * 3 / 2; // 幅の1.5倍
		else cursor_width = Pic()->Height()*width_ratio/1024;
		if (cursor_width <= 0) return; // カーソルなし(いいのか?)
		region = Rect(0, 0, Pic()->Width(), cursor_width);
	} else { // horizontal
		if (width_ratio == 0) cursor_width = Pic()->Height() * 3 / 2; // 高さの1.5倍
		else cursor_width = Pic()->Width()*width_ratio/1024;
		if (cursor_width <= 0) return; // カーソルなし(いいのか?)
		region = Rect(0, 0, cursor_width, Pic()->Height());
	}

	int height = region.height();
	int width = region.width();
	char* box = create_box(width, height, r, g, b);
	Surface* boxs = parent->Root().NewSurfaceFromRGBAData(width, height*3, box, ALPHA_MASK);
	cursor = new Button(container, panel, boxs, 0, 0, 0, height, 3, region, 2);
	cursor->Pic()->SetSurfaceFreeFlag();

	cursor->press_func = &Scale::PressCursor;
	cursor->press_pointer = (void*)this;
	cursor->drag_func = &Scale::DragCursor;
	cursor->drag_pointer = (void*)this;
	cursor->show();

	// 矢印等をクリックしたときの移動量計算
	int bar_width;
	if (is_vertical) bar_width = Pic()->Height();
	else bar_width = Pic()->Width();
	if (bar_width <= 0) value_add = max-min;
	else if (cursor_width == 0) value_add = 2;
	else value_add = scale_max*cursor_width/bar_width;
}

void Scale::PressArrowDown(void* pointer, Button* from) {
	Scale* self = (Scale*)pointer;
	self->SetScaleValue(self->value + self->value_add);
}

void Scale::PressArrowUp(void* pointer, Button* from){
	Scale* self = (Scale*)pointer;
	self->SetScaleValue(self->value - self->value_add);
}

void Scale::PressCursor(void* pointer, Button* from){
	Scale* self = (Scale*)pointer;
	self->value_dragstart = self->value;
}

void Scale::DragCursor(int x_from, int y_from,int x, int y, void* pointer, Button* from){
	Scale* self = (Scale*)pointer;
	int dx, w;
	if (self->is_vertical) {
		dx = y-y_from;
		w = self->Event::Video::Region().height();
	} else {
		dx = x-x_from;
		w = self->Event::Video::Region().width();
	}
	if (w == 0) return;
	self->SetScaleValue(self->value_dragstart + dx*scale_max/w);
}

int Scale::CalcValue(void) {
	Rect own_region = Event::Video::Region();
	int x, w;
	if (is_vertical) {
		w = own_region.height();
		x = mouse_y - own_region.ty;
	} else {
		w = own_region.width();
		x = mouse_x - own_region.lx;
	}
	if (w == 0) return 0;
	if (x < 0) x = 0;
	else if (x > w) x = w;
	return x*scale_max/w;
}

void Scale::Press(void){
	int v = CalcValue();
	if (v < value) SetScaleValue(value-value_add);
	else SetScaleValue(value+value_add);
}

void Scale::Motion(int x, int y){
	mouse_x = x;
	mouse_y = y;
}

void Scale::SetRange(int new_min, int new_max) {
	min = new_min;
	max = new_max;
	SetValue(value);
}

void Scale::SetValue(int new_value) {
	if (min == max) {
		value = min;
		SetScaleValue(0);
		return;
	}
	int scale_value = (new_value-min) * scale_max / (max-min);
	SetScaleValue(scale_value);
}

int Scale::GetValue(void) const{
	return min + value * (max-min) / scale_max;
}

void Scale::SetScaleValue(int new_value) {
	if (new_value < 0) value = 0;
	else if (new_value > scale_max) value = scale_max;
	else value = new_value;
	if (is_vertical) {
		int h = Pic()->Height();
		int y = (h-cursor_width) * value / scale_max;
		if (y < 0) y = 0;
		cursor->Pic()->Move(0, y);
	} else { // horizontal
		int w = Pic()->Width();
		int x = (w-cursor_width) * value / scale_max;
		if (x < 0) x = 0;
		cursor->Pic()->Move(x, 0);
	}
	if (change_func) {
		(*change_func)(change_pointer, this);
	}
}

TextButton::TextButton(Event::Container& container, PicContainer* parent, const char* text, int _text_size, Attribute attr, const Rect& r_orig, int _z, const Color& _fore, const Color& _pressed, const Color& _back) :
	Button(container, parent, (Surface*)0, 0, 0, 0, 0, 0, r_orig, _z),
	root(parent->Root()), surface(0), attribute(attr), text_size(_text_size),
	fore(_fore), pressed(_pressed), back(_back)
{
	bool rect_changed = false;
	// まず、テキスト領域の広さを得る
	Rect r(r_orig);

	if (text == NULL) text = "";
	int width = r.width(); int height = r.height();
	if (width == 0) width = parent->Width() - r.lx;

	TextGlyphStream gs = DefaultLayout(text_size)->Layout(text, width);

	if (r.width() == 0) { // 文字に合わせてウィジット作成
		rect_changed = true;
		width = gs.width() + text_size;
		r.rx = r.lx + gs.width();
		attribute = Attribute(attribute | CENTER);
	}
	if (r.height() == 0) {
		rect_changed = true;
		if (attribute & NOPADDING) r.by = r.ty + gs.height();
		else r.by = r.ty + gs.height() + text_size/2;
	}

	if (rect_changed) {
		// 大きさ変更
		Pic()->SetSurfaceRect(r);
	}

	sx = 0; sy = 0; sdx = 0;
	sdy = r.height();
	nptn = 3;
	int x = 0, y = 0;
	if (attribute & CENTER)
		x = (Pic()->Width() - gs.width()) / 2;
	y = (Pic()->Height() - gs.height()) / 2;

	//FIXME: (back.a == NULL ?)
	if (back.a == 0) { // 背景なし、もしくはボタン押の状態のみ背景あり
		surface = root.NewSurface(r.width(), r.height()*2, ALPHA_MASK);
		DSurfaceFill(surface, Rect(*surface), 0, 0, 0, 0);
		if (attribute & REVERSE) {
			DSurfaceFill(surface, Rect(0,r.height(),r.width(),r.height()*2), pressed.r, pressed.g, pressed.b, 0xff);
		}
		gs.SetColor(fore.r, fore.g, fore.b);
		DSurfaceRenderText(gs.begin(), gs.end(), Rect(0,0,gs.width(),gs.height()), surface, Rect(x,y));
		gs.SetColor(pressed.r, pressed.g, pressed.b);
		if (attribute & REVERSE) {
			gs.SetReverse(true);
			DSurfaceRenderText(gs.begin(), gs.end(), Rect(0,0,gs.width(),gs.height()), surface, Rect(x,y+r.height()));
			gs.SetReverse(false);
		} else {
			DSurfaceRenderText(gs.begin(), gs.end(), Rect(0,0,gs.width(),gs.height()), surface, Rect(x,y+r.height()));
		}
		nptn = 2;
	} else { // ボタン型の背景あり
		/* ラベル用の Surface を作る */
		width = r.width(); height = r.height();
		char* box = create_box(width, height, back.r, back.g, back.b);
		surface = root.NewSurfaceFromRGBAData(r.width(), r.height()*3, box, ALPHA_MASK);
	
		Surface* text_surface = root.NewSurface(r.width(), r.height(), ALPHA_MASK);
		DSurfaceFill(text_surface, Rect(*text_surface), 0, 0, 0, 0);
	
		gs.SetColor(fore.r, fore.g, fore.b);
		DSurfaceRenderText(gs.begin(), gs.end(), Rect(0,0,gs.width(),gs.height()), text_surface, Rect(x,y));
		root.BlitSurface(text_surface, Rect(0,0,width,height), surface, Rect(0,0));
		gs.SetColor(pressed.r, pressed.g, pressed.b);
		DSurfaceRenderText(gs.begin(), gs.end(), Rect(0,0,gs.width(),gs.height()), text_surface, Rect(x,y));
		root.BlitSurface(text_surface, Rect(0,0,width,height), surface, Rect(0,height));
		root.BlitSurface(text_surface, Rect(0,0,width,height), surface, Rect(0,height*2));
		root.DeleteSurface(text_surface);
	}

	Pic()->SetSurface(surface, 0, 0);
	show();
}

void TextButton::SetText(const char* text, const Color& _fore, const Color& _pressed, const Color& _back)
{
	int width = Pic()->Width(); int height = Pic()->Height();
	// まず、テキスト領域の広さを得る
	if (text == NULL) text = "";

	TextGlyphStream gs = DefaultLayout(text_size)->Layout(text, width);

	int x = 0, y = 0;
	if (attribute & CENTER) {
		x = (width - gs.width()) / 2;
		y = (height - gs.height()) / 2;
	}
	int surf_x = Pic()->SurfacePosX();
	int surf_y = Pic()->SurfacePosY();
	Pic()->SetSurface( (Surface*)0,0,0);
	root.DeleteSurface(surface);
	surface = NULL;

	//FIXME: (back.a == NULL) ?
	if (back.a == 0) { // 背景なし
		surface = root.NewSurface(width, height*2, ALPHA_MASK);
		DSurfaceFill(surface, Rect(*surface), 0, 0, 0, 0);
		if (attribute & REVERSE) {
			DSurfaceFill(surface, Rect(0,height,width,height*2), pressed.r, pressed.g, pressed.b, 0xff);
		}
		gs.SetColor(_fore.r, _fore.g, _fore.b);
		DSurfaceRenderText(gs.begin(), gs.end(), Rect(0,0,gs.width(),gs.height()), surface, Rect(x,y));
		gs.SetColor(_pressed.r, _pressed.g, _pressed.b);
		gs.SetReverse(true);
		DSurfaceRenderText(gs.begin(), gs.end(), Rect(0,0,gs.width(),gs.height()), surface, Rect(x,y+height));
		gs.SetReverse(false);
		nptn = 2;
	} else {
		/* ラベル用の Surface を作る */
		char* box = create_box(width, height, _back.r, _back.g, _back.b);
		surface = root.NewSurfaceFromRGBAData(width, height*3, box, ALPHA_MASK);
	
		Surface* text_surface = root.NewSurface(width, height, ALPHA_MASK);
		DSurfaceFill(text_surface, Rect(*text_surface), 0, 0, 0, 0);
	
		gs.SetColor(_fore.r, _fore.g, _fore.b);
		DSurfaceRenderText(gs.begin(), gs.end(), Rect(0,0,gs.width(),gs.height()), text_surface, Rect(x,y));
		root.BlitSurface(text_surface, Rect(0,0,width,height), surface, Rect(0,0));
		gs.SetColor(_pressed.r, _pressed.g, _pressed.b);
		DSurfaceRenderText(gs.begin(), gs.end(), Rect(0,0,gs.width(),gs.height()), text_surface, Rect(x,y));
		root.BlitSurface(text_surface, Rect(0,0,width,height), surface, Rect(0,height));
		root.BlitSurface(text_surface, Rect(0,0,width,height), surface, Rect(0,height*2));
		root.DeleteSurface(text_surface);
	}

	Pic()->SetSurface(surface, surf_x, surf_y);
	show();
}

TextButton::~TextButton() {
	if (surface) root.DeleteSurface(surface);
	surface = NULL;
}

Text::Text(Event::Container& container, PicContainer* parent, const Rect& r, const Rect& text_r, int _fontsize) :
	Event::Video(container),
	Event::Time(container),
	event(container),
	srcrect(0,0,0,0),
	layout(wdefault_font.c_str(), _fontsize), fontsize(_fontsize) {

	SetPic(parent->create_node(r, 0));
	surface = parent->Root().NewSurface(text_r.width(), text_r.height(), ALPHA_MASK);
	pictext = PicNode()->create_leaf(text_r, PicBase::CACHE_BACK);
	pictext->SetSurface(surface, 0, 0);
	pictext->show();
	cursor = 0;
	cursor_activated = false;
	window_activated = false;

	event.RegisterGlobalPressFunc(&Pressed, (void*)this);

	speed = 10;
	wait_delay = -1;
	status = PREPARE;
	old_time = wait_starttime = 0;
}

Text::~Text() {
	event.DeleteGlobalPressFunc(&Pressed, (void*)this);
	PicNode()->Root().DeleteSurface(surface);
}

void Text::SetSpeed(int new_speed) {
	speed = new_speed;
}

void Text::SetWait(int new_wait) {
	if (new_wait < 0) new_wait = 100000000;
	wait_delay = new_wait;
}

int Text::CalcScrollHeight(void) {
	int i;
	int len = bottom_pos.size();
	int y0 = bottom_pos[line_number];
	int height = Rect(*surface).height();
	for (i=line_number; i<len; i++)
		if (bottom_pos[i] - y0 > height) break;
	if (i == line_number) i = line_number + 1;
	return i - line_number - 1;
}

void Text::Elapsed(unsigned int current_time) {
	SetWakeup(current_time + 50);
	if (status == PREPARE) {
		old_time = current_time;
		return;
	}
	int nChar = speed * (current_time - old_time) / 1000;
	if (speed == -1 || press_count) nChar = -1;
	if (nChar == 0) return;
	if (speed == -1) old_time = current_time;
	else old_time += nChar * 1000 / speed;

	switch(status) {
	case WAIT: goto label_wait;
	case SCROLL: goto label_scroll;
	case DRAW2: goto label_draw2;
	case WAIT2: goto label_wait2;
	}

	status = DRAW;
	if (press_count) {
		nChar = -1;
		press_count = 0;
	}
	DrawText(nChar);
	if (nChar == 0) return;
	status = WAIT;
	cursor_activated = true;
	if (cursor_activated && window_activated && cursor) cursor->show();
	wait_starttime = current_time;
label_wait:
	if (current_time < wait_starttime + wait_delay && press_count == 0) return;
	press_count = 0;
	nChar = 0;
	cursor_activated = false;
	if (cursor) cursor->hide();
	while(cur_pos != gstream.end()) {
		// スクロールしては次行描画、を繰り返す
		for (scroll_height = CalcScrollHeight(); scroll_height > 0; scroll_height--) {
			status = SCROLL;
label_scroll:
			if (press_count) break;
			Scrollup(nChar);
			if (nChar == 0) return;
			status = DRAW2;
label_draw2:
			if (press_count) break;
			DrawText(nChar);
			if (nChar == 0) return;
		}
		if (nChar != 0 && scroll_height) {
			nChar = 100000;
			if (status == SCROLL) Scrollup(nChar);
			DrawText(nChar);
			scroll_height--;
			for (; scroll_height > 0; scroll_height--) {
				Scrollup(nChar);
				DrawText(nChar);
			}
		}
		press_count = 0;
		status = WAIT2;
		cursor_activated = true;
		if (cursor_activated && window_activated && cursor) cursor->show();
		wait_starttime = current_time;
label_wait2:
		if (current_time < wait_starttime + wait_delay && press_count == 0) return;
		press_count = 0;
		nChar = 0;
		cursor_activated = false;
		if (cursor) cursor->hide();
	}
	status = PREPARE;
}

bool Text::Pressed(int x, int y, void* pointer) {
	Text* wid = (Text*)pointer;
	if (wid->Pic()->IsHidden()) return true;
	wid->press_count++;
	return true;
}

void Text::Clear(void) {
	stream.container.clear();
	DSurfaceFill(surface, Rect(*surface), 0, 0, 0, 0);
	pictext->ReBlit();
	status = PREPARE;
}

void Text::Flush(void) {
	int nChar = -1;
	DrawText(nChar);
}

void Text::Start(void) {
	gstream.clear();
	bottom_pos.clear();
	layout.Layout(stream, gstream, bottom_pos, pictext->Width()-fontsize/2);

	// height の積算値として bottom_pos を計算
	std::vector<int>::iterator it;
	int pos = 0;
	for (it = bottom_pos.begin(); it != bottom_pos.end(); it++) {
		pos += *it;
		*it = pos;
	}

	cur_pos = gstream.begin();
	line_number = 0;
	scrolled_count = 0;
	srcrect = Rect(0, 0, pictext->Width(), pictext->Height());
	press_count = 0;

	status = DRAW;
	cursor_activated = false;
	if (cursor) cursor->hide();
}

void Text::DrawText(int& nChar) {
	// 描画範囲を得る
	iterator end = gstream.end();
	iterator it = cur_pos;
	while(nChar && it != end) { // nChar < 0 なら出来るだけの文字を描画
		if (! (it->flag & TextGlyph::Group)) nChar--;
		if (it->flag & TextGlyph::LineEnd) {
			if (bottom_pos[line_number+1] > srcrect.by) { //改行すると画面から出てしまう
				it++;
				if (nChar == 0) nChar = 1;
				break;
			}
			line_number++;
		}
		it++;
	}
	// 描画する
	Rect r = DSurfaceRenderText(cur_pos, it, srcrect, surface, Rect(0,0,0,0));
	pictext->ReBlit(r);
	cur_pos = it;
}

void Text::Scrollup(int& nChar) {
	if (nChar < 0) { // 一画面分スクロールする
		DSurfaceFill(surface, Rect(*surface), 0, 0, 0, 0);
		pictext->ReBlit();
		srcrect = Rect(*surface);
		srcrect.rmove(0, bottom_pos[line_number]);
		line_number++;
		scrolled_count = 0;
		return;
	}
	// スクロール幅を求める
	const int max_scroll_count = 8;
	int dy = bottom_pos[line_number+1] - bottom_pos[line_number];
	int cur_dy;
	if (scrolled_count+nChar >= max_scroll_count) {
		cur_dy = dy - (scrolled_count*dy/max_scroll_count);
		nChar -= max_scroll_count-scrolled_count;
		nChar++;
		scrolled_count = 0;
		line_number++;
		srcrect.rmove(0, dy);
	} else {
		cur_dy = (scrolled_count+nChar)*dy/max_scroll_count;
		cur_dy -=(scrolled_count*dy/max_scroll_count);
		scrolled_count += nChar;
		nChar = 0;
	}
	Rect r(*surface);
	DSurfaceMove(surface, r, surface, Rect(0, -cur_dy, 0, 0));
	r.ty = r.by-cur_dy;
	DSurfaceFill(surface, r, 0, 0, 0, 0);
	pictext->ReBlit();
}

void Text::activate(void) {
	event.RegisterGlobalPressFunc(&Pressed, (void*)this);
	Event::Video::activate();
	window_activated = true;
	if (cursor_activated && window_activated && cursor) cursor->show();
}

void Text::deactivate(void) {
	event.DeleteGlobalPressFunc(&Pressed, (void*)this);
	Event::Video::deactivate();
	window_activated = false;
	if (cursor) cursor->hide();
}

void Text::SetCursor(TimeCursor* c) {
	cursor = c;
	if (c) {
		if (cursor_activated && window_activated) c->show();
		else c->hide();
	}
}

Label::Label(PicContainer* parent, const Rect& r_orig, bool _is_center, const char* text, int _text_size) :
	is_center(_is_center),
	root(parent->Root()),
	text_size(_text_size) {
	Rect r(r_orig);

	if (text == NULL) text = "";
	int width = r.width();
	int height = r.height();
	if (width == 0) width = parent->Width() - r.lx;

	TextGlyphStream gs = DefaultLayout(text_size)->Layout(text, width);

	if (r.width() == 0) { // 文字に合わせてウィジット作成
		width = gs.width();
		r.rx = r.lx + gs.width();
	}
	if (r.height() == 0) {
		r.by = r.ty + gs.height();
	}

	SetPic(parent->create_leaf(r, 0));

	/* ラベル用の Surface を作る */
	surface = parent->Root().NewSurface(r.width(), r.height(), ALPHA_MASK);
	
	DSurfaceFill(surface, Rect(*surface), 0, 0, 0, 0);
	int x = 0, y = 0;
	if (is_center) {
		x = (Pic()->Width() - gs.width()) / 2;
		y = (Pic()->Height() - gs.height()) / 2;
	}

	DSurfaceRenderText(gs.begin(), gs.end(), Rect(*surface), surface, Rect(x,y));

	Pic()->SetSurface(surface, 0, 0);
	show();
}

Label::~Label() {
	root.DeleteSurface(surface);
}

void Label::SetText(const char* text) {
	if (text == NULL) text = "";
	TextGlyphStream gs = DefaultLayout(text_size)->Layout(text, Pic()->Width());
	DSurfaceFill(surface, Rect(*surface), 0, 0, 0, 0);
	int x = 0, y = 0;
	if (is_center) {
		x = (Pic()->Width() - gs.width()) / 2;
		y = (Pic()->Height() - gs.height()) / 2;
	}

	DSurfaceRenderText(gs.begin(), gs.end(), Rect(*surface), surface, Rect(x,y));
	Pic()->ReBlit();
}

Dialog::Dialog(Event::Container& container, PicContainer* parent, const char* text, bool with_cancel) :
	Event::Video(container) {

	int x,y;
	status = WAIT;
	set_func = NULL;
	set_pointer = NULL;

	XKFont::HorizLayout& layout = *DefaultLayout(26);
	int dialog_width = parent->Width() / 2;
	TextGlyphStream s_ok = layout.Layout("OK", dialog_width);
	TextGlyphStream s_cancel = layout.Layout("取消", dialog_width);
	TextGlyphStream s_text = layout.Layout(text, dialog_width);

	Rect r_text(0, 0, s_text.width(), s_text.height());
	Rect r_ok(0, 0, s_ok.width(), s_ok.height());
	Rect r_cancel(0, 0, s_cancel.width(), s_cancel.height());

	/* ダイアログボックスの Surface を作る */
	int dwidth = r_text.width() + (r_text.width()/10)*2 + 6;
	int dheight = r_text.height() + r_ok.height() + r_cancel.height()*3 + 4;
	surface_diag = parent->Root().NewSurface(dwidth, dheight, NO_MASK); // alpha なし
	DSurfaceFill(surface_diag, Rect(*surface_diag), 0xf0, 0xd0, 0xa0);
	DrawBox(surface_diag, Rect(0,0,dwidth,dheight));

	Surface* surface_text = parent->Root().NewSurface(r_text.width(), r_text.height(), ALPHA_MASK);
	s_text.SetColor(0x38, 0x20, 0x18);
	DSurfaceRenderText(s_text.begin(), s_text.end(), r_text, surface_text, Rect(0, 0, 0, 0));
	x = r_text.width()/10 + 3;
	y = r_cancel.height()+2;
	parent->Root().BlitSurface(surface_text, r_text, surface_diag, Rect(x, y, x+r_text.width(), y+r_text.height()));
	parent->Root().DeleteSurface(surface_text);

	/* panel をつくる */
	x = (parent->Width()-dwidth)/2;
	y = (parent->Height()-dheight)/2;
	SetPic(parent->create_node(Rect(x, y, x+dwidth, y+dheight), 0));

	/* ボタンを作成する */
	/* f8d8c8 背景(明)*/
	/* f0d0a0 背景*/
	/* b08040 枠(明)*/
	/* 805010 枠*/
	/* 382018 黒*/
	/* 9890f8 青*/ 
	/* dc6448 赤*/
	/* 各ボタンは左右にボタン幅の 1/4, 上下にボタン幅の 1/4 のマージンを持つ */
	Rect r_btn(r_ok); r_btn.join(r_cancel);
	int btn_width = r_btn.width() * 3 / 2;
	int btn_height = r_btn.height() * 3 / 2;
	surface_btn = parent->Root().NewSurface(btn_width, btn_height*4, ALPHA_MASK);
	DSurfaceFill(surface_btn, Rect(*surface_btn), 0, 0, 0, 0);
	s_ok.SetColor(0x38, 0x20, 0x18);
	DSurfaceRenderText(s_ok.begin(), s_ok.end(), r_ok, surface_btn, Rect( (btn_width-r_ok.width())/2,(btn_height-r_ok.height())/2));
	s_ok.SetColor(0x98, 0x90, 0xf8);
	DSurfaceRenderText(s_ok.begin(), s_ok.end(), r_ok, surface_btn, Rect( (btn_width-r_ok.width())/2,(btn_height-r_ok.height())/2 + btn_height));
	s_cancel.SetColor(0x38, 0x20, 0x18);
	DSurfaceRenderText(s_cancel.begin(), s_cancel.end(), r_cancel, surface_btn, Rect( (btn_width-r_cancel.width())/2,(btn_height-r_cancel.height())/2 + btn_height*2));
	s_cancel.SetColor(0xdc, 0x64, 0x48);
	DSurfaceRenderText(s_cancel.begin(), s_cancel.end(), r_cancel, surface_btn, Rect( (btn_width-r_cancel.width())/2,(btn_height-r_cancel.height())/2 + btn_height*3));

	x = (dwidth - btn_width*2) / 3;;
	y = r_cancel.height()*3/2 + r_text.height() + 2;
	if (!with_cancel) x = (dwidth - btn_width) / 2;
	Button* b_ok = new Button(container, PicNode(), surface_btn, 0, 0, 0, btn_height, 2, Rect(x, y, x+btn_width, y+btn_height), 1);
	DrawBox(surface_diag, Rect(x-3,y-2,x+btn_width+3,y+btn_height+2));
	b_ok->press_pointer = (void*)this;
	b_ok->press_func = &press_ok;
	if (with_cancel) {
		x += x + btn_width;
		Button* b_cancel = new Button(container, PicNode(), surface_btn, 0, btn_height*2, 0, btn_height, 2, Rect(x, y, x+btn_width, y+btn_height), 1);
		DrawBox(surface_diag, Rect(x-3,y-2,x+btn_width+3,y+btn_height+2));
		b_cancel->press_pointer = (void*)this;
		b_cancel->press_func = &press_cancel;
	}

	Pic()->SetSurface(surface_diag, 0, 0);
	Pic()->ZMove(ZMOVE_TOP);

	show_all();
}

Dialog::~Dialog() {
	PicRoot& root = PicNode()->Root();
	SetPic(NULL);
	root.DeleteSurface(surface_btn);
	root.DeleteSurface(surface_diag);
}

void Dialog::press_ok(void* pointer, Button* btn) {
	if (pointer) {
		Dialog* wid = (Dialog*)pointer;
		wid->status = OK;
		if (wid->set_func) {
			(*wid->set_func)(wid->set_pointer, wid);
		}
	}
}

void Dialog::press_cancel(void* pointer, Button* btn) {
	if (pointer) {
		Dialog* wid = (Dialog*)pointer;
		wid->status = CANCEL;
		if (wid->set_func) {
			(*wid->set_func)(wid->set_pointer, wid);
		}
	}
}

void Dialog::DrawBox(Surface* s, const Rect& r) {
	DSurfaceFill(s, Rect(r.lx, r.ty, r.rx, r.ty+1), 0x80, 0x50, 0x10);
	DSurfaceFill(s, Rect(r.lx, r.ty+1, r.rx, r.ty+2), 0x38, 0x20, 0x18);
	DSurfaceFill(s, Rect(r.lx, r.by-2, r.rx, r.by-1), 0x80, 0x50, 0x10);
	DSurfaceFill(s, Rect(r.lx, r.by-1, r.rx, r.by), 0x38, 0x20, 0x18);

	DSurfaceFill(s, Rect(r.lx, r.ty, r.lx+1, r.by), 0xb0, 0x80, 0x40);
	DSurfaceFill(s, Rect(r.lx+1, r.ty+1, r.lx+2, r.by-1), 0x80, 0x50, 0x10);
	DSurfaceFill(s, Rect(r.lx+1, r.ty+2, r.lx+2, r.by-2), 0x38, 0x20, 0x18);
	DSurfaceFill(s, Rect(r.rx-3, r.ty+2, r.rx-2, r.by-2), 0xb0, 0x80, 0x40);
	DSurfaceFill(s, Rect(r.rx-2, r.ty+1, r.rx-1, r.by-1), 0x80, 0x50, 0x10);
	DSurfaceFill(s, Rect(r.rx-1, r.ty, r.rx, r.by), 0x38, 0x20, 0x18);
}

AnmTime::AnmTime(Event::Container& container, PicBase* _pic, int _total_time, int _all_count) :
	Event::Time(container),
	PicAnm(_pic), start_time(0), total_time(_total_time), all_count(_all_count) {
	status = FINISHED;
	if (total_time == 0) total_time = 1;
}

AnmTime::AnmTime(Event::Container& container, std::vector<PicBase*> _pic, int _total_time, int _all_count) :
	Event::Time(container),
	PicAnm(_pic), start_time(0), total_time(_total_time), all_count(_all_count) {
	status = FINISHED;
	if (total_time == 0) total_time = 1;
}

void AnmTime::Elapsed(unsigned int current_time) {
	if (total_time == 0) return;
	if (status == FINISHED || current_time == 0) {SetWakeup(current_time+1); return;}
	if (start_time == 0) {
		start_time = current_time;
		Start();
	}
	unsigned int time_elapsed = current_time - start_time;
	if (time_elapsed < total_time) {
		int count = time_elapsed * all_count / total_time;
		Exec(count);
		int next_time = start_time + (count+1) * total_time / all_count;
		SetWakeup(next_time);
	} else {
		Exec(all_count);
		Finish();
		status = FINISHED;
	}
}

void AnmTime::Abort(void) { 
	if (status == FINISHED) return;
	if (start_time == 0) {
		Start();
		Exec(all_count);
	}
	if (total_time) {
		Finish();
	}
	status = FINISHED;
}

bool AnmTime::IsEnd(void) {
	return status == FINISHED;
}

AnmMove::AnmMove(Event::Container& container, PicBase* _pic, const Rect& _to, int total_time) :
	AnmTime(container, _pic, total_time),
	from(0,0), to(_to) {
	from.lx = _pic->PosX();
	from.ty = _pic->PosY();
	from.rx = from.lx + _pic->Width();
	from.by = from.ty + _pic->Height();

	int dx = to.lx - from.lx;
	int dy = to.ty - from.ty;
	if (dx < 0) dx = -dx;
	if (dy < 0) dy = -dy;
	if (dx < dy) dx = dy;
	if (dx == 0) dx = 1;
	SetAllCount(dx);
}

void AnmMove::Exec(int count) {
	Rect r(from);
	int dx = to.lx - from.lx;
	int dy = to.ty - from.ty;
	r.rmove(dx*count/all_count, dy*count/all_count);
	iterator it;
	for (it=pic.begin(); it!=pic.end(); it++)
		(*it)->Move(r.lx, r.ty);
}

AnmAlpha::AnmAlpha(Event::Container& container, PicBase* _pic, int alpha_from, int alpha_to, int total_time) :
	AnmTime(container, _pic, total_time),
	from(alpha_from), to(alpha_to), alpha_r(0,0,1,1) {
	if (from < 0) from = 0;
	if (from >= ALPHA_MAX) from = ALPHA_MAX;
	if (to < 0) to = 0;
	if (to >= ALPHA_MAX) to = ALPHA_MAX;
	int c = from - to;	
	if (c < 0) c = -c;
	if (c == 0) c = 1;
	SetAllCount(c);
}

AnmAlpha::AnmAlpha(Event::Container& container, std::vector<PicBase*> _pic, int alpha_from, int alpha_to, int total_time) :
	AnmTime(container, _pic, total_time),
	from(alpha_from), to(alpha_to), alpha_r(0,0,1,1) {
	if (from < 0) from = 0;
	if (from >= ALPHA_MAX) from = ALPHA_MAX;
	if (to < 0) to = 0;
	if (to >= ALPHA_MAX) to = ALPHA_MAX;
	int c = from - to;	
	if (c < 0) c = -c;
	if (c == 0) c = 1;
	SetAllCount(c);
}

void AnmAlpha::Start(void) {
	iterator it;
	for (it=pic.begin(); it!=pic.end(); it++)
		(*it)->show();
}

void AnmAlpha::Exec(int count) {
	alpha = (from * (all_count-count) + (to-from) * count) / all_count;
	iterator it;
	for (it=pic.begin(); it!=pic.end(); it++)
		(*it)->SetSurfaceAlpha(&alpha, alpha_r);
}

void AnmAlpha::Finish(void) {
	iterator it;
	for (it=pic.begin(); it!=pic.end(); it++) {
		if (to == 0) (*it)->hide();
		else if (to != ALPHA_MAX) fprintf(stderr,"Warning in AnmAlpha::Finish: alpha value suddenly changed.\n");
		(*it)->SetSurfaceAlpha(0,Rect(0,0));
	}
}

AnmAlphaMove::AnmAlphaMove(Event::Container& container, PicBase* _pic) :
	AnmTime(container, _pic, 0) {
}

void AnmAlphaMove::SetPtn(void) {
	int total = 0;
	std::vector<Ptn>::iterator it;
	for (it=ptns.begin(); it!=ptns.end(); it++) {
		if (total < it->next_tick) total = it->next_tick;
	}
	SetAllCount(total);
	SetTotalTime(total);
	cur_count = 0;
}

void AnmAlphaMove::Exec(int count) {
	if (ptns.empty()) return;
	if (cur_count != 0 && ptns[cur_count].next_tick > count) return;
	if (cur_count >= ptns.size()) return;
	// 次のパターンを探す
	// count <= it->next_tick なる条件を満たす最後の it を探す
	std::vector<Ptn>::iterator it;
	for (it=ptns.begin()+cur_count; it != ptns.end(); it++) {
		if (count <= it->next_tick) break;
	}
	if (it == ptns.end()) {
		fprintf(stderr,"end\n");
		it = ptns.end() - 1;
	}
	cur_count = it - ptns.begin();

	iterator p;
	for (p=pic.begin(); p!=pic.end(); p++) {
		// move
		(*p)->Move(it->pos.lx, it->pos.ty);
		(*p)->SetSurfacePos(it->surface_pos.lx, it->surface_pos.ty);
		// alpha set
		if (it->alpha == 0) (*p)->hide();
		else if (it->alpha == ALPHA_MAX) { (*p)->show(); (*p)->SetSurfaceAlpha(0, Rect(0,0)); }
		else { (*p)->show(); (*p)->SetSurfaceAlpha( &(it->alpha), Rect(0,0,1,1)); }
	}
}

void AnmAlphaMove::Finish(void) {
	if (ptns.empty()) return;
	if (cur_count >= ptns.size() - 1) return;
	cur_count = ptns.size() - 1;
	Exec(ptns[cur_count].next_tick); // 最後の pattern の状態にする
}

AnmPtnSolid::AnmPtnSolid(Event::Container& container, PicBase* _pic, const unsigned char* _ptn, const Rect& _alpha_r, int total_time) :
	AnmTime(container, _pic, total_time),
	ptn(_ptn), alpha_r(_alpha_r)
{
	ptn_len = alpha_r.width() * alpha_r.height();
	alpha = new unsigned char[ptn_len];
	int max_a = 0;
	for (int i=0; i<ptn_len; i++) {
		if (ptn[i] > max_a) max_a = ptn[i];
	}
	if (max_a == 0) max_a = 1;
	SetAllCount(max_a);
}

AnmPtnAlpha::AnmPtnAlpha(Event::Container& container, PicBase* _pic, const unsigned char* _ptn, const Rect& _alpha_r, int _band_width, int total_time) :
	AnmTime(container, _pic, total_time),
	ptn(_ptn), band(_band_width), alpha_r(_alpha_r)
{
	ptn_len = alpha_r.width() * alpha_r.height();
	alpha = new unsigned char[ptn_len];
	if (band <= 0) band = 1;
	SetAllCount(ALPHA_MAX+band);
}

void AnmPtnSolid::Exec(int count) {
	int i;
	for (i=0; i<ptn_len; i++) {
		if (ptn[i] <= count) alpha[i] = ALPHA_MAX;
		else alpha[i] = 0;
	}
	iterator it;
	for (it=pic.begin(); it!=pic.end(); it++)
		(*it)->SetSurfaceAlpha(alpha, alpha_r);
}

void AnmPtnAlpha::Exec(int count) {
	int i;
	int ptn_zero = count;
	int ptn_max = count - band;
	for (i=0; i<ptn_len; i++) {
		if (ptn[i] >= ptn_zero) alpha[i] = 0;
		else if (ptn[i] < ptn_max) alpha[i] = ALPHA_MAX;
		else alpha[i] = (ptn_zero-ptn[i])*ALPHA_MAX/band;
	}
	iterator it;
	for (it=pic.begin(); it!=pic.end(); it++) (*it)->SetSurfaceAlpha(alpha, alpha_r);
}

void AnmPtnSolid::Start(void) {
	iterator it;
	for (it=pic.begin(); it!=pic.end(); it++)
		(*it)->show();
}

void AnmPtnSolid::Finish(void) {
	iterator it;
	for (it=pic.begin(); it!=pic.end(); it++)
		(*it)->SetSurfaceAlpha(0, Rect(0,0));
}

void AnmPtnAlpha::Start(void) {
	iterator it;
	for (it=pic.begin(); it!=pic.end(); it++)
		(*it)->show();
}

void AnmPtnAlpha::Finish(void) {
	iterator it;
	for (it=pic.begin(); it!=pic.end(); it++) (*it)->SetSurfaceAlpha(0, Rect(0,0));
}

/*

Widget の種類
	Mouse: マウス位置に連動する。Surface と origin が必須
	Panel : なにも存在しないところに張りつけていく
		背景張りつけも可能
		ButtonPanel : 無効化可能。Group の Button がカーソルに入ったら、全Button / Label が「カーソル下」状態になる
			同一変数を扱うボタンは原則、同一ButtonPanel の下に入ること(同期する。高速化可能)
			そうでない場合、ボタンの GlobalVariable フラグを立てる必要がある
		背景種類:Window
			内部の透明度と枠形を別々に指定可能。枠形は全枠、部分枠どちらの形でも可能
			(部分枠なら、内部的には上枠、下枠、左右枠と別 Surface で管理する)
		DragButton
			Panel 全体をドラッグし、場所変更できるボタン。
	Button: 無効化>通常>カーソル下>ボタン押下 のpicture / animation
		Toggle Button にできる(Group化すればRadioButtonにもできる)
	Label : 無効化>通常>カーソル下 のanimation
		animation は
		・上への変化
		・下への変化
		・常時変形
		の3つの形式をもつ。
		形式は
		・x / y increment による(全領域と x,y の大きさを指定すると左上から右上、左下、という方へ勝手に領域を変更していく)
		・色変化(明度変化)。色テーブルを指定する。Surface は alpha のみとする
			どちらも、一つのラベルに使う時間の長さを指定する
		・callback による。指定した一定時間以上が立つとCallBack が呼び出され、新たなSurface , origin を指定する。

		・Surface は
			普通の画像
			文字列(適当に仮想化)
			画像数値列
		のいずれか
	Cursor
		リターンカーソル。Label の一種。
	Number
		数字を表示する。フォントの大きさ、もしくは画像数値列
	Text
		テキストを表示する
		パネルの大きさだけ指定すると適当にやってくれる
		カーソルの位置(文字の次/最終)を指定すること
		機能:文字送り速度設定、読み飛ばし設定(常に最高速で押しっぱなし)
	ProgressBar など
		バーの長さ、あるいは位置で変数の大きさを示す。
		Tick, Max を指定、変数の変化には適当に対応できるようにする
		バーの方向として縦/横。Surface は繰り返しで使う(速度上、32pixel くらいあったほうがいいかも?)
		バーの代わりにボタンも使える。Surface 指定のメソッドが違うだけ。
		オプション:バーのどこかをクリックされたとき、そこに移動するかそこに向かって移動するか
		オプション?:矢印ボタン(いらないか)
	ScrollBar
		横/縦。Panel と連動する(専用, ProgressBar の一種として実装)
		(Panel 側で「見えない部分はdelete, 見える部分は自動で作成」機能をつける?:バックログ)

	
	
メニューの出し方
	右クリック
	ボタンを押す
	上の方、右の方など領域に行くとヌっと出てくる
メニューモード内
	ボタンを押して終了
	マップを作っておき、各メニューに名前を割り振ると二次元に広がったメニューになる
	名前を割り振ると上に名前リストがでてくる
	名前を割り振ると横に名前リストが出てくる

*/
// }