/*
 * @(#)RidElement.java
 *
 * Copyright 2001 Nomura Research Institute, LTD. All rights reserved.
 *
 * RID-X : Element Super Class
 *
 * @author  Intelligent Technology Inc.
 * @version 1.01 2001/11/07
 */
package jp.co.nri.rid.doc;

import java.awt.Color;
import java.awt.Font;
import java.awt.Dimension;

import java.awt.font.LineBreakMeasurer;

import java.awt.geom.AffineTransform;

import java.io.File;

import java.util.Hashtable;
import java.util.Enumeration;
import java.util.Vector;
import java.util.Stack;

import javax.swing.JOptionPane;

import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.DOMException;

import jp.co.iti.fagot.ZApp;

import jp.co.iti.fagot.fob.FColor;
import jp.co.iti.fagot.fob.xml.XElementNS;

import jp.co.iti.fagot.geom.ZPoint;
import jp.co.iti.fagot.geom.ZDimension;
import jp.co.iti.fagot.geom.ZRectangle;
import jp.co.iti.fagot.geom.ZPath;

import jp.co.iti.fagot.gob.GElement;
import jp.co.iti.fagot.gob.GOperation;
import jp.co.iti.fagot.gob.GobManager;
import jp.co.iti.fagot.gob.GPath;
import jp.co.iti.fagot.gob.GRectangle;
import jp.co.iti.fagot.gob.GString;
import jp.co.iti.fagot.gob.GSelector;
import jp.co.iti.fagot.gob.PTransform;

import jp.co.iti.fagot.doc.DefaultElement;
import jp.co.iti.fagot.doc.DefaultDocument;
import jp.co.iti.fagot.doc.DocElement;
import jp.co.iti.fagot.doc.DocDocument;
import jp.co.iti.fagot.doc.DocGridControler;
import jp.co.iti.fagot.doc.DocPane;

import jp.co.iti.fagot.util.ZGraphicUtil;
import jp.co.iti.fagot.util.ZInstance;
import jp.co.iti.fagot.util.ZResource;
import jp.co.iti.fagot.util.ZDebug;
import jp.co.iti.fagot.util.ZString;

import jp.co.nri.Rid;
import jp.co.nri.rid.gob.RidGobManager;

import jp.co.nri.rid.error.RidMessage;
import jp.co.nri.rid.error.RidException;

import jp.co.nri.rid.environment.MessageDialog;
/**
 * Rid Element
 */
public class RidElement extends DefaultElement {

	/**
	 * デフォルトコンストラクタ
	 */
	protected RidElement() throws DOMException {
	}

	/**
	 * アトリビュートの取得
	 * @param name アトリビュートキー
	 */
	public String getAttribute(String name) {
	if ( name.equals("Key" ) ) {
		return getAttribute("File");
	}
	return super.getAttribute(name);
	}

	/**
	 * アトリビュートの設定
	 */
	public void setAttribute(String name,String value) {
	if ( name.equals("Key" ) ) {
		setAttribute("File",value);
	} else if ( name.equals("Name" ) ) {
		RidMessage ridMsg = new RidMessage();

		// 空白チェック
//		ZDebug.trace("valuie:"+value);
		if ( value.length()==0 ) {
			ZDebug.err(ridMsg.Error("E1004",value));
			ZInstance.map().put("setAttribute",
				new RidException( ridMsg.Error("E1004",value)));
			return;
		}

		// Page,Zoneユニットの場合
		if ( !getTagName().equals("UrlUnit") ) {
			// ファイル名有効文字チェック
			int nIndex = ZString.findOneOf( value, "\\/:;*?\"<>|#" ) ;
			if ( nIndex >= 0 ) {
				ZDebug.err(ridMsg.Error("E1001",value));
				ZInstance.map().put("setAttribute",
					new RidException( ridMsg.Error("E1001",value)));
				return;
			}

			// 先頭が空白,"."でないことを確認
			if ( value.startsWith(" ") ||
				 value.startsWith(".") ) {
				ZDebug.err(ridMsg.Error("E1005",value));
				ZInstance.map().put("setAttribute",
					new RidException( ridMsg.Error("E1005",value)));
				return;
			}

			// ファイル名の長さが215文字以下であることを確認
			if ( value.length() > 215 ) {
				ZDebug.err(ridMsg.Error("E1006",value));
				ZInstance.map().put("setAttribute",
					new RidException( ridMsg.Error("E1006",value)));
				return;
			}
		}

		// 同一ユニット名チェック(Topゾーンの親=RidProjectもRidElementZone)
		RidElementZone zone = (RidElementZone)getParentNode();
		// 20020520 これまで、ZoneUnitとPageUnitのみチェックしていたが、URLユニットも追加する
		// のでユニットタグのチェックを飛ばす
		// if ( zone != null && getTagName().equals("ZoneUnit") || getTagName().equals("PageUnit") ) {
		if ( zone != null ) {
			NodeList list = zone.getChildNodes();
			for ( int nAt=0 ; nAt<list.getLength() ; nAt++ ) {
				Node child = list.item(nAt);
				if ( child instanceof RidElement && child != this ) {
					if ( ((RidElement)child).getAttribute("Name").equalsIgnoreCase(value) ) {
						// 同名のUnitが存在します
						ZDebug.err(ridMsg.Error("E1000",value));
						ZInstance.map().put("setAttribute",
							new RidException( ridMsg.Error("E1000",value)));
						return;
					}
				}
			}
		}

		// ZoneUnit
		// Fileアトリビュートの設定
		if ( getTagName().equals("ZoneUnit") ) {
			super.setAttribute(name,value);
			updateFileAttribute();

		// PageUnit,ResourceUnit,DocumentUnit
		// File,FileTypeアトリビュートの設定
		} else if ( getTagName().equals("PageUnit")     ||
					getTagName().equals("ResourceUnit") || 
					getTagName().equals("DocumentUnit") ) {
			String[] values = ZString.tokenize( value, "." );
			if ( values.length > 1 ) {
//				super.setAttribute("FileType",values[values.length-1]);
				super.setAttribute("FileType",values[values.length-1].toLowerCase());
//				ZDebug.trace("FileType:"+values[values.length-1]);
			} else {
//				ZDebug.trace("FileTypeなし:"+value);
			}
			super.setAttribute(name,value);
			updateFileAttribute();

		// UrlUnit
		} else {
			String oldValue = getAttribute("Name");

			super.setAttribute(name,value);
			// 対応するリンク情報を更新する
			updateLink( oldValue );
		}
	} else if ( name.equals("File" ) ) {
		// 自動設定項目なので、なにもしない
	} else if ( name.equals("FileType" ) ) {
		// 自動設定項目なので、なにもしない
	} else if ( name.equals("AccessCount" ) ) {
		((RidDocumentSite)getOwnerDocument()).requestUpdateAccessCount();
		super.setAttribute(name,value);
	} else {
		super.setAttribute(name,value);
	}
	}

