In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

forked from: forked from: Collada Viewer(Material追加)

/**
 * Copyright khello ( http://wonderfl.net/user/khello )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/vTv7
 */

// forked from civet's forked from: Collada Viewer(Material追加)
// forked from tencho's Collada Viewer

package  {
    import com.bit101.components.CheckBox;
    import com.bit101.components.PushButton;
    import com.bit101.components.Style;
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Loader;
    import flash.display.Sprite;
    import flash.events.ErrorEvent;
    import flash.events.Event;
    import flash.events.IOErrorEvent;
    import flash.events.MouseEvent;
    import flash.geom.Rectangle;
    import flash.net.FileFilter;
    import flash.net.FileReference;    
    import net.hires.debug.Stats;    
    import org.papervision3d.Papervision3D;
    import org.papervision3d.core.math.Number3D;
    import org.papervision3d.events.FileLoadEvent;
    import org.papervision3d.materials.BitmapMaterial;
    import org.papervision3d.objects.parsers.DAE;
    public class DaeTree extends Sprite {
        private const DISPLAY:Rectangle = new Rectangle(0, 0, 465, 465);
        private var fr:FileReference;
        private var fr2:FileReference;
        private var view:Preview;
        private var activeDae:DAE;
        private var loadBtn:PushButton;
        private var loadBtn2:PushButton;
        private var clearBtn:PushButton;
        private var win:TreeWindow;
        private var style:TreeStyle;
        private var activeTree:TreeLimb;
        private var bounding:BoundingManager;
        public function DaeTree() {
            Style.BUTTON_FACE = 0xF0F0F0;
            var bg:Sprite = addChild(new Sprite()) as Sprite;
            bg.graphics.beginFill(0x444444, 1);
            bg.graphics.drawRect(0, 0, DISPLAY.width, DISPLAY.height);
            bg.graphics.endFill();
            stage.frameRate = 30;
            Papervision3D.PAPERLOGGER.unregisterLogger(Papervision3D.PAPERLOGGER.traceLogger);
            bounding = new BoundingManager();
            var loader:IconLoader = new IconLoader();
            loader.create(onDecode);
        }
        private var plusBtn:PushButton;
        private var minusBtn:PushButton;
        private var fitBtn:PushButton;
        private function onDecode(icons:Vector.<BitmapData>):void {
            win = new TreeWindow(this, 10, 210, "MODEL EXPLORER");
            win.setWindowSize(446, 242);
            win.draggable = false;
            win.folder.label = "";
            win.folder.icon = -1;
            win.folder.isFolder = false;
            win.folder.addEventListener(TreeEvent.CHANGE_SELECT, onChangeSelect);
            style = new TreeStyle();
            style.closeIcon = icons[0];
            style.openIcon = icons[1];
            style.icons = icons.slice(2, icons.length);
            style.textFormat.size = 12;
            style.lineSpacing = 16;
            win.folder.setStyle(style);
            //
            view = new Preview(DISPLAY.width-70, 200);
            view.x = DISPLAY.width - view.rect.width;
            addChild(view);
            addChild(new Stats()).x = DISPLAY.width - 70 - view.rect.width;
            fr = new FileReference();
            fr.addEventListener(Event.SELECT, onSelectFile);
            fr.addEventListener(Event.COMPLETE, onLoadFile);
            fr.addEventListener(IOErrorEvent.IO_ERROR, onError);
            loadBtn = new PushButton(this, 2, 104, "LOAD DAE", onClickBrowse);
            loadBtn.setSize(66, 20);
            new PushButton(this, 48, 158, "+", onClickZoomIn).setSize(20,20);
            new PushButton(this, 48, 180, "-", onClickZoomOut).setSize(20,20);
            
            //
            fr2 = new FileReference();
            fr2.addEventListener(Event.SELECT, onSelectFile2);
            fr2.addEventListener(Event.COMPLETE, onLoadFile2);
            fr2.addEventListener(IOErrorEvent.IO_ERROR, onError);
            loadBtn2 =  new PushButton(this, 2, 126, "MATERIAL", onClickBrowse2);
            loadBtn2.setSize(66, 20);
            
            Style.LABEL_TEXT = 0xFFFFFF;
            new CheckBox(this, 8, 158, "GRID", onClickGrid).selected = true;
        }
        private function onClickGrid(e:MouseEvent):void{
            view.setGrid(CheckBox(e.currentTarget).selected);
        }
        private function onChangeSelect(...arg):void {
            var selected:Vector.<TreeLimb> = win.folder.getSelectedLimbs();
            if (selected.length == 0) {
                view.resetHighlight();
                return;
            }
            var obj:* = selected[0].extra;
            if (obj == null) return;
            view.highlightModel(obj);
            var bounds:BoundingData = bounding.getBounds(obj);
            if (bounds) {
                view.setTarget(bounds.globalCenter);
                var size:Number = Math.max(bounds.size.x, bounds.size.y, bounds.size.z);
                view.setDistance(Math.max(1, size * 2));
            }
        }
        private function onClickZoomIn(...arg):void {
            view.zoom(1);
        }
        private function onClickZoomOut(...arg):void {
            view.zoom(-1);
        }
        private function clearModel():void {
            if (!activeDae) return;
            bounding.deleteAllData();
            ModelUtil.disposeMaterials(activeDae);
            view.removeModel(activeDae);
            activeDae.stop();
            activeDae = null;
            for each(var l:TreeLimb in win.folder.getLimbs()) l.dispose();
        }
        private function onError(e:ErrorEvent):void {
        }
        
        //------LOAD METERIAL-----------------------
        private function onClickBrowse2(...arg):void
        {
            fr2.browse([new FileFilter("image file(*.jpg;*.jpeg;*.gif;*.png)", "*.jpg;*.jpeg;*.gif;*.png")]);
        }
        private function onSelectFile2(e:Event):void {
            loadBtn2.enabled = false;
            fr2.load();
        }
        private function onLoadFile2(e:Event):void {
            var loader:Loader = new Loader();
            loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadBitmap);
            loader.loadBytes(fr2.data);
        }
        private function onLoadBitmap(e:Event):void
        {
            var bmp:Bitmap = e.currentTarget.content as Bitmap;
            if(bmp && activeDae) {
                this.material = new BitmapMaterial(bmp.bitmapData);
                walkDAEchildren(activeDae.getChildByName("COLLADA_Scene") );
                view.render();
            }
            loadBtn2.enabled = true;
        }
        
        private var material:BitmapMaterial;
        private function walkDAEchildren( node:Object ):void {
            // Loop over all of the child elements of the node
            for each (var key:Object in node.children) {
                key.material = this.material;
                //key.useOwnContainer = true;
                //key.alpha = 0.5;
                // Recursively walk the DAE child element to reach its children
                walkDAEchildren( key );
            }
        }
        //-------------------------------------------
        
        private function onClickBrowse(...arg):void {
            fr.browse([new FileFilter("collada file(*.dae)", "*.dae")]);
        }
        private function onSelectFile(e:Event):void {
            loadBtn.enabled = false;
            win.folder.label = "LOADING...";
            fr.load();
        }
        private function onLoadFile(e:Event):void {
            clearModel();
            var dae:DAE = new DAE();
            dae.addEventListener(FileLoadEvent.LOAD_COMPLETE, onLoadDae);
            dae.load(fr.data);
        }
        private function onLoadDae(e:FileLoadEvent):void {
            win.folder.label = fr.name;
            if(1){
                //メモリリークしにくかったコード
                var dae:DAE = e.currentTarget as DAE;
                dae.removeEventListener(FileLoadEvent.LOAD_COMPLETE, onLoadDae);
                activeDae = dae;
            } else {
                //なぜかこっちだとメモリリーク
                activeDae = e.currentTarget as DAE;
                activeDae.removeEventListener(FileLoadEvent.LOAD_COMPLETE, onLoadDae);
            }
            loadBtn.enabled = true;
            //モデルをプレビュー画面に収める為に境界情報を取得
            var rootBounds:BoundingData = bounding.getBounds(activeDae);
            var size:Number = Math.max(rootBounds.size.x, rootBounds.size.y, rootBounds.size.z);
            var scale:Number = 300 / size;
            activeDae.scale = scale;
            bounding.scale(scale);
            var move:Number3D = new Number3D( -rootBounds.localCenter.x, -rootBounds.localAABB.minY, -rootBounds.localCenter.z);
            activeDae.position = Number3D.add(activeDae.position, move);
            bounding.move(move);
            view.setTarget(rootBounds.globalCenter);
            view.addModel(activeDae);
            view.setDistance(700);
            //ルートフォルダに解析したDAEの階層構造を追加
            win.folder.addLimb(ModelUtil.getObjectTree(activeDae));
        }
    }
}
import com.bit101.components.VScrollBar;
import com.bit101.components.Window;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.DisplayObjectContainer;
import flash.display.Loader;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.MouseEvent;
import flash.filters.GlowFilter;
import flash.geom.ColorTransform;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
import flash.utils.ByteArray;
import flash.utils.describeType;
import flash.utils.Dictionary;
import mx.utils.Base64Decoder;
import org.papervision3d.cameras.CameraType;
import org.papervision3d.core.geom.Lines3D;
import org.papervision3d.core.geom.renderables.Line3D;
import org.papervision3d.core.geom.renderables.Triangle3D;
import org.papervision3d.core.geom.renderables.Vertex3D;
import org.papervision3d.core.geom.TriangleMesh3D;
import org.papervision3d.core.math.AxisAlignedBoundingBox;
import org.papervision3d.core.math.Matrix3D;
import org.papervision3d.core.math.Number3D;
import org.papervision3d.core.proto.DisplayObjectContainer3D;
import org.papervision3d.materials.special.LineMaterial;
import org.papervision3d.objects.DisplayObject3D;
import org.papervision3d.objects.primitives.Plane;
import org.papervision3d.scenes.Scene3D;
import org.papervision3d.view.BasicView;
//##############################################################################################################
//    モデルプレビュー画面
//##############################################################################################################
class Preview extends BasicView {
    public var rect:Rectangle;
    private var _isDrag:Boolean = false;
    private var _isGrid:Boolean = true;
    private var _camTarget:DisplayObject3D;
    private var _camDistance:Number = 700;
    private var _camRotation:Number = -45;
    private var _camAngle:Number = 20;
    private var _saveRotation:Number;
    private var _saveAngle:Number;
    private var _saveMousePos:Point;
    private var _grid:Lines3D;
    private var _canvasContainer:Bitmap;
    private var _zoom:Number = 30;
    private var _canvas:BitmapData;
    private var _selectModels:Vector.<DisplayObject3D>;
    private var _activeModel:DisplayObject3D;
    private var _objectList:Vector.<DisplayObject3D>;
    public function get camDistance():Number { return _camDistance; }
    //
    public function Preview(width:Number, height:Number) {
        super(width, height, false, false, CameraType.FREE);
        rect = new Rectangle(0, 0, width, height);
        _canvas = new BitmapData(width, height, true);
        _camTarget = new DisplayObject3D();
        _grid = new Lines3D(new LineMaterial(0x444444, 1));
        _grid.y = -10;
        var gridStep:Number = 50;
        var gridNum:int = 4;
        for (var i:int = -gridNum; i < gridNum; i++) {
            for (var n:int = -gridNum; n <= gridNum; n++) {
                _grid.addNewLine(0, gridStep * i, 0, gridStep * n, gridStep * (i + 1), 0, gridStep * n);
                _grid.addNewLine(0, gridStep * n, 0, gridStep * i, gridStep * n, 0, gridStep * (i + 1));
            }
        }
        scene.addChild(_grid);
        mouseEnabled = true;
        mouseChildren = false;
        buttonMode = true;
        var bg:Sprite = addChildAt(new Sprite(), 0) as Sprite;
        _canvasContainer = addChild(new Bitmap(_canvas)) as Bitmap;
        bg.graphics.beginFill(0x666666);
        bg.graphics.drawRect(0, 0, width, height);
        addEventListener(MouseEvent.MOUSE_DOWN, onMsDown);
        addEventListener(MouseEvent.MOUSE_WHEEL, onMsWheel);
        setDistance(camDistance);
    }
    public function setGrid(visible:Boolean):void {
        _isGrid = visible;
        render();
    }
    private function onMsWheel(e:MouseEvent):void {
        var vec:int = e.delta / Math.abs(e.delta);
        if (isNaN(vec)) vec = 1;
        zoom(vec);
    }
    public function zoom(vec:int):void {
        _zoom -= vec * 2;
        if (_zoom < 0.1) _zoom = 0.1;
        _camDistance = _zoom * _zoom;
        render();
    }
    private function onMsMove(e:MouseEvent):void {
        var dragOffset:Point = new Point(viewport.mouseX, viewport.mouseY).subtract(_saveMousePos);
        _camRotation = _saveRotation - dragOffset.x * 0.5;
        _camAngle = _saveAngle + dragOffset.y * 0.5;
        if (_camAngle < -89) _camAngle = -89;
        if (_camAngle > 89) _camAngle = 89;
        render();
    }
    private function onMsDown(e:MouseEvent):void {
        stage.addEventListener(MouseEvent.MOUSE_MOVE, onMsMove);
        stage.addEventListener(MouseEvent.MOUSE_UP, onMsUp);
        _isDrag = true;
        _saveRotation = _camRotation;
        _saveAngle = _camAngle;
        _saveMousePos = new Point(viewport.mouseX, viewport.mouseY);
    }
    private function onMsUp(...rest):void {
        stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMsMove);
        stage.removeEventListener(MouseEvent.MOUSE_UP, onMsUp);
        _isDrag = false;
    }
    public function render():void {
        var per:Number = Math.cos(Math.PI / 180 * _camAngle);
        var px:Number = Math.cos(Math.PI / 180 * _camRotation) * _camDistance * per;
        var py:Number = Math.sin(Math.PI / 180 * _camAngle) * _camDistance;
        var pz:Number = Math.sin(Math.PI / 180 * _camRotation) * _camDistance * per;
        var offset:Number3D = new Number3D(px, py, pz);
        camera.position = Number3D.add(_camTarget.position, offset);
        camera.lookAt(_camTarget);
        if(_objectList != null && _selectModels != null){
            _canvas.lock();
            _canvas.fillRect(_canvas.rect, 0);
            _objectList.forEach(function(o:DisplayObject3D, ...arg):void { o.visible = true; } );
            _grid.visible = (true && _isGrid);
            onRenderTick();
            _canvas.draw(viewport);
            _canvas.colorTransform(_canvas.rect, new ColorTransform(1, 1, 1, 0.3));
            _objectList.forEach(function(o:DisplayObject3D, ...arg):void { o.visible = _selectModels.indexOf(o) != -1; } );
            _grid.visible = false;
            onRenderTick();
            viewport.filters = [new GlowFilter(0xFFFF00, 1, 3, 3, 10, 1)];
            _canvas.draw(viewport);
            viewport.filters = [];
            _canvas.unlock();
            _canvasContainer.visible = true;
        } else {
            _grid.visible = (true && _isGrid);
            onRenderTick();
            _canvasContainer.visible = false;
        }
    }
    public function resetHighlight():void {
        _selectModels = null;
        _objectList.forEach(function(o:DisplayObject3D, ...arg):void { o.visible = true; } );
        render();
    }
    public function highlightModel(model:DisplayObject3D):void {
        _selectModels = ModelUtil.getChildObject(model, true).concat(ModelUtil.getParentObject(model, false));
        render();
    }
    public function addModel(model:DisplayObject3D):void {
        _activeModel = scene.addChild(model);
        _objectList = ModelUtil.getChildObject(_activeModel, true);
        render();
    }
    public function removeModel(model:DisplayObject3D):void {
        _activeModel = null;
        _selectModels = null;
        scene.removeChild(model);
        _objectList.length = 0;
        render();
    }
    public function setDistance(num:Number):void {
        _camDistance = num;
        _zoom = Math.sqrt(num);
        render();
    }
    public function setTarget(pos:Number3D):void {
        _camTarget.position = pos.clone();
    }
}
/**
 * モデルの境界情報を管理する
 */
