diff window/widget.cc @ 0:223b71206888

Initial import
author thib
date Fri, 01 Aug 2008 16:32:45 +0000
parents
children 8da1d92ac8f8
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/window/widget.cc
@@ -0,0 +1,1299 @@
+/*
+ * 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 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 (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 = 0;
+	press_pointer = 0;
+	drag_func = 0;
+	drag_pointer = 0;
+	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 = 0;
+	press_pointer = 0;
+	drag_func = 0;
+	drag_pointer = 0;
+	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 = 0;
+	arrow_up = 0;
+	cursor = 0;
+	panel = 0;
+
+	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) delete cursor;
+	cursor = 0;
+	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;
+
+	return;
+}
+
+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);
+	return;
+}
+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);
+	return;
+}
+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);
+	}
+	return;
+}
+
+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 == 0) 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;
+
+	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 == 0) 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 = 0;
+
+	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 = 0;
+	return;
+}
+
+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;
+	return;
+}
+
+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;
+	return;
+}
+
+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();
+	return;
+}
+
+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 == 0) 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 == 0) 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 = 0;
+	set_pointer = 0;
+
+	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(0);
+	root.DeleteSurface(surface_btn);
+	root.DeleteSurface(surface_diag);
+	return;
+}
+
+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;
+	}
+	return;
+}
+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, 見える部分は自動で作成」機能をつける?:バックログ)
+
+	
+	
+メニューの出し方
+	右クリック
+	ボタンを押す
+	上の方、右の方など領域に行くとヌっと出てくる
+メニューモード内
+	ボタンを押して終了
+	マップを作っておき、各メニューに名前を割り振ると二次元に広がったメニューになる
+	名前を割り振ると上に名前リストがでてくる
+	名前を割り振ると横に名前リストが出てくる
+
+*/
+// }