	/**
	 * アトリビュートの削除
	 * @param name  アトリビュート名
	 */
	public void removeAttribute(String name) {
	if ( name.equals("Name" ) ) {
		RidMessage ridMsg = new RidMessage();
		ZDebug.err(ridMsg.Error("E1004",""));
		ZInstance.map().put("setAttribute",
			new RidException( ridMsg.Error("E1004","")));
		return;
	} else {
		super.removeAttribute(name);
	}
	}

	//## IGobContainer：ヒット ########################################
	/**
	 * 要素に接続するか？
	 * @param gob リクエスト元Gob
	 * @param pt  論理座標
	 * @param obj  接続対象
	 */
	public byte getConnectModeImpl(GElement gob,ZPoint pt,Object obj) {
	// セレクターにヒットするか？
	GSelector sel = new GSelector();
	AffineTransform at = gob.getTransform().getTransform();
	sel = sel.createScaledSelector( 1/at.getScaleX(), 1/at.getScaleY() );
	for (byte nAt=GOperation.ZOOMBASE;nAt<=GOperation.ZOOMMAX;nAt++ ) {
		if ( sel.isHit(((GRectangle)gob).getSelectorPoint(nAt),pt) ) {
			return ( nAt ) ;
		}
	}

	// 塗りつぶしの場合
	if ( gob.getBounds().contains(pt) ) {
/*		つかみやすくするために、近くのセレクターポイントを選択
		return ( GOperation.MOVE ) ;
*/
		GRectangle gRect = (GRectangle)gob;
		byte nNear  = GOperation.ZOOMBASE;
		double dist = ZGraphicUtil.getDistance( gRect.getSelectorPoint(nNear), pt );
		for (byte nAt=(byte)(nNear+1);nAt<=GOperation.ZOOMMAX;nAt++ ) {
			double curDist = ZGraphicUtil.getDistance( gRect.getSelectorPoint(nAt), pt );
			if ( curDist < dist ) {
				nNear = nAt;
				dist  = curDist;
			}
		}
		return nNear;
	}
	return ( GOperation.NONE );
	}

	/**
	 * 接続Gobのハイライト設定
	 * @param szPanelId パネル識別子
	 * @param mode      接続点
	 */
	public void setHighlight(String szPanelId, byte mode) {
	GPath gob = (GPath)getMainGob(szPanelId);
	if ( gob == null ) {
		gob = (GPath)getMainGob(szPanelId+".current");
	}

	// 枠
	GPath gPath = new GPath();
	ZPath pathTrans = new ZPath( gob.getTransform().transform( gob.getPath() ) );
	gPath.setPath(pathTrans);
	gPath.getStyle().setStrokeColor( new FColor( Color.blue ));
	GobManager.list(szPanelId,GobManager.HIGHLIGHT).add(gPath);
	
	GRectangle gRect = new GRectangle();
	gRect.setRect( new ZRectangle(pathTrans.getBounds2D()) );
	if ( gob instanceof GRectangle ) {
		// 矩形=Zoneパネルの場合：周辺に８つのポイントを描画
		
		for (byte nAt=GOperation.ZOOMBASE;nAt<=GOperation.ZOOMMAX;nAt++ ) {
			GSelector  gSel    = new GSelector();
			if ( mode == nAt ) {
				gSel = gSel.createScaledSelector(2,2);
				gSel.getStyle().setFillColor( new FColor( Color.red ));
			} else {
				gSel.getStyle().setFillColor( new FColor( Color.blue ));
			}
			gSel.getStyle().setStrokeColor( new FColor( Color.blue ));

			ZPoint     pt      = gRect.getSelectorPoint(nAt) ;
			ZRectangle selRect = gSel.getRect();
			selRect.setCenter(pt);
			gSel.setRect(selRect);
			GobManager.list(szPanelId,GobManager.HIGHLIGHT).add(gSel);
		}
	} else {
		// Path=Mapパネルの場合は中央のみ
		ZPoint ptCenter = gRect.getRect().getCenter();
		GSelector  gSel    = new GSelector();
		gSel.getStyle().setFillColor( new FColor( (mode==GOperation.MOVE)?Color.red:Color.blue ));
		ZRectangle selRect = gSel.getRect();
		selRect.setCenter(ptCenter);
		gSel.setRect(selRect);
		GobManager.list(szPanelId,GobManager.HIGHLIGHT).add(gSel);
	}
	}

	/**
	 * 接続位置の取得
	 * @param szPanelId パネル識別子
	 * @param byte      接続モード
	 */
	public ZPoint getConnectPoint(String szPanelId, byte mode) {
	GElement gob = getMainGob(szPanelId);
	if ( gob == null ) {
		gob = getMainGob(szPanelId+".current");
	}
	if ( gob == null ) {
		gob = getMainGob(szPanelId+".omission");
	}
	if ( gob == null ) {
		Vector gobs = createAdditionalGobs(szPanelId);
		gob = (GElement)gobs.firstElement();
		GobManager.list(szPanelId,"additional").addAll(gobs);
	}
//	ZDebug.trace("Conn:"+getAttribute("File"));
	return gob.getConnectPointGob(mode);
	}

