// EditLayer.tjs - 一行編集用コントロールレイヤ
// Copyright (C)2001-2006, W.Dee and contributors  改変?配布は自由です

/*
単一行の編集コントロールとして動作するレイヤです。

書式  new EditLayer(<window>, <parent>)
*/

class EditLayer extends KAGLayer
{
	var Edit_text = ""; // 編集中のテキスト
	var Edit_selStart = 0; // カーソル位置
	var Edit_selLength = 0; // 選択長さ
	var Edit_scrollPos = 0; // 表示されている左端の文字位置
	var Edit_caretLayer; // キャレット用レイヤ
	var Edit_color = clWindow; // 背景色
	var Edit_antialiased = true; // アンチエイリアスをかけるか
	var Edit_opacity = 192; // 不透明度
	var Edit_maxChars = 0; // 最大文字数 ( 0 = 無制限 )
	var Edit_textColor = clWindowText;
	var Edit_blinkTimer;
	var Edit_vertical = false;
    var Edit_noborder = false;
    
	function EditLayer(win, par, vert = false)
	{
		super.KAGLayer(win, par);

		Edit_vertical = vert;

		hitType = htMask;
		hitThreshold = 0; // 全域でマウスイベントは不透明
		focusable = true; // フォーカスは受け取る
		useAttention = true; // 注視ポイントを使用する

		Edit_caretLayer = new global.Layer(win, this);
		Edit_caretLayer.hitType = htMask;
		Edit_caretLayer.hitThreshold = 256; // 完全にマウスイベントは透明
		resizeCaret();

		imeMode = imDontCare; // IME 使用可

		// 点滅用タイマを用意
		Edit_blinkTimer = new Timer(onBlink, '');

		cursor = vert ? crHBeam : crIBeam;

		update();
	}

	function finalize()
	{
		invalidate Edit_caretLayer;
		invalidate Edit_blinkTimer;
		super.finalize(...);
	}

	function assign(src)
	{
		assignImages(src, true);
		Edit_text = src.Edit_text;
		Edit_selStart = src.Edit_selStart;
		Edit_selLength = src.Edit_selLength;
		Edit_scrollPos = src.Edit_scrollPos;
		Edit_color = src.Edit_color;
		Edit_textColor = src.Edit_textColor;
		Edit_antialiased = src.Edit_antialiased;
		Edit_opacity = src.Edit_opacity;
		Edit_vertical = src.Edit_vertical;
		Edit_maxChars = src.Edit_maxChars;
		Edit_noborder = src.Edit_noborder;
        var f = font;
		var sf = src.font;
		f.face = sf.face;
		f.angle = sf.angle;
		f.bold = sf.bold;
		f.italic = sf.italic;
		f.height = sf.height;
		cursor = src.cursor;
		resizeCaret();
		update();
	}

	function resizeCaret()
	{
		// キャレットのサイズの初期化
		if(Edit_vertical)
		{
			var h = 2; // 2 固定
			var w = font.height;
			w = -w if(w<0);
			Edit_caretLayer.setImageSize(w, h);
			Edit_caretLayer.setSizeToImageSize();
			Edit_caretLayer.colorRect(0, 0, w, h, Edit_textColor);
		}
		else
		{
			var w = 2; // 2 固定
			var h = font.height;
			h = -h if(h<0);
			Edit_caretLayer.setImageSize(w, h);
			Edit_caretLayer.setSizeToImageSize();
			Edit_caretLayer.colorRect(0, 0, w, h, Edit_textColor);
		}
	}

	function getCaretXPos()
	{
		// キャレットの位置を取得
		var bch = Edit_text.substring(0, Edit_selStart); // キャレット以前の文字列
		var bchw = font.getTextWidth(bch);
		var sch = Edit_text.substring(0, Edit_scrollPos); // 表示以前の文字
		var schw = font.getTextWidth(sch);
		return bchw - schw + 3;
	}

	function ensureCaretVisible(step = 1)
	{
		// キャレットが確実に表示される位置までスクロールする
		var sp_save = Edit_scrollPos;
		var txtlen = Edit_text.length;
		if(Edit_selStart == txtlen)
			while(Edit_scrollPos >= 0 && getCaretXPos() <= 3) Edit_scrollPos-=step;
		else
			while(Edit_scrollPos >= 0 && getCaretXPos() < 3) Edit_scrollPos-=step;
		var lim = Edit_vertical ? imageHeight : imageWidth;
		while(Edit_scrollPos < txtlen && getCaretXPos() >= lim -3) Edit_scrollPos+=step;
		if(Edit_scrollPos < 0) Edit_scrollPos = 0;
		if(Edit_scrollPos > txtlen) Edit_scrollPos = txtlen;
		if(sp_save != Edit_scrollPos) update();
	}