class BoundingManager {
    private var boundsData:Dictionary;
    public function BoundingManager() {
        boundsData = new Dictionary();
    }
    public function deleteAllData():void {
        boundsData = new Dictionary();
    }
    public function move(point:Number3D):void {
        for (var k:* in boundsData) if(boundsData[k]) boundsData[k].move(point);
    }
    public function scale(num:Number, point:Number3D = null):void {
        for (var k:* in boundsData) if(boundsData[k]) boundsData[k].scale(num, point);
    }
    public function getBounds(obj:DisplayObject3D):BoundingData {
        if (!boundsData[obj]) (obj is TriangleMesh3D)? calculateTriangle(TriangleMesh3D(obj)) : calculateObject(obj);
        return boundsData[obj];
    }
    public function calculateObject(obj:DisplayObject3D):void {
        var transform:Matrix3D = ModelUtil.getWorldTransform(obj);
        var position:Number3D = obj.position.clone();
        Matrix3D.multiplyVector4x4(transform, position);
        var bounds:BoundingData = null;
        for each(var o:DisplayObject3D in obj.children) {
            var mixBound:BoundingData = getBounds(o);
            if (!bounds) {
                if (mixBound) bounds = mixBound.clone();
            } else if (mixBound) {
                bounds.union(mixBound, position);
            }
        }
        boundsData[obj] = bounds;
    }
    public function calculateTriangle(tri:TriangleMesh3D):void {
        var vts:Array = new Array();
        var transform:Matrix3D = ModelUtil.getWorldTransform(tri);
        var position:Number3D = tri.position.clone();//*
        Matrix3D.multiplyVector4x4(transform, position);
        for each(var vt:Vertex3D in tri.geometry.vertices) {
            var n:Number3D = vt.toNumber3D();
            Matrix3D.multiplyVector4x4(transform, n);
            vts.push(new Vertex3D(n.x - position.x, n.y - position.y, n.z - position.z));//*
        }
        var result:BoundingData = new BoundingData();
        result.localAABB = AxisAlignedBoundingBox.createFromVertices(vts);
        result.calculate();
        result.setPosition(position);
        boundsData[tri] = result;
    }
}
/**
 * モデルの境界情報
 */