	/**
	 * gobIdの判定
	 * @param gob     Gob
	 * @param szGobId GobId
	 */
	public boolean isGobId(GElement gob,String szGobId) {
	if ( gob != null && gob.getGobId() != null ) {
		String szId = gob.getGobId();
		if ( szId.equals(szGobId) ) {
			return true;
		} else if ( szGobId.equals("add") && szId.startsWith( szGobId ) ) {
			return true;
		}
	}
	return false;
	}

	//## IGobContainer：移動 ##########################################
	/**
	 * 移動の完了
	 * @param gob  リクエスト元Gob
	 * @param grid グリッドコントローラ
	 */
	public void moveEnd(GElement gob,DocGridControler grid) {
	// 取得した座標系は、ゾーン内座標系
	ZRectangle rect = new ZRectangle(getMovingPath(gob,grid).getBounds2D());
	DocPane editor = grid.getEditor();

	// メインGobの場合
	if ( isGobId(gob,"zone") || isGobId(gob,"map") || isGobId(gob,"new") ) {
		// 親ゾーンエレメントの取得
		ZPoint ptTL = rect.getTopLeft();
		ZPoint ptTrans = grid.transformToLP( ptTL );
		GElement paGob = RidGobManager.findParentZoneGob(editor.getPanelId(),ptTrans/*,this*/);
		RidDocument doc = (RidDocument)editor.getDocument();
		RidElementZone zone = null;
		if ( paGob == null ) {
			zone = (RidElementZone)doc.getRootElement(editor.getPanelId());
		} else {
			zone = (RidElementZone)paGob.getContainer();
		}

		// 座標を一旦論理座標系に変換
		rect.setX( ptTrans.getX() );
		rect.setY( ptTrans.getY() );

		// ゾーンの子供であれば、TopLeftをゾーン内座標に変換
		if ( paGob != null ) {
//			convertRectToParentZone(editor.getPanelId(),paGob,rect);
			convertRectToParentZone(editor,paGob,rect);
		}

		switch ( grid.getMoveMode() ) {
		  case GOperation.NEW:		// 新規作成
			// 追加
//			ZDebug.trace();
			setAttribute("Creator","Operator");
			setSelect(null,true);
			setDocument( doc );
			zone.appendChild( this );
			treeChanged();
			break;

		  default:	// その他移動
			// 親ゾーンが変更された場合は親ゾーンの移動
		  	RidElementZone prevZone = (RidElementZone)getParentNode();
			if ( zone != prevZone ) {
				// 名前のチェック
				if ( getTagName().equals("ZoneUnit") || getTagName().equals("PageUnit") ) {
					NodeList list = zone.getChildNodes();
					for ( int nAt=0 ; nAt<list.getLength() ; nAt++ ) {
						Node child = list.item(nAt);
						if ( child instanceof RidElement ) {
							if ( ((RidElement)child).getAttribute("Name").equalsIgnoreCase(getAttribute("Name")) ) {
								// 同名のUnitが存在します。移動できません
								RidMessage ridMsg = new RidMessage();
								MessageDialog.showError(ridMsg.Error("E1000",getAttribute("Name")));
								return;
							}
						}
					}
				}
				prevZone.removeChild(this);
				zone.appendChild(this);
				treeChanged();
			}
//			ZDebug.trace("new pa Gob :"+paGob+
//						" old pa zone:"+prevZone.getAttribute("Name")+
//						" new pa zone:"+zone.getAttribute("Name"));
			break;
		}

//		ZDebug.trace("SetRect:"+getAttribute("File"));
		setRect( editor.getPanelId(),rect ) ;

	// 他のゾーンへの写像Gobの場合
	} else {
		String szRoot = gob.getGobId().substring(4);
		setRect( editor.getPanelId(),szRoot,rect ) ;
	}
	}

	/**
	 * DOM Tree上での位置変更
	 */
	public void treeChanged() {
	}

	/**
	 * 親ゾーン内の座標へ変換（TopLeftのみ）
	 * @param gob  リクエスト元Gob
	 * @param grid グリッドコントローラ
	 */
	public void convertRectToParentZone(DocPane editor,GElement parent,ZRectangle rect) {
	RidElementZone zone = (RidElementZone)parent.getContainer();

	// 挿入対象ゾーンの親から、現在のルートまで親ゾーンを検索
	Stack stack = new Stack();
	RidDocument doc = (RidDocument)editor.getDocument();
	RidElementZone root = (RidElementZone)doc.getRootElement(editor.getPanelId());
	RidElementZone paZone = (RidElementZone)zone.getParentNode();
	while ( paZone != root ) {
		stack.push( paZone );
		paZone = (RidElementZone)paZone.getParentNode();
	}
	
	// 親ゾーンから、現在の親まで座標変換
	while ( stack.size()>0 ) {
		paZone = (RidElementZone)stack.pop();
		PTransform trans = paZone.getChildTransform(editor.getPanelId());

		ZPoint ptTL = rect.getTopLeft();
		ZPoint ptTrans = trans.inverseTransform(ptTL);
		rect.setX( ptTrans.getX() );
		rect.setY( ptTrans.getY() );
	}
	
	// 最後に直接の親変換
	PTransform trans = zone.getChildTransform(editor.getPanelId());
	if ( trans == null ) {
		// 初めての子供の場合は、ZoneのCenterからのオフセット
		ZPoint zoneCenter = parent.getBounds().getCenter();
		double dx = (4000*Rid.PANELSIZE) + ( rect.getX()-zoneCenter.getX() );
		double dy = (3000*Rid.PANELSIZE) + ( rect.getY()-zoneCenter.getY() );
		rect.setX( dx );
		rect.setY( dy );
	} else {
		ZPoint ptTL = rect.getTopLeft();
		ZPoint ptTrans = trans.inverseTransform(ptTL);
		rect.setX( ptTrans.getX() );
		rect.setY( ptTrans.getY() );
	}
	}