	function setCaretLayerPos()
	{
		resizeCaret();
		var xpos = getCaretXPos() -1;
		if(Edit_vertical)
		{
			Edit_caretLayer.top = xpos;
			Edit_caretLayer.left = imageWidth - 3 - Edit_caretLayer.imageWidth;
			setAttentionPos(imageWidth - 3 , Edit_caretLayer.top);
		}
		else
		{
			Edit_caretLayer.top = 3;
			Edit_caretLayer.left = xpos;
			setAttentionPos(xpos, 3);
		}
	}

	function showCaret()
	{
		setCaretLayerPos();
		Edit_caretLayer.visible = true;
		Edit_blinkTimer.interval = 500;
		Edit_blinkTimer.enabled = true;
	}

	function hideCaret()
	{
		Edit_blinkTimer.enabled = false;
		Edit_caretLayer.visible = false;
	}

	function onBlink()
	{
		Edit_caretLayer.visible = ! Edit_caretLayer.visible;
	}

	function stepLeft()
	{
		// キャレットを左へ
		if(Edit_selLength) Edit_selLength = 0, update();
		if(Edit_selStart > 0) Edit_selStart --, ensureCaretVisible(3);
		showCaret();
	}

	function stepRight()
	{
		// キャレットを右へ
		if(Edit_selLength) Edit_selLength = 0, update();
		if(Edit_selStart < Edit_text.length) Edit_selStart ++, ensureCaretVisible(3);
		showCaret();
	}

	function getCaretPosFromX(x)
	{
		// クリックされた位置を割り出す
		x -= 3;
		var text = Edit_text.substring(Edit_scrollPos);
		var i = 0;
		for(;;)
		{
			var tx = text.substring(0, i);
			var cw;
			if((cw = font.getTextWidth(tx)) > x)
			{
				i --;
				var cc = text.substring(i, 1);
				var ccw = font.getTextWidth(cc);
				cw -= ccw;
				if(x > cw + (ccw>>1)) i++;
				if(i < 0) i = 0;
				if(i > text.length) i = text.length;
				return i + Edit_scrollPos;
			}
			i++;
			if(i > text.length) return text.length + Edit_scrollPos;
		}
	}

	function onMouseDown(x, y)
	{
		// クリックされた
		if(x >= 3 && y >= 3 && x < imageWidth -3 && y < imageHeight - 3)
		{
			if(Edit_vertical)
				Edit_selStart = getCaretPosFromX(y);
			else
				Edit_selStart = getCaretPosFromX(x);
			ensureCaretVisible();
			showCaret();
		}

		focus();
		super.onMouseDown(...);
	}

	function insertCharacter(ch)
	{
		// ch をカーソル位置に挿入
		var bcr = Edit_text.substring(0, Edit_selStart); // 選択領域以前
		var acr = Edit_text.substring(Edit_selStart + Edit_selLength); // 選択領域以後
		Edit_text = bcr + ch + acr;
		Edit_selStart += ch.length;
		ensureCaretVisible();
		showCaret();
		update();
	}

	function deleteBeforeCaret()
	{
		// キャレットの手前の文字を一文字削除する
		if(Edit_selLength) return deleteSelection();
		if(Edit_selStart > 0)
		{
			var bcr = Edit_text.substring(0, Edit_selStart-1);
			var acr = Edit_text.substring(Edit_selStart + Edit_selLength);
			Edit_text = bcr + acr;
			Edit_selStart --;
			ensureCaretVisible(5);
			showCaret();
			update();
		}
	}

	function deleteAfterCaret()
	{
		// キャレットの次の一文字を削除する
		if(Edit_selLength) return deleteSelection();
		if(Edit_selStart < Edit_text.length)
		{
			var bcr = Edit_text.substring(0, Edit_selStart);
			var acr = Edit_text.substring(Edit_selStart + Edit_selLength + 1);
			Edit_text = bcr + acr;
			ensureCaretVisible();
			showCaret();
			update();
		}
	}