class BoundingData {
    public var localAABB:AxisAlignedBoundingBox;
    public var globalAABB:AxisAlignedBoundingBox;
    public var localCenter:Number3D;
    public var globalCenter:Number3D;
    public var size:Number3D;
    public var position:Number3D;
    public function BoundingData() {
        localAABB = new AxisAlignedBoundingBox(0, 0, 0, 0, 0, 0);
        globalAABB = new AxisAlignedBoundingBox(0, 0, 0, 0, 0, 0);
        size = new Number3D();
        position = new Number3D();
        localCenter = new Number3D();
        globalCenter = new Number3D();
    }
    public function calculate():void {
        size.x = (localAABB.maxX - localAABB.minX);
        size.y = (localAABB.maxY - localAABB.minY);
        size.z = (localAABB.maxZ - localAABB.minZ);
        localCenter.x = (localAABB.maxX + localAABB.minX) * 0.5;
        localCenter.y = (localAABB.maxY + localAABB.minY) * 0.5;
        localCenter.z = (localAABB.maxZ + localAABB.minZ) * 0.5;
    }
    public function setPosition(pos:Number3D):void {
        position = pos.clone();
        for each(var v:String in ["x", "y", "z"]) {
            for each(var m:String in ["min", "max"]) {
                var v2:String = v.toUpperCase();
                globalAABB[m + v2] = localAABB[m + v2] + position[v];
            }
        }
        globalCenter = Number3D.add(localCenter, position);
    }
    public function union(bounds:BoundingData, pos:Number3D):void {
        position = pos.clone();
        globalAABB.merge(bounds.globalAABB);
        for each(var v:String in ["x", "y", "z"]) {
            for each(var m:String in ["min", "max"]) {
                var v2:String = v.toUpperCase();
                localAABB[m + v2] = globalAABB[m + v2] - position[v];
            }
        }
        calculate();
        globalCenter = Number3D.add(localCenter, position);
    }
    public function move(xyz:Number3D):void {
        position.plusEq(xyz);
        setPosition(position);
    }
    public function scale(num:Number, point:Number3D = null):void {
        if (!point) point = new Number3D();
        position.minusEq(point);
        position.multiplyEq(num);
        position.plusEq(point);
        for each(var v:String in ["maxX", "maxY", "maxZ", "minX", "minY", "minZ"]) localAABB[v] *= num;
        calculate();
        setPosition(position);
    }
    public function clone():BoundingData {
        var bd:BoundingData = new BoundingData();
        for each(var v:String in ["x", "y", "z"]) {
            for each(var m:String in ["min", "max"]) {
                var v2:String = v.toUpperCase();
                bd.localAABB[m + v2] = localAABB[m + v2];
                bd.globalAABB[m + v2] = globalAABB[m + v2];
            }
        }
        bd.size = size.clone();
        bd.localCenter = localCenter.clone();
        bd.globalCenter = globalCenter.clone();
        bd.position = position.clone();
        return bd;
    }
}
class ModelUtil {
    static public function getParentObject(obj:DisplayObject3D, addMe:Boolean = true):Vector.<DisplayObject3D> {
        var list:Vector.<DisplayObject3D> = new Vector.<DisplayObject3D>();
        if (addMe) list.push(obj);
        var parent:DisplayObject3D = obj.parent as DisplayObject3D;
        if (parent) list = list.concat(getParentObject(parent, true));
        return list;
    }
    static public function getChildObject(obj:DisplayObject3D, addMe:Boolean = true):Vector.<DisplayObject3D> {
        var list:Vector.<DisplayObject3D> = new Vector.<DisplayObject3D>();
        if(addMe) list.push(obj);
        for each(var d:DisplayObject3D in obj.children) list = list.concat(getChildObject(d, true));
        return list;
    }
    static public function getChildTriangle(obj:DisplayObject3D, addMe:Boolean = true):Vector.<TriangleMesh3D> {
        var list:Vector.<TriangleMesh3D> = new Vector.<TriangleMesh3D>();
        if (addMe && describeType(obj).@name.split("::")[1] == "TriangleMesh3D") list.push(obj);
        for each(var d:DisplayObject3D in obj.children) list = list.concat(getChildTriangle(d, true));
        return list;
    }
    static public function forEachModel(obj:DisplayObject3D, func:Function, thisArg:* = null):void {
        func.apply(thisArg, [obj]);
        for each(var d:DisplayObject3D in obj.children) forEachModel(d, func);
    }
    /**
     * モデルの構造をTreeLimbオブジェクトで出力する
     */
    static public function getObjectTree(obj:DisplayObjectContainer3D):TreeLimb {
        var className:String = describeType(obj).@name.split("::")[1];
        var icon:int = (obj is TriangleMesh3D)? 5 : 0;
        var isFolder:Boolean = (className == "DisplayObject3D" || className == "DAE");
        var disp:DisplayObject3D = obj as DisplayObject3D;
        var label:String;
        if (disp != null) {
            var error:String = (disp.id == Number(disp.name))? "*" : "";
            label = error + disp.name +" (" + className + ")";
        } else {
            label = "(" + className + ")\n";
        }
        var tree:TreeLimb = new TreeLimb(label, isFolder, icon, obj);
        for each(var d:DisplayObject3D in obj.children) tree.addLimb(getObjectTree(d));
        return tree;
    }
    static public function getWorldTransform(obj:DisplayObject3D):Matrix3D {
        obj.translate(0, new Number3D());
        var res:Matrix3D = new Matrix3D();
        res.copy(obj.transform);
        var parent3D:DisplayObject3D = obj.parent as DisplayObject3D;
        if (parent3D != null) res.calculateMultiply(getWorldTransform(parent3D), res);
        return res;
    }
    /**
     * モデルのテクスチャを全部破棄(他と共有していた場合も関係なく消す)
     */
    static public function disposeMaterials(model:DisplayObject3D):void {
        for each(var tr:TriangleMesh3D in getChildTriangle(model, true)) {
            for each(var tg:Triangle3D in tr.geometry.faces) {
                if (tg.material) {
                    if (tg.material.bitmap) tg.material.bitmap.dispose();
                    tg.material.destroy();
                    tg.material = null;
                }
            }
        }
    }
}
//##############################################################################################################
//    以下ツリー表示クラス
//##############################################################################################################
/**
 * ツリーウィンドウ
 */