	//## 選択 #########################################################
	/**
	 * 選択の変更
	 * @param gob  リクエスト元Gob
	 * @param bSelect 選択状態
	 */
	public boolean setSelect( GElement gob,boolean bSelect ) {
	// 祖先が選択されていれば、選択しない
	if ( bSelect ) {
		if ( isAncestorSelect() ) {
			bSelect = false;
		}
	}
	bSelect = super.setSelect( gob,bSelect ) ;
	// 選択状態であれば、子孫を未選択状態に設定
	if ( bSelect ) {
		setChildUnSelect();
	}

	return bSelect;
	}

	/**
	 * 祖先が選択されているか？
	 */
	public boolean isAncestorSelect() {
	Node parent = getParentNode();
	if ( parent != null && parent instanceof RidElement ) {
		if ( ((RidElement)parent).isSelect() ) {
			return true;
		}
		return ((RidElement)parent).isAncestorSelect();
	}
	return false;
	}

	/**
	 * 子孫を未選択状態に設定
	 */
	public void setChildUnSelect() {
	// 子ノードの描画
	for ( int nIndex=0; nIndex<getLength() ; nIndex++ ) {
		Node child = (Node)item(nIndex);
		if ( child instanceof RidElement ) {
			((RidElement)child).setSelect( null,false );
			((RidElement)child).setChildUnSelect();
		}
	}
	}

	//## プロパティ ####################################################
	/**
	 * Fileアトリビュートの更新
	 */
	protected void updateFileAttribute() {
	RidElementZone top        = ((RidDocument)getOwnerDocument()).getTopZone();
	// TopZoneの場合はFileアトリビュートを更新する
  	if ( this == top ) {
		super.setAttribute("File",getAttribute("Name"));

	// その他の場合
	} else if (
		getTagName().equals("ZoneUnit")     ||
		getTagName().equals("PageUnit")     ||
		getTagName().equals("ResourceUnit") || 
		getTagName().equals("DocumentUnit") ) {
		
		String oldValue = getAttribute("File");
		
		RidElementZone parent = (RidElementZone)getParentNode();
		// 名前を変更して確定しないうちにノードが{cut|undo}で削除された場合、
		// プロパティパネルから名前変更のイベントが発生するので親がnullの場合がある
		if ( parent == null ) {
			ZDebug.trace("null:"+getAttribute("Name"));
			return ;
		}
		// 親がトップゾーンならばNameアトリビュートをそのままセット
	  	if ( parent == top ) {
			super.setAttribute("File",getAttribute("Name"));
		} else {
			String szParent = parent.getAttribute("File");
			super.setAttribute("File",szParent+"/"+getAttribute("Name"));
		}

		// 子供のアトリビュートも更新
		for ( int nIndex=0; nIndex<getLength() ; nIndex++ ) {
			Node child = (Node)item(nIndex);
			if ( child instanceof RidElement ) {
				((RidElement)child).updateFileAttribute();
			}
		}

		// 対応するゾーンにおけるAdditional位置情報の更新
	  	if ( getTagName().equals("ZoneUnit") ) {
			updatePosition(oldValue);
		}

		// 対応するリンクの更新
	  	if ( getTagName().equals("PageUnit") ) {
			updateLink(oldValue);
			updateScenarioItem(oldValue);
		}
	}
	}

	/**
	 * 名前が更新された時に、リンクの更新
	 */
	protected void updatePosition(String oldValue) {
	// すべてのPage,UrlUnitを取得
	RidElementZone top = ((RidDocument)getOwnerDocument()).getTopZone();
	Vector allUnits = top.getPages(true);
	allUnits.addAll( top.findChildElements(".UrlUnit") );

	String szFile = getAttribute("File");
	Enumeration elm = allUnits.elements();
	while ( elm.hasMoreElements() ) {
		RidElement unit = (RidElement)elm.nextElement();
		XElementNS vw = unit.findChildElement(".ViewInfo.Zone.Position","On="+oldValue);
		if ( vw != null ) {
			vw.setAttribute("On",szFile);
		}
	}
	}

	/**
	 * 名前が更新された時に、リンクの更新
	 */
	protected void updateLink(String oldValue) {
//	ZDebug.trace("updateLink:"+oldValue+"→"+getAttribute("Key"));
	DocElement link = (DocElement)((DocElement)getOwnerDocument().getDocumentElement()).findChildElement(".Link",null);
	if ( link != null ) {
		Vector links = link.findChildElements("LinkUnit");
		Enumeration elm = links.elements() ;
		while ( elm.hasMoreElements() ) {
			RidElementLink linkUnit = (RidElementLink)elm.nextElement();
			linkUnit.updateKeyAttribute(oldValue,this);
		}
	}
	}

	/**
	 * 名前が更新された時に、シナリオアイテムの更新
	 */
	protected void updateScenarioItem(String oldValue) {
	DocElement scene = (DocElement)((DocElement)getOwnerDocument().getDocumentElement()).findChildElement(".Scenario",null);
	if ( scene != null ) {
		Vector scenes = scene.findChildElements("ScenarioUnit");
		Enumeration elm = scenes.elements() ;
		while ( elm.hasMoreElements() ) {
			DocElement scenario = (DocElement)elm.nextElement();
			Enumeration elmitem = scenario.findChildElements("ScenarioItem").elements();
			while ( elmitem.hasMoreElements() ) {
				RidElementScenarioItem item = (RidElementScenarioItem)elmitem.nextElement();
				item.updateKeyAttribute(oldValue,this);
			}
		}
	}
	}

	/**
	 * ゾーン名の取得
	 */
	protected String getZone() {
	DocElement parent = (DocElement)getParentNode();
	return parent.getAttribute("File");
	}