	function onKeyPress(key)
	{
		// キーが押された
		if(#key >= 32)
		{
			// 普通の文字
			if(!Edit_maxChars || Edit_text.length < Edit_maxChars)
				insertCharacter(key);
			// マウスカーソルを一時的に非表示にする
			window.hideMouseCursor();
		}
		else super.onKeyPress(...);
	}

	function onKeyDown(key)
	{
		if(key == (Edit_vertical?VK_UP:VK_LEFT)) stepLeft();
		else if(key == (Edit_vertical?VK_DOWN:VK_RIGHT)) stepRight();
		else if(key == VK_DELETE) deleteAfterCaret();
		else if(key == VK_BACK) deleteBeforeCaret();
		else super.onKeyDown(...);
	}


	function onFocus()
	{
		// フォーカスを得た
		super.onFocus(...);
		showCaret();
	}

	function onBlur()
	{
		// フォーカスを失った
		super.onBlur(...);
		hideCaret();
	}


	function onPaint()
	{
		// 描画の直前に呼ばれる
		// 内容を描画する
		// とりあえずクリア
		var vert = Edit_vertical;
		var imwidth = vert?imageHeight:imageWidth;
		fillRect(0, 0, imageWidth, imageHeight, 0);
		colorRect(0, 0, imageWidth, imageHeight, Edit_color, Edit_opacity);
		// 文字を描画
		// グローバル変数や、オブジェクト内変数はアクセスが遅いので、
		// よく使う変数はローカルに持ってくる
		var h = font.height;
		h = -h if(h<0);
		var x = vert ? imageWidth - 3 : 3;
		var y = 3;
		var chpos = Edit_scrollPos;
		var text = Edit_text;
		var textlen = text.length;
		var selstart = Edit_selStart;
		var selend = Edit_selLength + selstart;
		var highlightbg = 0xff000000 | clHighlight;
		var highlighttext = clHighlightText;
		var anti = Edit_antialiased;
		var textcolor = Edit_textColor;
		var tx = "";
		if(vert)
		{
			for(;;)
			{
				y = 3 + font.getTextWidth(tx);
				if(y > imwidth) break;
				if(chpos >= textlen) break;
				var ch = text[chpos++];
				var chw = font.getTextWidth(ch);
				if(chpos >= selstart && chpos < selend)
				{
					// 選択領域内
					// 背景を塗る
					fillRect(x, y, h, chw, highlightbg);
					// 文字を書く
					drawText(x, y, ch, highlighttext, 255, anti);
				}
				else
				{
					// 選択領域外
					drawText(x, y, ch, textcolor, 255, anti);
				}
				tx += ch;
			}
		}
		else
		{
			for(;;)
			{
				x = 3 + font.getTextWidth(tx);
				if(x > imwidth) break;
				if(chpos >= textlen) break;
				var ch = text[chpos++];
				var chw = font.getTextWidth(ch);
				if(chpos >= selstart && chpos < selend)
				{
					// 背景を塗る
					fillRect(x, y, chw, h, highlightbg);
					// 文字を書く
					drawText(x, y, ch, highlighttext, 255, anti);
				}
				else
				{
					// 選択領域外
					drawText(x, y, ch, textcolor, 255, anti);
				}
				tx += ch;
			}
		}
		// 文字領域外をもう一度クリア
		if(vert)
		{
			fillRect(0, imageHeight - 3, imageWidth, 3, 0);
			colorRect(0, imageHeight - 3, imageWidth, 3, Edit_color, Edit_opacity);
		}
		else
		{
			fillRect(imageWidth - 3, 0, 3, imageHeight, 0);
			colorRect(imageWidth - 3, 0, 3, imageHeight, Edit_color, Edit_opacity);
		}
        if (!Edit_noborder) {
            // 枠を描画
            colorRect(0, 0, imageWidth, 1, 0x000000, 128);
            colorRect(0, 1, 1, imageHeight-2, 0x000000, 128);
            colorRect(imageWidth-1, 1, 1, imageHeight-1, 0xffffff, 128);
            colorRect(1, imageHeight-1, imageWidth-2, 1, 0xffffff, 128);
        }
	}

	property text
	{
		setter(x)
		{
			x = "" if x === void;
			Edit_text = string x;
			Edit_selStart = 0;
			Edit_selLength = 0;
			Edit_scrollPos = 0;
			setCaretLayerPos();
			update();
		}
		getter
		{
			return Edit_text;
		}
	}

	property maxChars
	{
		setter(x)
		{
			Edit_maxChars = int x;
			Edit_selStart = 0;
			Edit_selLength = 0;
			Edit_scrollPos = 0;
			if(Edit_maxChars && Edit_text.length >= Edit_maxChars)
				Edit_text = Edit_text.substring(0, Edit_maxChars);
			setCaretLayerPos();
			update();
		}
		getter
		{
			return Edit_maxChars;
		}
	}

	property width
	{
		setter(x)
		{
			super.width = x;
			imageWidth = x;
			update();
		}
		getter
		{
			return super.width;
		}
	}

	property height
	{
		setter(x)
		{
			super.height = x;
			imageHeight = x;
			update();
		}
		getter
		{
			return super.height;
		}
	}

	function setSize(w, h)
	{
		super.setSize(w, h);
		setImageSize(w, h);
		update();
	}

	property vertical
	{
		setter(x)
		{
			Edit_vertical = int x;
			update();
		}
		getter
		{
			return Edit_vertical;
		}
	}

	property color
	{
		setter(x)
		{
			Edit_color = int x;
			update();
		}
		getter
		{
			return Edit_color;
		}
	}

	property textColor
	{
		setter(x)
		{
			Edit_textColor = int x;
			update();
		}
		getter
		{
			return Edit_textColor;
		}
	}

	property antialiased
	{
		setter(x)
		{
			Edit_antialiased = int x;
			update();
		}
		getter
		{
			return Edit_antialiased;
		}
	}

	property bgOpacity
	{
		setter(x)
		{
			Edit_opacity = int x;
			update();
		}
		getter
		{
			return Edit_opacity;
		}
	}

	property noborder
	{
		setter(x)
		{
			Edit_noborder = int x;
			update();
		}
		getter
		{
			return Edit_noborder;
		}
	}
}