class TreeWindow extends Window {
    private var _workSpace:Rectangle;
    private var _container:Sprite;
    private var _bg:Sprite;
    private var _folder:TreeLimb;
    private var _vscroll:VScrollBar;
    private var _hscroll:VScrollBar;
    private var _padding:Number = 10;
    private var _bgcolor:uint = 0xF9F9F9;
    public function get folder():TreeLimb { return _folder; }
    public function get bgcolor():uint { return _bgcolor; }
    public function set bgcolor(value:uint):void {
        _bgcolor = value;
        var ct:ColorTransform = new ColorTransform();
        ct.color = _bgcolor;
        _bg.transform.colorTransform = ct;
    }
    public function TreeWindow(parent:DisplayObjectContainer = null, xpos:Number = 0, ypos:Number = 0, title:String = "Window") {
        super(parent, xpos, ypos, title);
        _workSpace = new Rectangle();
        _bg = content.addChild(new Sprite()) as Sprite;
        _bg.graphics.clear();
        _bg.graphics.beginFill(0xFFFFFF);
        _bg.graphics.drawRect(0, 0, 100, 100);
        _bg.graphics.endFill();
        _bg.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDownBg);
        addEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel);
        _container = content.addChild(new Sprite()) as Sprite;
        _folder = _container.addChild(new TreeLimb()) as TreeLimb;
        _folder.addEventListener(TreeEvent.RESIZE, onResize);
        _vscroll = new VScrollBar(content, 0, 0, onVScroll);
        _hscroll = new VScrollBar(content, 0, 0, onHScroll);
        _hscroll.rotation = -90;
        setWindowSize(width, height);
        var rect:Rectangle = _folder.getVisibleRect();
        rect.inflate(_padding, _padding);
        var px:int = int(rect.left), py:int = int(rect.top);
        _hscroll.setSliderParams(px, px + 100, px);
        _vscroll.setSliderParams(py, py + 100, py);
        bgcolor = _bgcolor;
    }
    private function onMouseWheel(e:MouseEvent):void {
        var vec:int = e.delta / Math.abs(e.delta);
        if (!_vscroll.enabled) return;
        _vscroll.value += vec * -30;
    }
    private function onMouseDownBg(e:MouseEvent):void {
        _folder.resetSelect();
    }
    private function onHScroll(...arg):void{
        _folder.x = int(_workSpace.x - _hscroll.value);
    }
    private function onVScroll(...arg):void {
        _folder.y = int(_workSpace.y - _vscroll.value);
    }
    private function onResize(e:TreeEvent):void {
        e.bounds.inflate(_padding, _padding);
        var perV:Number = Math.min(2, _workSpace.height / e.bounds.height);
        var perH:Number = Math.min(2, _workSpace.width / e.bounds.width);
        _vscroll.enabled = (perV < 1);
        _hscroll.enabled = (perH < 1);
        if (perV < 0.1 || isNaN(perV)) perV = 0.1;
        if (perH < 0.1 || isNaN(perH)) perH = 0.1;
        _vscroll.setThumbPercent(perV);
        _hscroll.setThumbPercent(perH);
        if (!_vscroll.enabled) _vscroll.value = e.bounds.top;
        if (!_hscroll.enabled) _hscroll.value = e.bounds.left;
        _vscroll.setSliderParams(e.bounds.top, e.bounds.height - _workSpace.height - _padding, _vscroll.value);
        _hscroll.setSliderParams(e.bounds.left, e.bounds.width - _workSpace.width - _padding, _hscroll.value);
    }
    public function setWindowSize(w:Number, h:Number):void {
        setSize(w, h);
        _workSpace = new Rectangle(0, 0, width - _vscroll.width, height - _titleBar.height - _hscroll.width);
        _hscroll.x = _workSpace.left;
        _hscroll.y = _workSpace.bottom + 10;
        _vscroll.x = _workSpace.right;
        _vscroll.y = _workSpace.top;
        _hscroll.height = _workSpace.width;
        _vscroll.height = _workSpace.height;
        _container.scrollRect = _workSpace;
        _bg.width = _workSpace.width;
        _bg.height = _workSpace.height;
    }
    public function dispose():void {
        _folder.dispose();
        removeEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel);
        _bg.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDownBg);
        _folder.removeEventListener(TreeEvent.RESIZE, onResize);
    }
}
/**
 * ツリー表示用スタイル
 */