	//## 描画 #########################################################
	/**
	 * 描画エレメントの取得の前処理
	 * @param szPanelId パネル識別子
	 */
	public void preCreateGobs(String szPanelId) {
	setMainGob( szPanelId, null ) ;
	setMainGob( szPanelId+".current", null ) ;
	setMainGob( szPanelId+".omission", null ) ;
	super.preCreateGobs( szPanelId ) ;
	}

	/**
	 * 描画(影)の作成
	 * @param gPath オリジナルエレメント
	 */
	public Vector createShadowGobs(GPath gPath) {
	Vector gobs = new Vector();
	// 影色の設定
	ZResource res = ZApp.getTheApp().getResource();
	Color col = ZGraphicUtil.stringToColor(res.getString("unit.color.shadow"));

	// 影座標の設定
	ZPath path   = gPath.getPath();
	AffineTransform at = new AffineTransform(1,0,0,1,5,5);
	ZPath pathTrans = new ZPath(at.createTransformedShape(path));

	GPath gShadow=new GPath();
	gShadow.setPath( pathTrans );
	gShadow.getStyle().setFillColor( new FColor( col ));

	gobs.add(gShadow);
	return gobs;
	}

	/**
	 * 描画(影)の作成
	 * @param szPanelId パネル識別子
	 */
	public Vector createAdditionalGobs(String szPanelId) {
	Vector gobs = new Vector();

	// 座標識別子の変換
	DocDocument doc  = (DocDocument)getOwnerDocument();
	DocElement  root = (DocElement)doc.getRootElement( szPanelId ) ;
//	String szRoot = ZString.replace(root.getAttribute("File"),"/",".");
//	szRoot = ZString.replace(szRoot," ","__");
	String szRoot = root.getAttribute("File");
	// 影色の設定
	Color col = getBackgroundColor();

	// 影座標の設定
	ZRectangle rect = null ;
	if ( hasRect(szPanelId,szRoot) ) {
		rect = getRect( szPanelId,szRoot );
	} else {
		ZPoint pt = getSideSpace(szPanelId,szRoot);
		rect = new ZRectangle(pt.getX(),pt.getY(),20,20);
		setRect(szPanelId,szRoot,rect);
	}

	GRectangle gRect = new GRectangle();
	// 矩形の設定
	gRect.setRect( rect );
	// 背景色の設定
	gRect.getStyle().setFillColor( new FColor( col ));
	gRect.getStyle().setStrokeColor( new FColor( Color.black ));
	gRect.setSelect(isSelect());
	gRect.setEditOption(GRectangle.NORESIZE);
	gRect.setContainer(this);
	gRect.setGobId("add."+szRoot); // szRoot が "zone","map"と重ならないよう"add"をつける
	gobs.add(gRect);
	setMainGob(szPanelId+".current",gRect);

	// タイトル文字の表示
	double dTitleHeight = getTitleHeight();
	Vector children = new Vector();

	ZResource res = ZApp.getTheApp().getResource();
	String szTextType = res.getString("viewtext.Zone."+getTagName());
	String szText = getAttribute(szTextType);
//	String szText = getAttribute("Key");
	if ( szText.length() > 0 ) {
		ZRectangle rttxt =
			new ZRectangle(rect.getX()+LINE+TTLIN,rect.getY()+rect.getHeight()+LINE+TTLIN,
						   1000,dTitleHeight);
//		ZDebug.trace("str:"+szText+",,,"+rttxt);
		GString str = new GString();
		str.setText( szText );
		str.getStyle().setFillColor( new FColor( Color.black ));
		str.setFormat( GString.SINGLE|GString.LEFT|GString.MIDDLE );
		str.setRect( rttxt );
		children.add(str);
		gRect.addForeChildren(children);
	}

	return gobs;
	}

	/**
	 * タイトルの作成
	 * @param szPanelId パネル識別子
	 */
	public Vector createTitleGobs(String szPanelId) {
	Vector gobs = new Vector();

	ZRectangle rt = getRect(szPanelId);

	// タイトル矩形
	double dTitleHeight = getTitleHeight();
	GRectangle grect=new GRectangle();
	ZRectangle rtttl=new ZRectangle(rt.getX(),rt.getY(),
									rt.getWidth(),dTitleHeight+TTLIN*2);
	grect.setRect( rtttl );
	// 背景色は、親ゾーンの背景色を指定
	Color bgcol = ((RidElement)getParentNode()).getBackgroundColor();
	grect.getStyle().setFillColor( new FColor( bgcol ));

	// 枠色の設定
	ZResource res = ZApp.getTheApp().getResource();
	boolean isExist = getBooleanAttr("Exist", true );
	Color col = res.getColor("unit.color."+((isExist)?"":"Not")+"Exist");
	grect.getStyle().setStrokeColor( new FColor( col ));
	gobs.add(grect);

	// タイトル文字の表示
	String szTextType = res.getString("viewtext.Zone."+getTagName());
	String szText = getAttribute(szTextType);
	if ( szText.length() > 0 && rtttl.getWidth()>=30 ) {
/*
		ZRectangle rttxt =
			new ZRectangle(rt.getX()+LINE+TTLIN,rt.getY()+LINE+TTLIN,
						   rt.getWidth()-(LINE+TTLIN)*2,dTitleHeight);
		if ( getTagName().equals("ZoneUnit") ) {
			// ゾーンの場合はシュリンクボタンがあるので少し短め
			rttxt =
			new ZRectangle(rt.getX()+LINE+TTLIN,rt.getY()+LINE+TTLIN,
						   rt.getWidth()-(LINE+TTLIN)*4-dTitleHeight,dTitleHeight);
		}
*/		
		ZRectangle rttxt = getTagName().equals("ZoneUnit") ?
			new ZRectangle(rt.getX()+LINE+TTLIN,rt.getY()+LINE+TTLIN,	// ゾーンの場合はシュリンクボタンがあるので少し短め
						   rt.getWidth()-(LINE+TTLIN)*4-dTitleHeight,dTitleHeight) :
			new ZRectangle(rt.getX()+LINE+TTLIN,rt.getY()+LINE+TTLIN,
						   rt.getWidth()-(LINE+TTLIN)*2,dTitleHeight);
		
		GString str = new GString();
		str.setText( szText );
		Color txtcol = (ZGraphicUtil.isBlackFamily(bgcol)?
						Color.white:Color.black);
		str.getStyle().setFillColor( new FColor( txtcol ));
		str.setFormat( GString.SINGLE|GString.LEFT|GString.MIDDLE );
		str.setRect( rttxt );
		gobs.add(str);
	}
	return gobs;
	}

	/**
	 * 背景色の取得
	 */
	public Color getBackgroundColor() {
	Color col = getColorAttr("PaintColor");
	if ( col == null ) {
		col = ZApp.getTheApp().getResource().getColor("unit.color."+getTagName());
	}
	return col;
	}

	//## メインGob ####################################################
	/**
	 * メインGobの設定
	 * @param szPanelId パネル識別子
	 * @param gob       メインGob
	 */
	protected void setMainGob(String szPanelId,GElement gob) {
	if ( gob != null ) {
//		ZDebug.trace("setMain:"+szPanelId+":"+gob.toString());
//		ZDebug.trace(getTagName()+"."+getAttribute("Name")+":"+szPanelId);
		mMainGobs.put( szPanelId, gob );
	} else {
		mMainGobs.remove( szPanelId );
	}
	}

	/**
	 * 省略時の親Gobの設定
	 * @param szPanelId パネル識別子
	 * @param gob       親Gob
	 */
	protected void setOmissionGob(String szPanelId,GElement gob) {
	if ( gob != null ) {
		mMainGobs.put( szPanelId+".omission", gob );
	} else {
		mMainGobs.remove( szPanelId+".omission" );
	}
	// 子ノードにも通知
	for ( int nIndex=0; nIndex<getLength() ; nIndex++ ) {
		Node elm = (Node)item(nIndex);
		if ( elm instanceof RidElement ) {
			((RidElement)elm).setOmissionGob( szPanelId,gob );
		}
	}
	}

	/**
	 * メインGobの取得
	 * @param szPanelId パネル識別子
	 */
	protected GElement getMainGob(String szPanelId) {
//	ZDebug.trace(getTagName()+"."+getAttribute("Name")+":"+szPanelId+"..."+mMainGobs.get( szPanelId ));
	return (GElement)mMainGobs.get( szPanelId );
	}

	//## ゾーンパネルの自動配置 #######################################
	/**
	 * インポート時に追加されたUnitの自動レイアウト
	 * @param szPanelId パネル識別子
	 */
	public ZPoint getSpace(String szPanelId) {
	boolean bUpper=getTagName().equals("UrlUnit");
	// 定数
	int    nCnt   =  20;
	double width  = 150;
	double height = 150;

	RidElementZone zone = (RidElementZone)getParentNode();
	ZPoint ptStart = new ZPoint((4000*Rid.PANELSIZE)-(nCnt/2*width),(bUpper)?(3000*Rid.PANELSIZE)-height:(3000*Rid.PANELSIZE));
	boolean[] bExist = new boolean[nCnt];
	// 位置存在フラグの初期化
	int nAt = 0;
	for ( nAt=0 ; nAt<nCnt ; nAt++ ) {
		bExist[nAt] = false;
	}

//	ZDebug.trace("getSpaceStart:"+getAttribute("Key")+":upper:"+bUpper);

	for ( int nIndex=0; nIndex<zone.getLength() ; nIndex++ ) {
		Node child = (Node)zone.item(nIndex);
		if ( child instanceof DocElement && ((DocElement)child).hasRect(szPanelId) ) {
			ZRectangle rtChild = ((DocElement)child).getRect(szPanelId);
			boolean bCheck = false;
			// 現在の存在チェック領域よりも上下の場合は、存在チェック領域を上下にずらす
			if ( bUpper ) {
				if ( rtChild.getY() < ptStart.getY() ) {
					int nBai = (int)(((3000*Rid.PANELSIZE)-rtChild.getY())/height);
//	ZDebug.trace("ずらす:"+((DocElement)child).getAttribute("Name")+":rt:"+rtChild+":oldSta:"+ptStart+":nBai:"+nBai);
					ptStart.setY( (3000*Rid.PANELSIZE) - nBai*height );
//	ZDebug.trace("      :→newSta"+ptStart);
					for ( nAt=0 ; nAt<nCnt ; nAt++ ) {
						bExist[nAt] = false;
					}
					bCheck = true;
				} else if ( rtChild.getY() < (ptStart.getY()+height) ) {
					bCheck = true;
				}
			} else {
				if ( (rtChild.getY()+rtChild.getHeight()) > (ptStart.getY()+height) ) {
					int nBai = (int)((rtChild.getY()+rtChild.getHeight()-(3000*Rid.PANELSIZE))/height);
//	ZDebug.trace("ずらす:"+((DocElement)child).getAttribute("Name")+":rt:"+rtChild+":oldSta:"+ptStart+":nBai:"+nBai);
					ptStart.setY( (3000*Rid.PANELSIZE) + nBai*height );
//	ZDebug.trace("      :→newSta"+ptStart);
					for ( nAt=0 ; nAt<nCnt ; nAt++ ) {
						bExist[nAt] = false;
					}
					bCheck = true;
				} else if ( (rtChild.getY()+rtChild.getHeight()) > ptStart.getY() ) {
					bCheck = true;
				}
			}
			// 存在チェック領域の使っている部分をチェックする
			if ( bCheck ) {
				int nStart = Math.min(nCnt,Math.max(0     ,(int)((rtChild.getX()-ptStart.getX())/width)));
				int nEnd   = Math.max(-1  ,Math.min(nCnt-1,(int)((rtChild.getX()+rtChild.getWidth()-ptStart.getX())/width)));
//	ZDebug.trace("チェック:"+((DocElement)child).getAttribute("Name")+":pt:"+ptStart+":rt:"+rtChild+",nStart"+nStart+",nEnd"+nEnd);
				for ( nAt=nStart; nAt<=nEnd ; nAt++ ) {
					bExist[nAt] = true;
				}
			}
		}
	}
	for ( nAt=0 ; nAt<nCnt ; nAt++ ) {
		if ( !bExist[nAt] ) {
			break;
		}
	}
	// 全ての領域が使われていれば
	if ( nAt == nCnt ) {
//	ZDebug.trace("全て使用:oldSta"+ptStart);
		nAt = 0;
		ptStart.setY( ptStart.getY()+((bUpper)?(-height):height) ) ;
//	ZDebug.trace("        :newSta"+ptStart);
	}
	
	ptStart.setX( ptStart.getX()+nAt*width );
//	ZDebug.trace(getAttribute("Key")+"nAt:"+nAt+" pt:"+ptStart.toString());

	// オーバフローチェック
	Dimension dim = ((DocDocument)getOwnerDocument()).getLogicalSize();
	if ( ptStart.getY() < 0 ) {
		ptStart.setY(0);
		if ( !mOverFlow[0] ) {
			ZDebug.err("URL Overflow");
			RidMessage ridMsg = new RidMessage();
			MessageDialog.showError(ridMsg.Error("W1000","URL"));
		}
		mOverFlow[0] = true;
	} else if ( ptStart.getY() > dim.getHeight()-height ) {
		ptStart.setY(dim.getHeight()-height);
		if ( !mOverFlow[1] ) {
			ZDebug.err("Zone/Page Overflow");
			RidMessage ridMsg = new RidMessage();
			MessageDialog.showError(ridMsg.Error("W1000","Zone/Page"));
		}
		mOverFlow[1] = true;
	}
	return ptStart;
	}