class TreeStyle {
    public var openIcon:BitmapData;    //フォルダを開いた時の画像
    public var closeIcon:BitmapData;    //フォルダを閉じた時の画像
    public var noIcon:BitmapData;    //アイコンが無い時の画像
    public var icons:Vector.<BitmapData> = new Vector.<BitmapData>();    //アイコン画像リスト
    public var textFormat:TextFormat = new TextFormat("_sans", 14, 0x000000);    //テキストフォーマット
    public var selectedBoxColor:uint = 0x4CA4D8;    //
    public var selectedLabelColor:uint = 0xFFFFFF;    //
    public var dotV:BitmapData;    //破線画像(縦)
    public var dotH:BitmapData;    //破線画像(横)
    public var buttonSize:Number = 11;    //フォルダ開閉ボタンのサイズ(奇数値推奨)
    public var lineSpacing:Number = 20;    //行間
    public var lineIndent:Number = 14;    //横線の長さ
    public var labelOffset:Point = new Point(10, 0);    //ラベルの位置
    public var treeOffset:Point = new Point(8, 8);
    public function TreeStyle():void {
        dotV = new BitmapData(1, 2, true, 0);
        dotV.setPixel32(0, 0, 0xFF000000);
        dotH = new BitmapData(2, 1, true, 0);
        dotH.setPixel32(0, 0, 0xFF000000);
        noIcon = new BitmapData(3, 3, false, 0xFF000000);
        openIcon = new BitmapData(7, 7, false, 0xFF000000);
        openIcon.fillRect(new Rectangle(1, 1, 5, 5), 0xFFFFFFFF);
        closeIcon = openIcon.clone();
        closeIcon.fillRect(new Rectangle(2, 2, 3, 3), 0xFF000000);
    }
    public function clone():TreeStyle {
        var newStyle:TreeStyle = new TreeStyle();
        for each(var v:XML in describeType(this).variable) {
            if (v.@type.indexOf("Vector") != -1) for each(var bd:BitmapData in this[v.@name]) newStyle.icons.push(bd.clone());
            else if (v.@type.indexOf("BitmapData") != -1) newStyle[v.@name] = this[v.@name].clone();
            else if (v.@type.indexOf("TextFormat") == -1) newStyle[v.@name] = this[v.@name];
        }
        var ba:ByteArray = new ByteArray();
        ba.writeObject(textFormat);
        ba.position = 0;
        var obj:Object = ba.readObject();
        for (var k:String in obj) newStyle.textFormat[k] = obj[k];
        return newStyle;
    }
}
/**
 * ツリーアイテム
 */
class TreeLimb extends Sprite {
    private var _icon:int;
    private var _isFolder:Boolean;
    private var _label:String;
    private var _extra:*;
    private var _isOpen:Boolean = true;
    private var _selected:Boolean = false;
    private var _parentLimb:TreeLimb;
    private var _rootLimb:TreeLimb;
    private var _isRoot:Boolean = true;
    private var _style:TreeStyle = new TreeStyle();
    private var _isDirtyStyle:Boolean = true;
    private var _lastParent:DisplayObjectContainer;
    private var _lastLineVisible:Boolean = true;
    private var _lastBounds:Rectangle;
    private var _itemContainer:Sprite;
    private var _switchContainer:Sprite;
    private var _openSprite:Sprite;
    private var _closeSprite:Sprite;
    private var _iconSprite:Sprite;
    private var _openIcon:Bitmap;
    private var _closeIcon:Bitmap;
    private var _labelText:TextField;
    private var _limbs:Sprite;
    private var _lineV:Sprite;
    private var _lineH:Sprite;
    private var _selectBox:Sprite;
    private var _selectedItems:Vector.<TreeLimb>;
    private var _onStage:Boolean = false;    //ステージ上に配置されているか
    private var _isAdding:Boolean = false;    //addLimb()等で追加されたオブジェクトか
    private var _beforeMoveParent:TreeLimb;    //移動直前の親
    private var _isGhost:Boolean = false;    //既に破棄されたアイテムか
    private var _isDirtyChild:Boolean = true;    //自分の子も更新対象にするか
    private var _lastVisibleCount:int = 1;    //最新の見えているフォルダ数
    private var _isDirtyVisibleCount:Boolean = true;    //見えているフォルダの数が変化した
    /**[g/s]ユーザーデータ*/
    public function get extra():* { return _extra; }
    public function set extra(value:*):void { _extra = value; }
    /**[g/s]表示テキスト*/
    public function get label():String { return _label; }
    public function set label(value:String):void { _labelText.text = _label = value; refreshLabel(); }
    /**[g/s]フォルダアイコンを使うか*/
    public function get isFolder():Boolean { return _isFolder; }
    public function set isFolder(value:Boolean):void { _isFolder = value; refresh(false); }
    /**[g/s]ファイルアイコンの種類(isFolder=trueで無効。-1でアイコン無し。)*/
    public function get icon():int { return _icon; }
    public function set icon(value:int):void { _icon = value; refreshStyle(); }
    /**[g/s]自分が選択されているか*/
    public function get selected():Boolean { return _selected; }
    public function set selected(value:Boolean):void { setSelect(value, true); }
    /**[g/s]サブフォルダを開いているか*/
    public function get isOpen():Boolean { return _isOpen; }
    public function set isOpen(value:Boolean):void { setOpen(value); }
    /**[g]ルートフォルダ*/
    public function get rootLimb():TreeLimb { return _rootLimb; }
    /**[q]自分がルートフォルダか*/
    public function get isRoot():Boolean { return _isRoot; }
    /**[g]スタイル*/
    public function get style():TreeStyle { return _style; }
    /**[q]子の数*/
    public function get numLimb():int { return _limbs.numChildren; }
    //コンストラクタ
    public function TreeLimb(label:String = "New Item", isFolder:Boolean = true, icon:int = 0, extra:* = null) {
        _selectedItems = new Vector.<TreeLimb>();
        _lineV = addChild(new Sprite()) as Sprite;
        _lineH = addChild(new Sprite()) as Sprite;
        _itemContainer = addChild(new Sprite()) as Sprite;
        _selectBox = _itemContainer.addChild(new Sprite()) as Sprite;
        _selectBox.graphics.beginFill(0xBE9852, 1);
        _selectBox.graphics.drawRect(0, 0, 100, 10);
        _switchContainer = _itemContainer.addChild(new Sprite()) as Sprite;
        _openSprite = _switchContainer.addChild(new Sprite()) as Sprite;
        _closeSprite = _switchContainer.addChild(new Sprite()) as Sprite;
        _closeSprite.mouseEnabled = false;
        _openSprite.addEventListener(MouseEvent.CLICK, onClickOpen);
        _openSprite.buttonMode = true;
        _iconSprite = _itemContainer.addChild(new Sprite()) as Sprite;
        _labelText = _itemContainer.addChild(new TextField()) as TextField;
        _labelText.autoSize = TextFieldAutoSize.LEFT;
        _labelText.selectable = false;
        _limbs = addChild(new Sprite()) as Sprite;
        _iconSprite.buttonMode = _selectBox.buttonMode = true;
        _iconSprite.doubleClickEnabled = _selectBox.doubleClickEnabled = true;
        _iconSprite.addEventListener(MouseEvent.CLICK, onClickIcon);
        _iconSprite.addEventListener(MouseEvent.DOUBLE_CLICK, onWclickIcon);
        _selectBox.addEventListener(MouseEvent.CLICK, onClickIcon);
        _selectBox.addEventListener(MouseEvent.DOUBLE_CLICK, onWclickIcon);
        _label = label;
        _labelText.text = _label;
        _labelText.mouseEnabled = false;
        _isFolder = isFolder;
        _icon = icon;
        _extra = extra;
        checkRoot(true);
        addEventListener(Event.ADDED, onAdded);
        addEventListener(Event.ADDED_TO_STAGE, onAddedStage);
        addEventListener(Event.REMOVED_FROM_STAGE, onRemoveStage);
        refresh();
    }
    private function onRemoveStage(e:Event):void {
        _onStage = false;
        if (_isAdding) _beforeMoveParent = _parentLimb;
    }
    private function onAddedStage(e:Event):void {
        _onStage = true;
    }
    private function onAdded(e:Event):void { checkRoot(); }
    private function onClickOpen(e:MouseEvent):void { setOpen(!_isOpen); }
    private function onClickIcon(e:MouseEvent):void {
        setSelect(true);
        _rootLimb.dispatchEvent(new TreeEvent(TreeEvent.CLICK_ITEM, this));
    }
    private function onWclickIcon(e:MouseEvent):void {
        setOpen(!_isOpen);
        _rootLimb.dispatchEvent(new TreeEvent(TreeEvent.WCLICK_ITEM, this));
    }
    private function getRoot():TreeLimb { return (_parentLimb == null)? this : _parentLimb.getRoot(); }
    private function checkRoot(exe:Boolean = false):void {
        if (parent === _lastParent && !exe) return;
        _lastParent = parent;
        _parentLimb = (parent == null || parent.parent == null)? null : parent.parent as TreeLimb;
        _rootLimb = getRoot();
        _isRoot = _rootLimb === this;
        for each(var l:TreeLimb in getLimbs()) l.checkRoot(true);
    }
    /**選択されている全てのアイテムを取得*/
    public function getSelectedLimbs():Vector.<TreeLimb> {
        return _rootLimb._selectedItems.concat();
    }
    /**全ての選択を解除*/
    public function resetSelect(dispatch:Boolean = true):void {
        for each(var l:TreeLimb in _rootLimb._selectedItems) l.setSelect(false, false, false);
        if (dispatch) _rootLimb.dispatchEvent(new TreeEvent(TreeEvent.CHANGE_SELECT, this));
    }
    /**自分を選択or解除*/
    public function setSelect(selected:Boolean = true, multiSelect:Boolean = false, dispatch:Boolean = true):void {
        _selected = selected;
        if (_selected && !multiSelect) for each(var l:TreeLimb in _rootLimb._selectedItems) if(l !== this) l.setSelect(false, multiSelect, false);
        var index:int = _rootLimb._selectedItems.indexOf(this);
        if (_selected && index == -1) _rootLimb._selectedItems.push(this);
        if (!_selected && index != -1) _rootLimb._selectedItems.splice(index, 1);
        refreshSelect();
        if (dispatch) _rootLimb.dispatchEvent(new TreeEvent(TreeEvent.CHANGE_SELECT, this));
    }
    /**フォルダの開閉*/
    public function setOpen(isOpen:Boolean = true, subLimbs:Boolean = false):void {
        _isOpen = isOpen;
        if (subLimbs) for each(var l:TreeLimb in getLimbs(true)) l.setOpen(isOpen, subLimbs);
        refreshDirty();
    }
    /**自分のインデックス位置を取得*/
    public function getIndex():int {
        return (!parent)? 0 : parent.getChildIndex(this);
    }
    private function getParentLimbs():Vector.<TreeLimb> {
        var result:Vector.<TreeLimb> = new Vector.<TreeLimb>();
        if (!isRoot) {
            result.push(_parentLimb);
            result = result.concat(_parentLimb.getParentLimbs());
        }
        return result;
    }
    private function getUpdateLimbs():Vector.<TreeLimb> {
        var result:Vector.<TreeLimb> = new Vector.<TreeLimb>();
        if (isRoot) {
            result.push(this);
        } else if (_parentLimb) {
            for (var i:int = getIndex(); i < _parentLimb.numLimb; i++) result.push(_parentLimb.getLimbAt(i) as TreeLimb);
            result = result.concat(_parentLimb.getUpdateLimbs());
        }
        return result;
    }
    /**自分の子をまとめて取得*/
    public function getLimbs(subLimbs:Boolean = false, addMe:Boolean = false):Vector.<TreeLimb> {
        var result:Vector.<TreeLimb> = new Vector.<TreeLimb>();
        for (var i:int = 0; i < numLimb; i++) {
            var limb:TreeLimb = getLimbAt(i);
            result.push(limb);
            if (subLimbs) result = result.concat(limb.getLimbs(subLimbs));
        }
        if (addMe) result.push(this);
        return result;
    }
    /**ユーザーデータが一致する全ての子を取得*/
    public function getLimbByExtra(extra:*, subLimbs:Boolean = true):Vector.<TreeLimb> {
        var result:Vector.<TreeLimb> = new Vector.<TreeLimb>();
        for each(var limb:TreeLimb in getLimbs(subLimbs)) if (limb.extra === extra) result.push(limb);
        return result;
    }
    /**ラベル名が一致する全ての子を取得*/
    public function getLimbByLabel(label:String, subLimbs:Boolean = true):Vector.<TreeLimb> {
        var result:Vector.<TreeLimb> = new Vector.<TreeLimb>();
        for each(var limb:TreeLimb in getLimbs(subLimbs)) if (limb._label == label) result.push(limb);
        return result;
    }
    /**指定インデックスの子を取得*/
    public function getLimbAt(index:int):TreeLimb {
        return (index < 0 || index >= numLimb)? null : _limbs.getChildAt(index) as TreeLimb;
    }
    /**TreeLimbオブジェクトを子に追加*/
    public function addLimb(limb:TreeLimb):TreeLimb {
        return addLimbAt(limb, numLimb);
    }
    /**複数のTreeLimbオブジェクトを子に追加*/
    public function addLimbs(limbs:Vector.<TreeLimb>):Vector.<TreeLimb> {
        for each(var l:TreeLimb in limbs) addLimb(l);
        return limbs;
    }
    /**TreeLimbオブジェクトを指定インデックスに追加*/
    public function addLimbAt(limb:TreeLimb, index:int = -1):TreeLimb {
        for each(var l:TreeLimb in limb.getLimbs(true, true)) if(l.selected) l.setSelect(false);
        limb._isAdding = true;
        if (limb._parentLimb === this) limb.parent.removeChild(limb);
        if (index > numLimb || index < 0) index = numLimb;
        if (limb.parent != null) limb.parent.removeChild(limb);
        _limbs.addChildAt(limb, index);
        limb.newStyle(_style, true);
        limb.refreshDirty();
        if (limb._beforeMoveParent) {
            limb._beforeMoveParent.refreshDirty();
        }
        _beforeMoveParent = null;
        limb.refresh();
        limb._isAdding = false;
        return limb;
    }
    /**ファイルを子に追加*/
    public function addFile(label:String = "", icon:int = 0, extra:* = null):TreeLimb {
        return addLimb(new TreeLimb(label, false, icon, extra));
    }
    /**複数のファイルを子に追加*/
    public function addFiles(labels:Array, icons:Array = null, extras:Array = null):Vector.<TreeLimb> {
        if (icons == null) icons = new Array();
        if (extras == null) extras = new Array();
        while (icons.length < labels.length) icons.push(0);
        while (extras.length < labels.length) extras.push(null);
        var result:Vector.<TreeLimb> = new Vector.<TreeLimb>();
        for (var i:int = 0; i < labels.length; i++) result.push(addFile(labels[i], icons[i], extras[i]));
        return result;
    }
    /**フォルダを子に追加*/
    public function addFolder(label:String = "", extra:* = null):TreeLimb {
        return addLimb(new TreeLimb(label, true, 0, extra));
    }
    /**複数のフォルダを子に追加*/
    public function addFolders(labels:Array, extras:Array = null):Vector.<TreeLimb> {
        if (extras == null) extras = new Array();
        while (extras.length < labels.length) extras.push(null);
        var result:Vector.<TreeLimb> = new Vector.<TreeLimb>();
        for (var i:int = 0; i < labels.length; i++) result.push(addFolder(labels[i], extras[i]));
        return result;
    }
    /**TreeLimbオブジェクトを子から切り離す*/
    public function removeLimb(limb:TreeLimb):Boolean {
        for each(var l:TreeLimb in getLimbs(true, true)) if (l === limb) { l.remove(); return true; }
        return false;
    }
    /**複数のTreeLimbオブジェクトを子から切り離す*/
    public function removeLimbs(limbs:Vector.<TreeLimb>):int {
        var count:int = 0;
        for each(var l:TreeLimb in limbs) count += int(l.removeLimb(l));
        return count;
    }
    private function destroyData(subLimbs:Boolean = true):void {
        removeAllListeners();
        _extra = null;
        _selected = false;
        _isGhost = true;
        _selectedItems.length = 0;
        if(subLimbs) for each(var l:TreeLimb in getLimbs()) l.destroyData(true);
    }
    /**自分を破棄する*/
    public function dispose(subLimbs:Boolean = true):void {
        remove();
        destroyData(subLimbs);
    }
    /**自分を親から切り離す*/
    public function remove():Boolean {
        for each(var l:TreeLimb in getLimbs(true, true)) if(l.selected) l.setSelect(false);
        if (parent == null) return false;
        parent.removeChild(this);
        if (!isRoot && !_rootLimb._isGhost) refreshDirty();
        checkRoot(true);
        return true;
    }
    /**内部イベントリスナを全て削除*/
    public function removeAllListeners():void {
        removeEventListener(Event.ADDED, onAdded);
        removeEventListener(Event.ADDED_TO_STAGE, onAddedStage);
        removeEventListener(Event.REMOVED_FROM_STAGE, onRemoveStage);
        _iconSprite.removeEventListener(MouseEvent.CLICK, onClickIcon);
        _iconSprite.removeEventListener(MouseEvent.DOUBLE_CLICK, onWclickIcon);
        _selectBox.removeEventListener(MouseEvent.CLICK, onClickIcon);
        _selectBox.removeEventListener(MouseEvent.DOUBLE_CLICK, onWclickIcon);
        _openSprite.removeEventListener(MouseEvent.CLICK, onClickOpen);
    }
    /**自分の子をソートして再配置*/
    public function sortOn(names:String, options:Object = null):void {
        var list:Array = new Array();
        for each(var l:TreeLimb in getLimbs()) list.push(l);
        list.sortOn(names, options);
        var sortedLimbs:Vector.<TreeLimb> = new Vector.<TreeLimb>();
        for each(var limb:TreeLimb in list) sortedLimbs.push(limb);
        addLimbs(sortedLimbs);
    }
    /**繋がった全てのアイテムのスタイルを更新*/
    public function updateStyle():void {
        setStyle(_style);
    }
    /**繋がった全てのアイテムにスタイルを適用*/
    public function setStyle(style:TreeStyle):void {
        _rootLimb.newStyle(style, true);
    }
    private function newStyle(style:TreeStyle, subLimbs:Boolean = true):void {
        _style = style;
        refreshStyle();
        refreshSelect();
        if (subLimbs) for each(var l:TreeLimb in getLimbs()) l.newStyle(_style, subLimbs);
        refresh();
    }
    private function refreshSelect():void {
        _labelText.textColor = (_selected)? _style.selectedLabelColor : uint(_style.textFormat.color);
        _selectBox.alpha = int(_selected);
    }
    private function refreshDirty():void {
        _isDirtyVisibleCount = true;
        for each(var tl1:TreeLimb in getParentLimbs()) tl1._isDirtyVisibleCount = true;
        for each(var tl2:TreeLimb in getUpdateLimbs()) {
            tl2._isDirtyChild = false;
            tl2.refresh(true);
        }
    }
    private function refresh(subLimbs:Boolean = true):void {
        var showLine:Boolean = !(_isRoot && numLimb == 0);
        if (showLine != _lastLineVisible) refreshStyle();
        var lineHeight:Number = 0;
        var heightList:Array = new Array();
        if (_isOpen && subLimbs) {
            var nextY:Number = 0;
            for (var i:int = 0; i < numLimb; i++ ) {
                var limb:TreeLimb = _limbs.getChildAt(i) as TreeLimb;
                limb.x = 0;
                limb.y = nextY;
                if (_isDirtyChild) limb.refresh(subLimbs);
                nextY += heightList[heightList.push(limb.getVisibleCount() * _style.lineSpacing) - 1];
            }
            heightList.forEach(function(...arg):void { lineHeight += (arg[1] == heightList.length - 1)? _style.lineSpacing : arg[0]; } );
        }
        if (_isFolder) _closeIcon.visible = !(_openIcon.visible = (_isOpen && numLimb));
        _limbs.visible = _isOpen;
        _openSprite.visible = (numLimb > 0);
        _closeSprite.visible = _openSprite.visible && !_isOpen;
        _lineV.graphics.clear();
        _lineV.graphics.beginBitmapFill(_style.dotV);
        _lineV.graphics.drawRect(0, 0, 1, lineHeight);
        _lineV.graphics.endFill();
        if (_isRoot) {
            var rect:Rectangle = getVisibleRect();
            if (_lastBounds == null || !rect.equals(_lastBounds)) {
                var event:TreeEvent = new TreeEvent(TreeEvent.RESIZE, this);
                event.bounds = rect.clone();
                dispatchEvent(event);
                _lastBounds = rect;
            }
        }
        _isDirtyChild = false;
        _lastLineVisible = showLine;
    }
    private function refreshLabel():void {
        _labelText.setTextFormat(_style.textFormat);
        _selectBox.width = _labelText.textWidth + 4;
        _selectBox.height = _labelText.textHeight;
        refreshSelect();
    }
    private function refreshStyle():void {
        refreshLabel();
        var showLine:Boolean = !(_isRoot && numLimb == 0);
        var cornerX:int = _style.treeOffset.x + (int(_style.buttonSize / 2) + _style.lineIndent) * int(showLine);
        while (_iconSprite.numChildren) _iconSprite.removeChildAt(0);
        var icons:Vector.<Bitmap> = new Vector.<Bitmap>();
        if (_isFolder) {
            _openIcon = new Bitmap(_style.openIcon);
            _closeIcon = new Bitmap(_style.closeIcon);
            icons.push(_openIcon, _closeIcon);
        } else if (_style.icons.length > _icon && _icon >= 0) icons.push(new Bitmap(_style.icons[_icon]));
        else icons.push(new Bitmap(_style.noIcon));
        for each(var ico:Bitmap in icons){
            _iconSprite.addChild(ico);
            ico.x = cornerX - int(ico.width / 2);
            ico.y = _style.treeOffset.y - int(ico.height / 2);
        }
        _lineV.x = cornerX;
        _lineH.x = _style.treeOffset.x;
        _lineV.y = _lineH.y = _style.treeOffset.y;
        _lineH.graphics.clear();
        if (showLine) {
            _lineH.graphics.beginBitmapFill(_style.dotH);
            _lineH.graphics.drawRect(0, 0, int(_style.buttonSize / 2) + _style.lineIndent, 1);
        }
        var boxw:Number = _style.buttonSize, thick:Number = 1, linew:Number = Math.max(3, boxw - thick * 2 - 4), lineh:Number = 1;
        _openSprite.graphics.clear();
        for each(var draw:Array in [[0, 0, 0, boxw, boxw], [1, thick, thick, boxw - thick * 2, boxw - thick * 2], [0, (boxw - linew) / 2, (boxw - lineh) / 2, linew, lineh]]) {
            _openSprite.graphics.beginFill(draw.shift() * 0xFFFFFF);
            _openSprite.graphics.drawRect.apply(null, draw);
        }
        _closeSprite.graphics.clear();
        _closeSprite.graphics.beginFill(0);
        _closeSprite.graphics.drawRect((boxw - lineh) / 2, (boxw - linew) / 2, lineh, linew);
        _switchContainer.x = _style.treeOffset.x - (_style.buttonSize-1) / 2;
        _switchContainer.y = _style.treeOffset.y - (_style.buttonSize-1) / 2;
        _labelText.x = cornerX + _style.labelOffset.x;
        _labelText.y = _style.treeOffset.y - _labelText.textHeight / 2 - 2  + _style.labelOffset.y;
        _selectBox.x = _labelText.x;
        _selectBox.y = _labelText.y + 3;
        var ct:ColorTransform = new ColorTransform();
        ct.color = _style.selectedBoxColor;
        ct.alphaMultiplier = _selectBox.alpha;
        _selectBox.transform.colorTransform = ct;
        _limbs.x = cornerX - _style.treeOffset.x;
        _limbs.y = _style.lineSpacing;
    }
    /**自分より下の階層の矩形サイズを取得する*/
    public function getVisibleRect():Rectangle {
        var rect:Rectangle = _itemContainer.getBounds(rootLimb);
        if (_isOpen) for each(var l:TreeLimb in getLimbs()) rect = rect.union(l.getVisibleRect());
        return rect;
    }
    private function getVisibleCount():int {
        var count:int = 1;
        if (!_isDirtyVisibleCount) {
            count = _lastVisibleCount;
        } else {
            if (_isOpen) for each(var l:TreeLimb in getLimbs()) count += l.getVisibleCount();
            _isDirtyVisibleCount = false;
            _lastVisibleCount = count;
        }
        return count;
    }
    /**複製*/
    public function clone(subLimbs:Boolean = true):TreeLimb {
        var newLimb:TreeLimb = new TreeLimb();
        for each(var x:XML in describeType(this).accessor.(@declaredBy.split("::").pop() == "TreeLimb" && @access == "readwrite")) newLimb[x.@name] = this[x.@name];
        newLimb._style = _style;
        if(subLimbs) for each(var l:TreeLimb in getLimbs()) newLimb.addLimb(l.clone());
        return newLimb;
    }
}
class TreeEvent extends Event {
    public var extra:*;
    public var targetLimb:TreeLimb;
    public var bounds:Rectangle;
    static public const CHANGE_SELECT:String = "onChangeSelect";
    static public const CLICK_ITEM:String = "onClickItem";
    static public const WCLICK_ITEM:String = "onWclickItem";
    static public const RESIZE:String = "onResize";
    public function TreeEvent(type:String, target:TreeLimb = null, extra:* = null, bubbles:Boolean = false, cancelable:Boolean = false) {
        this.extra = extra;
        targetLimb = target;
        super(type, bubbles, cancelable);
    }
}
/**
 * このサンプルに使うアイコン画像を生成する汎用性のないクラス
 */