	/**
	 * ゾーン内リンク影の自動レイアウト
	 * @param szPanelId パネル識別子
	 * @param szRoot    ルートパネル識別子
	 */
	public ZPoint getSideSpace(String szPanelId,String szRoot) {
	boolean bLeft=getTagName().equals("UrlUnit");
	// 定数
	int    nCnt   =  20;
	double width  = 150;
	double height =  50;

	// この階層での最初のgetSideSpace()の場合、PageUnit,UrlUnitを取得し、
	// 主たるページ、ゾーンの矩形を取得する
	if ( mAllUnits == null ) {
		// すべてのPage,UrlUnitを取得
		RidElementZone top = ((RidDocumentSite)getOwnerDocument()).getTopZone();
		mAllUnits = top.getPages(true);
		mAllUnits.addAll( top.findChildElements(".UrlUnit") );

		mRootRt = null;
		DocDocument doc  = (DocDocument)getOwnerDocument();
		DocElement  root = (DocElement)doc.getRootElement( szPanelId ) ;
		for ( int nIndex=0; nIndex<root.getLength() ; nIndex++ ) {
			Node child = (Node)root.item(nIndex);
			if ( child instanceof DocElement && ((DocElement)child).hasRect(szPanelId) ) {
				ZRectangle rtChild = ((DocElement)child).getRect(szPanelId);
				if ( mRootRt == null ) {
					mRootRt = rtChild;
				} else {
					mRootRt.union( rtChild );
				}
			}
		}

		// 左右に少し隙間をあける
		mRootRt.setX(     mRootRt.getX()-width/2 );
		mRootRt.setWidth( mRootRt.getWidth()+width );
	}

	ZPoint ptStart = new ZPoint((bLeft)?(mRootRt.getX()-width):(mRootRt.getX()+mRootRt.getWidth()),mRootRt.getY());

	// 位置存在フラグの初期化
	boolean[] bExist = new boolean[nCnt];
	int nAt = 0;
	for ( nAt=0 ; nAt<nCnt ; nAt++ ) {
		bExist[nAt] = false;
	}

	Enumeration elm = mAllUnits.elements();
	while( elm.hasMoreElements() ) {
		RidElement unit = (RidElement)elm.nextElement();
		if ( unit.hasRect(szPanelId,szRoot) ) {
			ZRectangle rtChild = unit.getRect(szPanelId,szRoot);
//			ZDebug.trace("hasRect:"+unit.getAttribute("Key"));
			boolean bCheck = false;
			// 現在の存在チェック領域よりも左右の場合は、存在チェック領域を上下にずらす
			if ( bLeft ) {
				if ( rtChild.getX() < ptStart.getX() ) {
					int nBai = (int)((mRootRt.getX()-rtChild.getX())/width);
//					ZDebug.trace("rtChild:"+rtChild+"  getX:"+ptStart+"  nBai:"+nBai);
					ptStart.setX( mRootRt.getX() - (nBai+1)*width );
					for ( nAt=0 ; nAt<nCnt ; nAt++ ) {
						bExist[nAt] = false;
					}
					bCheck = true;
				} else if ( rtChild.getX() < (ptStart.getX()+width) ) {
					bCheck = true;
				}
			} else {
				if ( (rtChild.getX()+rtChild.getWidth()) > (ptStart.getX()+width) ) {
					int nBai = (int)((rtChild.getX()+rtChild.getWidth()-(mRootRt.getX()+mRootRt.getWidth()))/width);
					ptStart.setX( mRootRt.getX()+mRootRt.getWidth() + nBai*width );
					for ( nAt=0 ; nAt<nCnt ; nAt++ ) {
						bExist[nAt] = false;
					}
					bCheck = true;
				} else if ( (rtChild.getX()+rtChild.getWidth()) > ptStart.getX() ) {
					bCheck = true;
				}
			}
			// 存在チェック領域の使っている部分をチェックする
			if ( bCheck ) {
				int nStart = Math.min(nCnt,Math.max(0   ,(int)((rtChild.getY()-ptStart.getY())/height)));
				int nEnd   = Math.max(-1  ,Math.min(nCnt,(int)((rtChild.getY()+rtChild.getHeight()-ptStart.getY())/height)));
//				if ( bUpper) ZDebug.trace("Rect:"+rtChild+",nStart"+nStart+",nEnd"+nEnd);
				for ( nAt=nStart; nAt<=nEnd ; nAt++ ) {
					bExist[nAt] = true;
				}
			}
		}
	}
	for ( nAt=0 ; nAt<nCnt ; nAt++ ) {
		if ( !bExist[nAt] ) {
			break;
		}
	}
	// 全ての領域が使われていれば
	if ( nAt == nCnt ) {
		nAt = 0;
		ptStart.setX( ptStart.getX()+((bLeft)?(-width):width) ) ;
	}
	
	ptStart.setY( ptStart.getY()+nAt*height );
//	if ( bUpper ) ZDebug.trace(getAttribute("File")+"nAt:"+nAt+" pt:"+ptStart.toString());

	// オーバフローチェック
	Dimension dim = ((DocDocument)getOwnerDocument()).getLogicalSize();
	if ( ptStart.getX() < 0 ) {
		ptStart.setX(0);
		if ( !mOverFlow[3] ) {
			ZDebug.err("URL Overflow");
			RidMessage ridMsg = new RidMessage();
			MessageDialog.showError(ridMsg.Error("W1000","URL"));
		}
		mOverFlow[3] = true;
	} else if ( ptStart.getX() > dim.getWidth()-width ) {
		ptStart.setX(dim.getWidth()-width);
		if ( !mOverFlow[2] ) {
			ZDebug.err("Page Overflow");
			RidMessage ridMsg = new RidMessage();
			MessageDialog.showError(ridMsg.Error("W1000","Page"));
		}
		mOverFlow[2] = true;
	}
	return ptStart;
	}

	//## static:タイトル ##############################################
	/**
	 * タイトル高さの取得
	 */
	static protected double getTitleHeight() {
	if ( TTL == 0 ) {
		ZDimension dim = ZApp.getTextPreferredSize("Hjgy",null,0);
		TTL = dim.getHeight();
	}
	return TTL;
	}

	/**
	 * 文字列に適当なサイズの取得
	 */
	static public ZDimension getTextPreferredSize(String szFont, int nSize, String szText,AffineTransform at,double dWidth) {
	ZResource res = ZApp.getTheApp().getResource();
	if ( szFont == null || szFont.length()==0 ) {
		szFont = res.getString("app.font.name");
	}
	if ( nSize == 0 ) {
		nSize = res.getInt("app.font.size",10);
	}

	Font font = new Font(szFont,0,nSize);
	
	if ( at == null ) {
		at = new AffineTransform(1,0,0,1,0,0);
	}
//	LineBreakMeasurer lbm = ZApp.createLineBreakMeasurer( szText,font,null,null,at);
//	return ZGraphicUtil.getTextPreferredSize(lbm,at,szText.length(),dWidth);
	LineBreakMeasurer lbm = ZApp.createLineBreakMeasurer( szText,font,null,null,null);
	return ZGraphicUtil.getTextPreferredSize(lbm,null,szText.length(),dWidth);
	}

	//## 基本操作:Rect #################################################
	/**
	 * 矩形を保持しているか？
	 * @param szPanel パネル識別子
	 * @param szRoot  ルートゾーン
	 */
	public boolean hasRect(String szPanel, String szRoot) {
	XElementNS elm = findChildElement(".ViewInfo."+szPanel+".Position","On="+szRoot);
	if ( elm != null ) {
		return ( elm.hasAttribute("X") &&
				 elm.hasAttribute("Y") &&
				 elm.hasAttribute("Width") &&
				 elm.hasAttribute("Height") ) ;
	}
	return false;
	}

	/**
	 * 矩形の設定
	 * @param szPanel パネル識別子
	 * @param szRoot  ルートゾーン
	 * @param rect  矩形
	 */
	public void setRect(String szPanel, String szRoot, ZRectangle rect) {
//	ZDebug.trace();
	XElementNS elm = getChildElement(".ViewInfo."+szPanel+".Position","On="+szRoot);
//	ZDebug.trace("getElement:On:"+elm.getAttribute("On"));
//	elm.setAttribute("On"		, szRoot);
	elm.setDoubleAttr("X"		, rect.getX());
	elm.setDoubleAttr("Y"		, rect.getY());
	elm.setDoubleAttr("Width"	, rect.getWidth());
	elm.setDoubleAttr("Height"	, rect.getHeight());
	}

	/**
	 * 矩形の取得
	 * @param szPanel パネル識別子
	 * @param szRoot  ルートゾーン
	 */
	public ZRectangle getRect(String szPanel, String szRoot) {
	XElementNS elm = findChildElement(".ViewInfo."+szPanel+".Position","On="+szRoot);
	if ( elm == null ) {
		return new ZRectangle();
	}
	double dX      = elm.getDoubleAttr("X",0);
	double dY      = elm.getDoubleAttr("Y",0);
	double dWidth  = elm.getDoubleAttr("Width",0);
	double dHeight = elm.getDoubleAttr("Height",0);
	return new ZRectangle( dX, dY, dWidth, dHeight );
	}

	//## アトリビュート################################################
	/**
	 * タイトル用の矩形の高さ
	 */
	static public double TTL = 0;

	/**
	 * タイトル用矩形のインセット
	 */
	final static public double TTLIN = 2;

	/**
	 * 線の幅
	 */
	final static public double LINE  =  1;

	/**
	 * パネル毎のメインGobを保存する
	 */
	protected Hashtable mMainGobs = new Hashtable();

	/**
	 * getSideSpace用全ユニット
	 */
	static public Vector mAllUnits = null;

	/**
	 * getSideSpace用全ユニット格納領域
	 */
	static protected ZRectangle mRootRt = null;

	/**
	 * 表示領域オーバフローフラグ
	 *	0:上
	 *	1:下
	 *	2:右
	 *	3:左
	 */
	static protected boolean[] mOverFlow = null;
	{
		mOverFlow = new boolean[4];
		for ( int nIndex=0 ; nIndex<4 ; nIndex++ ) {
			mOverFlow[nIndex] = false;
		}
	}
}