class IconLoader {
    private var loader:Loader;
    private var iconImages:Vector.<BitmapData> = new Vector.<BitmapData>();
    private var iconSize:Rectangle = new Rectangle(0, 0, 13, 13);
    private var iconNum:int = 8;
    private var iconData:String = "iVBORw0KGgoAAAANSUhEUgAAAGgAAAANCAMAAABy+9t6AAAAA3NCSVQICAjb4U/gAAAAe1BMVEX//////5n//wCZ///x5L7/51Dv3a+97ard3d3/zMzq1Zvo0ZJm/wDMzMzly4Tmy4Sg4G3/zACqyu3tvarcuFh+0SyZzAAzzMyrq6u+qXNts+DIo1S+mFKgkXTgfW3/ZpksmdGFhVCffDxie1h7YlhYaHvROyxtVikAAADVecskAAAACXBIWXMAAArwAAAK8AFCrDSYAAAAFnRFWHRDcmVhdGlvbiBUaW1lADA0LzI4LzEwTP2JvQAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNAay06AAAAFWSURBVDiNvdRhV4IwFMbxkRVhMwOGZGqiFTzf/xN2790dd3bqlad29oK/wn4oZzjPA4Cfx7fw10YsJ8fL5TJ9C0zn/XwqXMaiyKPJo8yjtQD6vpdydLydpq1SxEwE7ZWCczyhTsFTo2l4apQlT4225YnE9JFyuCVjpg6TQkSJ47B6cFCnQH1XQJ0Gw2sDdUp0zyXUaTG+tXz5YqFQVYHvWKkQQHEQaEfBd/RC0GoVV8CJoLrW+CRoGDTeCeo6jQ+CxpEv32yEqqrjkaDwyNRTCDv6vzgO550EnUmGQWQYRIZBZBhEhkFMEROhwKvTyvxg8hBIRoJkJEhGgmQkSEaCNjdHg0K4j4/lIv4EWufQ+t8hDxjkAYM8YJAHDPLAz5CX1de6dS7CJ8q23wwpZTFDSsX7VIj3kTStbStnoRSyqPMY8ujyGNPPI0jfDLF/e9fFD66ItNoX84d0b8+P1/QAAAAASUVORK5CYII=";
    private var iconAlpha:uint = 0x66FF00;
    public function IconLoader() {
        loader = new Loader();
        loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, function():void { } );
    }
    public function create(func:Function):void {
        var b64d:Base64Decoder = new Base64Decoder();
        b64d.decode(iconData);
        var ba:ByteArray = b64d.toByteArray();
        loader.contentLoaderInfo.addEventListener(Event.COMPLETE, function():void {
            onDecode.apply();
            func.apply(null, [iconImages]);
        });
        loader.loadBytes(ba);
    }
    private function onDecode():void {
        loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, onDecode);
        var img:BitmapData = Bitmap(loader.content).bitmapData;
        var bmp:BitmapData = new BitmapData(img.width, img.height, true);
        bmp.copyPixels(img, img.rect, new Point());
        bmp.threshold(bmp, bmp.rect, new Point(), "==", 0xFF << 24 | iconAlpha, 0x00000000);//背景色を透明化
        iconImages.length = 0;
        for (var i:int = 0; i < iconNum; i++) {
            iconImages[i] = new BitmapData(iconSize.width, iconSize.height, true);
            iconImages[i].copyPixels(bmp, new Rectangle(iconSize.width * i, 0, iconSize.width, iconSize.height), new Point());
        }
    }
}