//
//  EditorView.m
//  X-BASIC'
//
//  Created by AIG-Soft(iMac) on 2012/09/18.
//  Copyright (c) 2012-15年 AIG-Soft. All rights reserved.
//

#import "EditorView.h"
#import "libs.h"
#import "main.h"
#import "FileControl2.h"
#import "click.h"
#import "mode.h"
#import "UIView+waku.h"
#import "UIActivityIndicatorView+show.h"

#define FMT_LINENO			@"%05d:"	// 行番号の表示フォーマット
#define	LEN_LINENO			(6+1)		// フォーマットの表示長さ(行番号後ろのスペーサーの文字数を含む)
#define SRCH_UNDER_LINES	15			// 検索時に下何行分表示するか;テキスト表示フォントを大きくして1画面内に収まる行数をこれ以下にしてしまうと、検索しても画面内に入らなくなってしまうが、面倒なので対策せず(テキストフォントを小さくして！)

@interface EditorView()
//<UIScrollViewWithTouchEvent>
{
	UIActivityIndicatorView	*indicator;
}
@end

@implementation EditorView

//-------------------------------------------------------------------

-(void)startIndicator
{
	printSelfs(@"cnt=%d",cntActivityIndicator);
	if (cntActivityIndicator++==0) {// 多重処理対応
		indicator=[UIActivityIndicatorView show:self style:UIActivityIndicatorViewStyleWhiteLarge fbackground:YES];
	} // 多重処理対応
}

-(void)stopIndicator
{
	printSelfs(@"cnt=%d",cntActivityIndicator);
	if (cntActivityIndicator>0) {
		if (--cntActivityIndicator==0) {
			[indicator dismiss];
		}
	} // 多重処理対応
}

//-------------------------------------------------------------------

// xibからUIViewを読み込む時の処理
- (void)awakeFromNib
{
	printSelf();
	printFrame(@"self.frame",self.frame);
	printFrame(@"editor",_editor.frame);

	alertView				=nil;
	alertView.delegate		=self;

	// 入れると落ちる
//	_editorWithLineNo.selectable	=
//	_editor.selectable				=YES;

	// iOS7 バグ回避
//	_editor.layoutManager.allowsNonContiguousLayout =
//	_editorWithLineNo.layoutManager.allowsNonContiguousLayout = NO;
	
	_editorWithLineNo.delegate		=
	_editor.delegate				=self;
	_editorWithLineNo.scrollsToTop	=
	_editor.scrollsToTop			=YES;	// ステータスバーのタップで先頭にスクロール;スクロールさせたいもののみYESにし、他はNOにしなければならない
	// editorとeditorWithLineNoは内容以外すべて同じ(大きさや表示位置)

	// iOS7.1でもズームでデッドロックすることがあるので中止
//	if (getSystemVersion()==7.1) {
//		// iOS7.1のみズーム可能
//		_editorWithLineNo.minimumZoomScale=
//		_editor.minimumZoomScale=0.5;
//		_editorWithLineNo.maximumZoomScale=
//		_editor.maximumZoomScale=2.5;
//	} else {
		// iOS8.xはUITextViewのズームに多数のバグがいるため使えない
		_editorWithLineNo.minimumZoomScale=
		_editor.minimumZoomScale=
		_editorWithLineNo.maximumZoomScale=
		_editor.maximumZoomScale=1;
//	}
	// UIWebViewはズームできない

    search.delegate              = self;
    search.hidden                = YES;
    fsearchMode                  = SEARCH_NONE;
	fscrollSync					 = NO;
    keyboardFrameEnd.size.height = 0;	// キーボードを表示してない印

    errorViewFrame               = _errorView.frame;	// 元の位置とサイズを保存
    _errorView.delegate          = self;
	lblErrors.hidden			 =
    _errorView.hidden            = YES;	// 最初はエラー表示なし
    errorViewScroll              = _errorView.scrollView;
    errorViewScroll.bounces      = NO;	// バウンス不可とする
    errorViewScroll.scrollsToTop = NO;
	//
	// UIWebViewはズームできない
    CGRect frame                 = errorViewFrame;
    frame.size.width             *= 2;// 横幅を倍にする；エラーメッセージも折り返しをさせないため
    _errorView.frame             = frame;
	//
	[lblBasicFile 	 setWaku:[UIColor blueColor] width:1.0];
	[lblSourceEncode setWaku:[UIColor blueColor] width:1.0];
	[_btnLineNo		 setWaku:[UIColor blueColor] width:1.0];


	// ズーム解除のため、ソース名Viewにタップジェスチャーを設定する
	lblBasicFile.userInteractionEnabled=YES; // IBでも設定しているけどなぜか効かないので
	UITapGestureRecognizer *tapGesture=[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(onTapTapView:)];
	tapGesture.numberOfTapsRequired=2;
	[lblBasicFile addGestureRecognizer:tapGesture];

}

// editorの表示フォントを設定する
-(void)setEditorFont:(NSString *)fontName_ pointSize:(CGFloat)pointSize_
{
	printSelf();
	_fontName=fontName_;
	_fontSize=pointSize_;
}

-(void)changeEditorView:(CGRect)rframe
{
	printSelf();
	printFrame(@"回転前_editor.frame", _editor.frame);
	// rootとselfの双方でResizeしているためか縦幅がおかしくなるので、rootからサイズを渡して補正する
	self.frame=rframe;
	// btnLineNoだけは自動調整でうまく合わせ込めなかったのでプログラム的に調整
	vwBack.hidden=YES; // 回転中に一瞬背景が見えてしまうので消す
	CGRect frame={
		// origin
		{lblBasicFile.frame.size.width+1,lblBasicFile.frame.origin.y},
		// size
		{self.frame.size.width-(lblBasicFile.frame.size.width+lblSourceEncode.frame.size.width)-2,lblBasicFile.frame.size.height}}; // -2:ボタンの左右に線を表示するため
	_btnLineNo.frame=frame;
	printFrame(@"BasicFile",	lblBasicFile.frame);
	printFrame(@"btnLineNo", 	_btnLineNo.frame);
	printFrame(@"SourceEncode", lblSourceEncode.frame);
}

-(void)changeEditorViewEnd
{
	vwBack.hidden=NO; // 背景を戻す
	printFrame(@"回転後_editor.frame", _editor.frame);
}

//-------------------------------------------------------------------

// ソース名を表示
-(void)displayBasic:(NSString *)source
{
	lblBasicFile.text=[NSString stringWithFormat:@"%@%@",NSLocalStr(@"FILE"),source];
	printSelfs(@":%@",lblBasicFile.text);
}

// Encode種別を表示する
-(void)displayEncode
{
	lblSourceEncode.text=[NSString stringWithFormat:@"%@%@",NSLocalStr(@"ENCODE"),(operationStatus.fsjis? NSLocalStr(@"FORCED_SJIS"):@"UTF8")];
}

// Encodeの表示色を設定する
-(void)colorEncode:(UIColor *)color
{
	lblSourceEncode.textColor=color;
}

//-------------------------------------------------------------------

// errorViewの表示設定
-(void)errorViewHidden:(BOOL)fhidden
{
	printSelf();
	nslog(@"fhidden=%d",fhidden);
	if (_errorView.hidden==YES && fhidden==YES) {
		// すでに消えているときに「消す」ときは何もしない
		nslog(@"すでに消えているときに「消す」ときは何もしない");
		return;
	}
	if (_errorView.hidden==NO && fhidden==NO) {
		// すでに表示されているときに「表示する」ときは何もしない
		nslog(@"すでに表示されているときに「表示する」ときは何もしない");
		return;
	}
	/*
	 editor(editorWithLineNo)のサイズと
	*/
	
	
	CGFloat h=errorViewFrame.size.height;
	CGRect edFrame=_editor.frame;
	nslog(@"元_editor.frame=%@", NSStringFromCGRect(edFrame));
	if (fhidden) {
		// 消す
		nslog(@"errorView消す");
		edFrame.size.height+=h; // エラー表示の分editorの表示を戻す
	} else {
		// 表示する
		nslog(@"errorView表示する");
		edFrame.size.height-=h; // エラー表示の分editorの表示を減らす
	}
	_editorWithLineNo.frame=
	_editor.frame=edFrame;
	nslog(@"新_editor.frame=%@", NSStringFromCGRect(edFrame));

	// エラー閉じボタンの位置を設定する
	CGRect frame=btnErrClose.frame;
	frame.origin.y=_errorView.frame.origin.y; // y座標をerrorViewに合わせる(これをしないとiPhone5でずれる)
	btnErrClose.frame=frame;
	
	// エラー数ラベルの表示位置(y)をエラー閉じボタンの中心と合わせる
	// IBで配置しても、実表示時に何故かずれるので対策
	edFrame=lblErrors.frame;
	frame=btnErrClose.frame;
	edFrame.origin.y=(frame.origin.y+frame.size.height/2)-edFrame.size.height/2;
	lblErrors.frame=edFrame;
	
	lblErrors.hidden  =
	btnErrClose.hidden=
	_errorView.hidden =fhidden;
}

//----------------------------------------------
// webViewデリゲート
//----------------------------------------------

-(void)webViewDidFinishLoad:(UIWebView *)webView
{
	printSelf();
	// エラー表示でもここに来る
}

// UIWebViewからのデリゲート
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
	printSelf();
	// エラー表示の時
	nslog(@"navigationType=%zd",navigationType);
	switch (navigationType) {
		case UIWebViewNavigationTypeLinkClicked:
		{ // Switch case is in protected scopeが発生することがあるので一応付けておく(X-BASICでは発生しないが、これを他のプログラムに入れると発生したので)
			// リンクがクリックされた
			nslog(@"リンクがクリックされた");
			nslog(@"request=%@",request);
			NSString *lnoStr=[[[request URL]path]lastPathComponent]; // path="/行番号" , last〜="行番号"
			nslog(@"URL=%@",lnoStr); // 行番号
			[self gotoLineNo:[lnoStr integerValue] fremake:NO]; // 行番号はすでにあるので再作成しない
		}
			break;
		default: // その他
			break;
	}
	return (YES);
}

//----------------------------------------------
// キーボード表示/消去通知
//----------------------------------------------

// キーボード表示通知の設定
-(void)keyboardNotification:(BOOL)onoff
{
	if (onoff) { // ON
		nslog(@"keyboardNotification on");
		// キーボード表示前に通知させる
		[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
		// キーボード表示完了に通知させる
		[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidShow:)	 name:UIKeyboardDidShowNotification	 object:nil];
		// キーボード消去時に通知させる
		[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidHide:)  name:UIKeyboardDidHideNotification  object:nil];
		//
	} else { // OFF
		nslog(@"keyboardNotification off");
		// キーボード通知の解除
		[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification 	object:nil];
		[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidShowNotification 	object:nil];
		[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidHideNotification  	object:nil];
	}
}

//----------------------------------------------

-(void)keyboardDidShow:(NSNotification *)notification
{
	printSelfs(@"%@",notification);
	waitKeyboardDidShow=YES;	// 表示された
}

-(void)keyboardWillShow:(NSNotification *)notification
{
	printSelfs(@"%@",notification);
	if (keyboardFrameEnd.size.height!=0) {
		nslog(@"すでに表示中なので何もしない");
		// なぜかキーボードがすでに表示されているにもかかわらず再度通知がかかることがあるので
		return;
	}
	
	// キーボードの表示開始時の場所と大きさを取得
	//	keyboardFrameBegin = [[notification.userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
	//	printFrame(@"キーボード表示開始時サイズ", keyboardFrameBegin);
	
	// キーボードの表示完了時の場所と大きさを取得
	keyboardFrameEnd = [(notification.userInfo)[UIKeyboardFrameEndUserInfoKey] CGRectValue];
	printFrame(@"キーボード表示終了時サイズ", keyboardFrameEnd); // {origin.x,y},{size.width,height}}
	// iOSバグ：横画面において、キーボードwidthとheightが逆になってる(iPadで確認、iPhoneは不明)
#if 0
	//UIDeviceOrientation deviceOrientation=[UIDevice currentDevice].orientation; <- これではシミュレーター上では方向が得られない
	switch (deviceOrientation) {
		case UIDeviceOrientationLandscapeLeft:
		case UIDeviceOrientationLandscapeRight:
		{ // なので入れ替える
			nslog(@"横画面なのでwidth←→heightを入れ替える");
			CGFloat w=keyboardFrameEnd.size.width;
			keyboardFrameEnd.size.width=keyboardFrameEnd.size.height;
			keyboardFrameEnd.size.height=w;
			printFrame(@"修正キーボード表示終了時サイズ", keyboardFrameEnd); // {origin.x,y},{size.width,height}}
		}
		default:
			nslog(@"他方向:%d",deviceOrientation);
			break;
	}
#else
	// deviceOrientationではシミュレーター上では方向が得られないので、簡易的に幅の大小で判定している
	CGFloat w=keyboardFrameEnd.size.width;
	if (w<keyboardFrameEnd.size.height) {
		nslog(@"横画面なのでwidth←→heightを入れ替える");
		keyboardFrameEnd.size.width=keyboardFrameEnd.size.height;
		keyboardFrameEnd.size.height=w;
		printFrame(@"修正キーボード表示終了時サイズ", keyboardFrameEnd); // {origin.x,y},{size.width,height}}
	}
#endif
	// editorの表示サイズとスクロールバーを変更
	// origin=始点(固定) , size=表示サイズ
	if (_errorView.hidden==NO) {
		[self errorViewHidden:YES];
		// errorViewを表示したままだとスクロールエリアがerrorView分抜けた状態になるので消す
	}
	// ここの時点で、editorはsearchBarの分下げられている
	CGRect frame=_editor.frame;
	printFrame(@"キーボード前editor"	 , frame);
	frame.size.height-=keyboardFrameEnd.size.height;
	printFrame(@"キーボード付きeditor", frame);
	_editor.frame=frame;
	_editorWithLineNo.frame=frame;
}

-(void)keyboardDidHide:(NSNotification *)notification
{
	printSelfs(@"%@",notification);

	// 外部キーボードモードの時はキーボードが表示されてないので、ここも呼び出されない。
	// 表示されてない＝キーボード表示に伴う他表示の調整もされてないので、それはそれでかまわないが、
	// searchBarに関する終了処理だけは別途呼び出す必要がある
	
	// サイズを戻す
	CGRect frame=_editor.frame;
	frame.size.height+=keyboardFrameEnd.size.height; // keyboardWillShowで得ていた値
	printFrame(@"キーボードなしeditor", frame);
	_editor.frame=frame;
	_editorWithLineNo.frame=frame;
	
	keyboardFrameEnd.size.height=0;	// キーボードを消した印
	if (!search.hidden) {
		// 検索中の場合は検索そのものを解除する
		[self eraseSearchBarMain];
	}

	// iOS7でのキーボード下の表示欠けバグ回避のため
	CGPoint ofst=_editor.contentOffset;
	ofst.y++;
	_editor.contentOffset=ofst;
	ofst.y--;
	_editor.contentOffset=ofst;
	
	ofst=_editorWithLineNo.contentOffset;
	ofst.y++;
	_editorWithLineNo.contentOffset=ofst;
	ofst.y--;
	_editorWithLineNo.contentOffset=ofst;
}

//----------------------------------------------
// UITextViewのデリゲート
//----------------------------------------------
/* 呼び出し順
 textViewShouldBeginEditing
 textViewDidBeginEditing
 キーボードの表示
 入力
 textView
 textViewDidChange
 
 edit終了
 textViewShouldEndEditing
 textViewDidEndEditing
 */

// _editor.editable=NOの時はそもそもここには来ない
// YESの時に、ここに本当に編集しても良いか問い合わせに来る
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView
{
	printSelf();
	//
	if (operationStatus.fsjis) {
		// 強制SHIFT-JISでは編集不可にする
		return (NO);
	}
	// 編集されると行が変わる可能性があるので検索結果は消す
	[self remakeLineNo:nil]; // 検索結果を消す
	[self eraseSearchBar];	 // 検索が出てたら消す
	// この後キーボードが表示される
	return (YES); // 編集可能
}

- (BOOL)textViewShouldEndEditing:(UITextView *)textView
{
	printSelf();
	// 強制SHIFT-JISでは編集不可にする
	return (operationStatus.fsjis? NO:YES);
}

- (void)textViewDidEndEditing:(UITextView *)textView
{
	printSelf();
}

- (void)textViewDidChange:(UITextView *)textView
{
	printSelf();
	_fafterEdit=YES;	// 編集済の印
}

- (void)textViewDidBeginEditing:(UITextView *)textView
{
	printSelf();
}

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
	printSelfs(@"text=%@",text);
	nslog(@"textView:range=%d,%d/text=%@",(int)range.location,(int)range.length,text);
	// textView.text.lenth=現在の入力文字数
	// text=今回の入力文字；かな漢字変換が入るときは、length=0でtext=変換前の文字で呼び出される
	// 「例」length=0,text=a → 変換後 → length=1,text=あ
	// text.location=入力位置
	// text.length=今回の入力文字数
	// 強制SHIFT-JISでは編集不可にする
	return (operationStatus.fsjis? NO:YES);
}

//-------------------------------------------------------------------
// UIScrollViewのデリゲート;スクロール
//-------------------------------------------------------------------

#if 0
// スクロール後、というか途中に何度か呼び出される
// ただし、手動スクロール時のみ。contentOffsetの代入による位置移動時には来ない
- (void)scrollViewDidScroll:(UIScrollView *)scview
{
	// contentOffset値が現在値になるのはここ
	printSelfs("%@",scview);
	
	if (scview==_editor) {
		nslog(@"editor scroll");
		return;
	}
	if (scview==_editorWithLineNo) {
		nslog(@"editorWithLineNo scroll");
		return;
	}
	// 他の時は何もしない
}
#endif

- (void)scrollViewDidScrollToTop:(UIScrollView *)scview
{
	printSelfs(@"%@",@":「先頭までスクロール」を実行して完了した");
	// ScrollToTopはすでに表示が先頭行になっているときは発行されない。
	// そのため、先頭行で横だけスクロールしている状態ではhomeに戻らないので注意
//	[self remakeLineNo:nil];
	[self gotoLineNo:1 fremake:-YES]; // 前の行番号ジャンプの形跡を消す
}

// 最終行へ
// アクションシートからくる
-(void)scrollToEndOfSource
{
	printSelf();
	[self gotoLineNo:lines fremake:-YES]; // 前の行番号ジャンプの形跡を消す
}

//-------------------------------------------------------------------
// UIScrollViewのデリゲート;ズーム
//-------------------------------------------------------------------

#if 0	// デリゲートが来ているかどうかの確認用
-(BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView
{
	printSelf();
	return (YES);
}
#endif

#if 0	// ズームするとデッドロックすることがあるので禁止
// UIScrollViewのデリゲート:ズーム対象のUIViewを返す
// UITextViewはUIScrollViewを継承しているので、そこからも呼び出される
// UIWebViewもそれを継承しているが呼びだされない＝ズーム不可
// これがないとズームしない(読み出し時はzoom=1.0になってしまい、書き込み時は無視される)
// ズーム前に呼び出される
-(UIView *)viewForZoomingInScrollView:(UIScrollView *)scview
{
	printSelf();
	// この中でzoom関連値をいじると再帰呼び出しになって無限ループに入ってしまうので注意
	if (scview==_editor||scview==_editorWithLineNo) {
		nslog(@"editor:%@",scview);
		// 返すView:editorの中のスクロール対象UIView
		return scview.subviews[0];
	}
#ifdef DEBUG
	else {
		nslog(@"scview:%@",scview);
	}
#endif
	return nil;
}

- (void)scrollViewDidZoom:(UIScrollView *)scview
{
	printSelfs(@"%@",scview);

	// scviewはズーム対象なので、表示しているかどうかで変わらない
	// iOS7まではhiddenのものは来なかったのかもしれないが、iOS8ではそうではないので変更

	if (_editorWithLineNo.zoomScale==_editor.zoomScale) {
		nslog(@"両方のズーム同期完了:%f",_editor.zoomScale);
		// これで相互設定による無限ループを排除する
		return;
	}
	nslog(@"ズーム処理");
	CGFloat zoom;
	BOOL fresetZoom=YES;
	if ([self isWithLineNo]) {
		zoom=_editorWithLineNo.zoomScale;
		_editor.zoomScale=zoom; // ズーム同期
		if (zoom!=1.0) {
			fresetZoom=NO;
			nslog(@"editorWithLineNoズーム中:%f",zoom);
		}
	} else {
		zoom=_editor.zoomScale;
		_editorWithLineNo.zoomScale=zoom; // ズーム同期
		if (zoom!=1.0) {
			fresetZoom=NO;
			nslog(@"editorズーム中:%f",zoom);
		}
	}
	if (fresetZoom) {
		nslog(@"ズーム解除");
		_editorWithLineNo.showsHorizontalScrollIndicator=_editor.showsHorizontalScrollIndicator	=NO;
		_editorWithLineNo.alwaysBounceHorizontal		=_editor.alwaysBounceHorizontal			=NO;
		//
	} else {
		nslog(@"ズーム設定");
		// contentSizeは元*zoomされた値が入ってきている
		_editorWithLineNo.showsHorizontalScrollIndicator=_editor.showsHorizontalScrollIndicator	=YES;
		_editorWithLineNo.alwaysBounceHorizontal		=_editor.alwaysBounceHorizontal			=YES;
	}
}
#endif

-(void)resetZoomWithAnimation
{
	printSelf();
	if (_editor.zoomScale!=1.0||_editorWithLineNo.zoomScale!=1.0) {
		// ズーム解除
		// offsetも戻すとなんか変な動きになるので中止
//		CGPoint ofst=_editor.contentOffset;
//		CGPoint ofstWithLineNo=_editorWithLineNo.contentOffset;
//		// ズームするとxは負数位置に入ることが多いけど、0で戻す
//		ofst.x=0;
//		ofstWithLineNo.x=0;
		[UIView animateWithDuration:0.3
						 animations:^(){
							 _editorWithLineNo.zoomScale=
							 _editor.zoomScale=1.0;
//							_editor.contentOffset=ofst;
//							_editorWithLineNo.contentOffset=ofstWithLineNo;
						 }
						 completion:^(BOOL finished) {
						 }
		 ];
	}
}

//-------------------------------------------------------------------
// タッチイベント処理
//-------------------------------------------------------------------

#if 0	// UIScrollViewは内部でタッチイベントを隠蔽するので直接ここには来ない
// 画面タッチイベントを取得
// Viewを重ねてしまうと(上のViewでも)タッチが受け取れなくなるようである
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
	printSelf();

	CGPoint point=[[touches anyObject]locationInView:self];
	nslog(@"point=%@",NSStringFromCGPoint(point));
	
	// 他の領域なら上へお任せ
	[super touchesBegan:touches withEvent:event];
}

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
	printSelf();
	
	CGPoint point=[[touches anyObject]locationInView:self];
	nslog(@"point=%@",NSStringFromCGPoint(point));
	
	// 他の領域なら上へお任せ
	[super touchesMoved:touches withEvent:event];
	
}
#endif

// +TouchEventカテゴリで外に引っ張り出している
- (void)scrollTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
	printSelf();

#ifdef DEBUG
	CGPoint point=[[touches anyObject]locationInView:self];
	nslog(@"point=%@",NSStringFromCGPoint(point));
#endif
	// スクロール時に行えば良いようにも思えるが、gotoLineNo自体がスクロールを起こすため、
	// 手動スクロールに限る必要がある＝タッチを取るしかない
	fscrollSync=NO;
}

-(void)onTapTapView:(UITapGestureRecognizer *)gesture
{
	printSelfs(@"gesture=%@",gesture);
	nslog(@"ダブルタップ");
	[self resetZoomWithAnimation];
}

//-------------------------------------------------------------------
// テキスト設定
//-------------------------------------------------------------------

// 行番号のON/OFF
-(void)lineNoHidden:(BOOL)fhidden
{
	printSelf();
	
	nslog(@"fscrollSync=%d",fscrollSync);
	nslog(@"editor.contentOffset=%@",NSStringFromCGPoint(_editor.contentOffset));
	nslog(@"editorWithLineNo.contentOffset=%@",NSStringFromCGPoint(_editorWithLineNo.contentOffset));
	
	if (fhidden) {
		nslog(@"行番号を消す");
		if (!fscrollSync) {
			nslog(@"gotoLineNo直後以外ならスクロール位置同期をかける:e<-w");
			// 簡易同期なのでずれる
// 本当は↓これで良いはず。
//			_editor.contentOffset=_editorWithLineNo.contentOffset;
// ところが、iOS7/8のcontentOffsetには極めて重大なバグがいて、設定してもその位置に移動しない。
// なので、アニメーションを使い、contentOffsetを設定値まで連続で与え続けて移動させる
			[_editor setContentOffset:_editorWithLineNo.contentOffset animated:YES];
			fscrollSync=YES;
		}
		_editor.hidden=NO;
		_editorWithLineNo.hidden=YES;
	} else {
		nslog(@"行番号を表示");
		if (!fscrollSync) {
			nslog(@"gotoLineNo直後以外ならスクロール位置同期をかける:e->w");
			// 簡易同期なのでずれる
//			_editorWithLineNo.contentOffset=_editor.contentOffset;
			[_editorWithLineNo setContentOffset:_editor.contentOffset animated:YES];
			fscrollSync=YES;
		}
		_editor.hidden=YES;
		_editorWithLineNo.hidden=NO;
	}

//	切換しただけでは同期解除は必要ない
//	fscrollSync=NO;
	
	// 行番号をON/OFFしたら検索開始位置を戻す
	searchLine=0;
}

// setter
-(void)setText:(NSString *)text
{
	printSelf();
	_text=text; // copy

	_editorWithLineNo.zoomScale=_editor.zoomScale=1.0;
//	[self resetZoomWithAnimation];
	
	// テキスト設定時は行番号あり、エラー無しで
	[self setFont:text fwithLineNo:(_opeMode!=MODE_EDIT) lnos:nil];

	// 1行目に移動
	[self gotoLineNo:1 fremake:NO];
}

// 行番号文字列再作成(単一行エラー付き)
// elno=内部行番号
-(void)remakeLineNo1:(uwLineNO)elno
{
	printSelfs(@"elno=%d",elno);
	NSArray *lnos=@[[NSValue valueWithBytes:&elno objCType:@encode(uwLineNO)]];
	[self setFont:nil fwithLineNo:YES lnos:lnos];
}

// 行番号文字列再作成
// lnos=nil/[lnos count]==0のときは反転行なし
-(void)remakeLineNo:(NSArray *)lnos
{
	[self setFont:nil fwithLineNo:YES lnos:lnos];
}

// 編集（保存後）、情報を内部に反映する
-(void)remakeLineNoAll
{
	// self.testにも戻す
	_text=self.editor.text;
	[self setFont:_text fwithLineNo:YES lnos:nil];
}

//----------------------------------------------

/// テキストを表示する
// 行番号
/// @param text	表示するテキスト nilの時は現在のself.textを使う
/// @param fwithLineNo	行番号側も再作成するかどうか
/// @param lnos	エラー行番号の配列(fwithLineNo=YESのときのみ有効)<br>
-(void)setFont:(NSString *)text fwithLineNo:(BOOL)fwithLineNo lnos:(NSArray *)lnos
{
	printSelf();
	
	nslog(@"setFont:fwithLineNo=%d,lnos=%@",fwithLineNo,lnos);
	
	uwLineNO elno=LINENO_SPECIAL; // 警告回避のため
	NSEnumerator *emu=nil;
	if (fwithLineNo) {
		if (lnos==nil||[lnos count]==0) {
			// 行番号再作成(エラー行無し)
			emu=nil;
			elno=LINENO_SPECIAL; // ありえない行番号
			nslog(@"エラー行なし");
		} else {
			nslog(@"count=%zd/lnos=%@",[lnos count],lnos);
			emu=[lnos objectEnumerator];
			[[emu nextObject] getValue:&elno]; // 最初の値を得ておく
			nslog(@"最初エラー行:%d",elno+1);
		}
	}
	
	BOOL fremake; // 情報を再作成するかどうか；時間がかかるので高速化のため
	// YESのとき、editor/editorWithLineNoともに再作成
	if (text==nil) {
		// 現在の内容を使う
		if (!fwithLineNo) {
			nslog(@"再作成すべき項目はなにもないので即リターン");
			return;
		}
		nslog(@"情報再利用");
		fremake=NO;
		// text=self.text;	相当
		// 前回のlineArrayを使う
	} else {
		nslog(@"情報再作成");
		fremake=YES;
		fwithLineNo=YES; // 行番号側も再作成

		// 前の内容を消す
		_editor.attributedText=nil;
		_editorWithLineNo.attributedText=nil;

		// 検索開始位置も初期化する
		selectRange.location=0;
		selectRange.length  =0;
		searchLine=0;
		search.text=@"";
		
		// 改行によって分解する
		lineArray=[text componentsSeparatedByString:@"\x0a"]; // =LFでありiOS上では\nのようである
		// これでlineArray内に改行は存在しなくなる
		nslog(@"lineArray=%@",lineArray);
		// これをすると、最後に空の要素が入ってしまう
		// 「例」A\nB\nを分割すると、[0]="A\n",[1]="B\n",[2]=""となる。
		// しかし、A\nBのときは[1]="B"で[2]は存在しない
		// 行数を表示する
		lines=[lineArray count];
		if ([lineArray[lines-1] length]==0) lines--; // 最後に空が入っていなら-1する
		[_btnLineNo setTitle:[NSString stringWithFormat:@"%5d",lines] forState:UIControlStateNormal];
//		nslog(@"lines =%d",lines);
	}
	
	// フォント設定
	nslog(@"_fontName=%@/_fontSize=%f",_fontName,_fontSize);
	UIFont *font=[UIFont fontWithName:_fontName size:_fontSize];
	_editor.font=font; // ここで設定が正しくないとき(font=nullとかpointSize=0)システムデフォルトフォント(Helvetica)が設定される
	_editorWithLineNo.font=font; // 上のelse{}ないで設定すると何故かこれが正しく設定されない;fontSize=0になってしまう。なのでここに置いた

	nslog(@"setFont:fremake=%d,fwithLineNo=%d,lnos=%@",fremake,fwithLineNo,lnos);
/* ここで状態としてありえるのは以下の3つのみ
 	fremake	fwithLineNo	lno
 	NO		NO			*		何もしない
 	NO		YES			nil		行番号表示属性をクリア	editor側は不変
 	NO		YES			あり		指定行番号を変化		editor側は不変
 	YES		YES			*		全部を再作成
*/
	NSMutableAttributedString *atrbTextAll;
	NSMutableAttributedString *atrbTextAllWithLineNo;
	UIFont *lineNoFont;
	UIColor *lineNoColor;
	UIColor *errLineNoColor;
	NSAttributedString *spacer;
	if (fwithLineNo) {
		atrbTextAllWithLineNo=[[NSMutableAttributedString alloc]init];
		// 行番号用フォント
		lineNoFont		=[UIFont fontWithName:@"Trebuchet MS" size:font.pointSize];
		lineNoColor   	=[UIColor colorWithRed:0.8 green:0.8 blue:0.8 alpha:1.0]; // 普通行
		errLineNoColor	=[UIColor redColor]; // エラー行色
		spacer			=[[NSAttributedString alloc]
								  initWithString:@" "	// LEN_SPCACER文字にすること
								  attributes:@{
											   NSFontAttributeName:lineNoFont,
											   NSBackgroundColorAttributeName:_editorWithLineNo.backgroundColor
											   }
						  ];
		
	}
	if (fremake) {
		atrbTextAll=[[NSMutableAttributedString alloc]init];

		// 各行の高さと位置情報の取得のための準備
		lineAtrb=nil; // 前のを開放(念のため)
		lineAtrb=[[NSMutableArray alloc]init];
	}
	
	ULong top=0;	// その行先頭までの総文字数
	uwLineNO lno=0; // 内部行番号
	for (NSString *s in lineArray) {
		int len=(int)[s length]+1;	// 改行は含まれていないので入れておく
		// 行番号有り無しでのtopの違いは計算式で得られるので、ここで記録するのはなしの時のもの
		LINE_ATRB la;	// この行の情報
		la.top=top;
		la.len=len;
		nslog(@"(%d)top=%d,len=%d",(int)lno,(int)top,len);
		top+=len;

		// 行本体
		NSAttributedString *atrbText1 = [[NSAttributedString alloc]
											 initWithString:[NSString stringWithFormat:@"%@\n",s]
											 attributes:@{
														  NSFontAttributeName:font
														  }
										 ];
		if (fwithLineNo) {
			NSAttributedString *lineNo;
			// 行番号は桁がずれないようにする
			if (lno==elno) { // エラー行
				nslog(@"エラー行=%d",elno+1);
				lineNo = [[NSAttributedString alloc]
						  initWithString:[NSString stringWithFormat:FMT_LINENO,lno+1]
						  attributes:@{
									   NSFontAttributeName:lineNoFont,
									   NSBackgroundColorAttributeName:errLineNoColor
									   }
						  ];
				// 次のエラー番号を得る
				NSValue *val=[emu nextObject];
				if (val!=nil) {
					[val getValue:&elno];
					nslog(@"次エラー行:%d",elno+1);
				} else {
					elno=LINENO_SPECIAL; // 次はない
					nslog(@"次エラー行なし");
				}
			} else {
				// 普通行
				lineNo = [[NSAttributedString alloc]
							 initWithString:[NSString stringWithFormat:FMT_LINENO,lno+1]
							 attributes:@{
										  NSFontAttributeName:lineNoFont,
										  NSBackgroundColorAttributeName:lineNoColor
										  }
							 ];
			}
			[atrbTextAllWithLineNo 	appendAttributedString:lineNo];
			[atrbTextAllWithLineNo 	appendAttributedString:spacer];
			[atrbTextAllWithLineNo 	appendAttributedString:atrbText1];
		}
		if (fremake) {
			// 行情報記録
			[lineAtrb addObject:[NSValue valueWithBytes:&la objCType:@encode(LINE_ATRB)]];
//			nslog(@"(%d行:長さ=%d,文字位置=%d",lno,(int)la.len,(int)la.top);
			[atrbTextAll 			appendAttributedString:atrbText1];
		}
		lno++;
		if (lno==lines) break; // lineArrayは最後に""が入っているので、これで強制中断する
	}
	
	// テキスト設定
	if (fwithLineNo) {
		_editorWithLineNo.attributedText=atrbTextAllWithLineNo;
	}
	if (fremake) {
		_editor.attributedText			=atrbTextAll;


	}

	[self stopIndicator];
}

//----------------------------------------------
// 検索
//----------------------------------------------

// searchBarの表示に従ってeditor位置を調整する
// direc : 1.0=下げる , -1.0=上げる
-(void)searchBarWithScreen:(CGFloat)direc
{
	printSelf();
	nslog(@"searchBar分、editorを%@",direc<0? @"上げる":@"下げる");
	//
	CGFloat h=search.frame.size.height*direc;
	nslog(@"searchBar高さ:%f",h);
	//
	CGRect frame=_editor.frame;
	printFrame(@"editor", frame);
	frame.origin.y   +=h;
	frame.size.height-=h;
	_editor.frame=frame;
	_editorWithLineNo.frame=frame;
	printFrame(@"editor", frame);
}

-(void)eraseSearchBar
{
	printSelf();
	nslog(@"search.hidden=%d",search.hidden);
	if (search.hidden==YES) {
		nslog(@"すでに消えているので何もしない");
		return;
	}
	clickSound();
	
	// iOS8ではこれがないと消えない
	// iOS7までは必ずキーボードが表示されていたのでkeyboardDidHideが実行されていたが、
	// iOS8では外部キーボード表示中はkeyboardDidHideが実行されないので、
	// 別途処理しなければならない。
	[self eraseSearchBarMain];

	// キーボードが表示されているときは消す→keyboardDidHide→eraseSearchBarMainが呼び出される
	[search resignFirstResponder];
}

-(void)eraseSearchBarMain
{
	printSelf();
	search.hidden=YES;
	[self searchBarWithScreen:-1.0]; // searchBar分、editorを上げる
	
	// 検索を実行すると、なぜかeditorが編集可能になってしまうので戻す
	if (!_fafterEdit) {
		_editor.editable=NO;
		_editorWithLineNo.editable=NO;
	}
	
	// もともとerrorViewが表示されていたときは戻す
	if (_errorView.hidden==NO) {
		// これをしないと、_editor.frameがいっぱいに広がってしまう
		nslog(@"errorViewが表示されていたので_editor.frameを戻す");
		[self errorViewHidden:YES]; // 一旦消す
		[self errorViewHidden:NO ]; // 再表示する
	}
}

-(void)displaySearchEnd
// 「検索終了」としばらく表示して消す
{
	printSelf();
	alertView = [[UIAlertView alloc]  initWithTitle:NSLocalStr(@"SEARCHEND")
											message:NSLocalStr(@"AUTOERASE")
										   delegate:self
								  cancelButtonTitle:nil
								  otherButtonTitles:NSLocalStr(@"OK"),nil
				 ];
	[alertView show];
	// 一定時間後に消去
	[self performSelector:@selector(eraseSearchEnd:) withObject:self afterDelay:2.0];
}

-(void)eraseSearchEnd:(id)sender
{
	printSelf();
	[alertView dismissWithClickedButtonIndex:0 animated:YES]; // ボタン番号は適当
	alertView=nil; // 開放した印
}

-(void)searchMain
{
	printSelf();
	[self startIndicator];

	if (search.hidden==YES) {
		nslog(@"searchBarを表示する");
		if (lines==0) return; // 1行もないときは検索不可
		//
		[self searchBarWithScreen:1.0]; // searchBar分、editorを下げる
		search.hidden=NO;
	} // すでに開いている時な内容の変更のみ(呼び出し前におこなわれている)
	
	// キーボードを表示
	[search becomeFirstResponder];
	[self remakeLineNo:nil]; // 前の検索結果を消す
	[self stopIndicator];
}

- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar
{
	printSelf();
	[self eraseSearchBar];
	// iOS6までは検索欄に何か入力しないとCancelボタンは押せるようにならない。iOS7では問題ない。
}

- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
{
	printSelf();
	[self startIndicator];
	
	fscrollSync=NO;
	NSInteger len=[search.text length];
	nslog(@"検索文字列(len=%zd)=%@",len,search.text);
	// 実検索処理
	__block uwLineNO lno;
	__block BOOL fmatch=NO; // ブロック構文の中で参照できるようにするための宣言
	switch (fsearchMode) {
		case SEARCH_LINENO: // 行番号検索
			lno=[search.text integerValue]; // 1〜
			if (lno<=0 || lno>lines) {
				// 無効な行番号なので「見つからない」
				break;
			}
			// 指定行番号にジャンプする
			[self gotoLineNo:lno fremake:YES]; // 行番号も再作成
			//
			fmatch=YES; // 発見した
			searchLine=lno-1; // 文字列検索のために記録
			break;
		//
		case SEARCH_STRING: // 文字列検索
		{ // ローカルブロック for switch
			NSStringEnumerationOptions opt=NSStringEnumerationByLines;
			// NSStringEnumerationByLines=行単位で切り出し
			// NSStringEnumerationByWords=単語単位；うまくいかない（複数単語の組み合わせの検索ができなくなる）
			// NSStringEnumerationByComposedCharacterSequences=1文字ごと

			lno=searchLine; // 行番号(0〜)
			nslog(@"検索開始行番号(0〜)=%d",lno);
			NSRange	searchRange; // 検索範囲;location,length
			LINE_ATRB la;
			[lineAtrb[lno] getValue:&la];
			
			NSString *text;
			
			if ([self isWithLineNo]) {
				text=_editorWithLineNo.text;
				searchRange.location=la.top+LEN_LINENO*lno; // 行番号分も;本当は表示されている先頭を入れたい
			} else {
				text=_editor.text;
				searchRange.location=la.top; // 本当は表示されている先頭を入れたい
			}
			searchRange.length=[text length]-searchRange.location; // テキスト最後まで
			nslog(@"searchRange=%d〜%d",(int)searchRange.location,(int)searchRange.length);
			
			[text enumerateSubstringsInRange:searchRange options:opt usingBlock:
			 ^(NSString *subString,NSRange subStringRange,NSRange encloseingRange,BOOL *stop) {
				 // オプションに従ってtextを切り出してくるので、比較して結果を返す
				 // 切り出せないときはここは呼び出されない
//				 nslog(@"-----\nsubString range=%d〜%d/enclosing range=%d〜%d"
//					   ,(int) subStringRange.location,(int) subStringRange.length	// 先頭からの文字位置と文字数
//					   ,(int)encloseingRange.location,(int)encloseingRange.length	// 前回の位置から切り出した最後の文字までの長さ
//					   );
				 if ([self isWithLineNo]) {
					 // 行番号付きの時は行番号部分は省いて比較
					 subString=[subString substringFromIndex:LEN_LINENO];
				 }
				 NSInteger subLen=[subString length];
				 nslog(@"%d.subString(subLen=%zd)=%@",lno,subLen,subString);
				 // 行単位なので、その中での比較を別途行う必要がある
				 {
					 *stop=NO;
					 NSRange range={0,len}; // len=検索文字列長
					 for (NSInteger i=0;i<=subLen-len;i++) {
						 range.location=i;
						 NSString *s=[subString substringWithRange:range]; // lenもサロゲートペアを考慮してないからこれでよい
						 //nslog(@"%@ : %@",s,search.text);
						 if ([s isEqualToString:search.text]) {
							 // 一致
							 *stop=YES; // 一致したら終了
							 break;
						 }
					 }
				 }
				 if (*stop==YES) {
					 fmatch=YES; // 発見した
					 // その行番号にジャンプする
					 [self gotoLineNo:lno+1 fremake:YES]; // 行番号も再作成;gotoLineNoに渡すのは1〜の行番号
					 searchLine=lno+1; // 次回検索は次の行から
				 } // if _(*stop)
				 // 一致行番号(0〜)
				 lno++;
			 } // ^()
			 ]; // enumerateSubstringsInRange
		} // ローカルブロック
			break;
	} // switch
	if (!fmatch) {
		// 見つからなかった時=検索終了
		[self eraseSearchBar]; // 検索バーを消す
		[self displaySearchEnd];
	}
	[self stopIndicator];
}

//----------------------------------------------
// 行番号ジャンプ
//----------------------------------------------

/// 行番号を表示しているかどうか
-(BOOL)isWithLineNo
{
	return (_editorWithLineNo.hidden==NO);
}

// fremake YES=その行番号に印を表示 , NO=表示しない、再作成しない , -YES=表示しないが再作成する
-(void)gotoLineNo:(uwLineNO)lno1 fremake:(int)fremake
{
	nslog(@"gotoLineNo:lno1=%d,fremake=%d",lno1,fremake);
	printSelf();

	if (lines==0) return; // 行情報がないときは処理しない；最初に飛んできてしまうので
	
	uwLineNO lno=lno1-1; // 行番号ｰ>内部番号
	
	if (fremake==YES) {
		// 行番号に印をつけるので再作成が必要(ただし表示されるかどうかは別)
		// 先に行番号付きテキストを修正してからscrollRangeToVisibleをかけないとキャンセルされてしまう
		[self remakeLineNo1:lno];
	}

	if (lno1<=SRCH_UNDER_LINES) {
		nslog(@"この行の範囲では常に先頭から:%d〜%d,lno=%d",1,SRCH_UNDER_LINES,lno1);
		_editor.contentOffset			=CGPointZero;
		_editorWithLineNo.contentOffset	=CGPointZero;
	} else {
		// 行情報
		LINE_ATRB la;
		[lineAtrb[lno] getValue:&la];
		nslog(@"(%d)top=%d,len=%d",lno+1,(int)la.top,la.len);
		// そこ+下SRCH_UNDER_LINES行が見えるようにスクロール
		
		// editor側
		selectRange.location=la.top;
		int len=la.len; // その行分
		if (len==0) len=1; // 長さ0の行でも表示位置にするため(終端とか)
		int j=1;
		for (int i=1;i<=SRCH_UNDER_LINES;i++) { // 次の行から
			LINE_ATRB la2;
			if (lno+i>=lines) break;
			[lineAtrb[lno+i] getValue:&la2];
			len+=la2.len;
			j++;
		}
		selectRange.length=len;
		nslog(@"editor:SelectRange=%tu〜%tu",selectRange.location,selectRange.length);
		[_editor scrollRangeToVisible:selectRange];
		
		// editorWithLineNo側
		selectRange.location=la.top+LEN_LINENO*lno; // 行番号分も
		nslog(@"(%d)top=%d,len=%d/行番号付き=+%d",lno+1,(int)la.top,la.len,LEN_LINENO*lno);
		
		nslog(@"editorWithLineNo:SelectRange=%tu〜%tu",selectRange.location,selectRange.length);
		nslog(@"検索結果文字列:%@",[_editorWithLineNo.text substringWithRange:selectRange]);
		len+=(LEN_LINENO*j);
		selectRange.length=len;
		[_editorWithLineNo scrollRangeToVisible:selectRange];
	}
	fscrollSync=YES;

	// iOS8では先にscrollRangeToVisibleかけてからテキストを修正しても移動できる
	
	// これをするとなぜかeditableがYESになってしまうので、元に戻す
	if (_opeMode!=MODE_EDIT) {
		_editor.editable=NO;
		_editorWithLineNo.editable=NO;
	}
}

//----------------------------------------------

// 行番号ジャンプ
-(void)searchLineJump
{
	printSelf();
	if (fsearchMode!=SEARCH_LINENO) {
		// 前回と異なる検索モードの時
		search.text=@""; // 前の内容を消す
	}
	fsearchMode=SEARCH_LINENO;
	// キーボードタイプを設定
	search.keyboardType=UIKeyboardTypeNumbersAndPunctuation;	// UIKeyboardTypeNumberPad;数字だけにするとSearchキーが存在しないので検索できない
	// 外部キーの場合はEnter押下でいけるけど
	search.placeholder=NSLocalStr(@"LINENO");

	// 検索開始位置
	[self searchMain];
}

// RootVCから検索を押すとここに来る
-(void)searchString
{
	printSelf();
	if (fsearchMode!=SEARCH_STRING) {
		// 前回と異なる検索モードの時
		search.text=@""; // 前の内容を消す
	}
	fsearchMode=SEARCH_STRING;
	// キーボードタイプを設定
	search.keyboardType=UIKeyboardTypeDefault;
	search.placeholder =NSLocalStr(@"SEARCH");

	[self searchMain];
}

-(void)errorJump
{
	printSelf();
	slLineNO lno0=xb_errLine_info[0].lno;
	if (lno0<0) {
		nslog(@"statup内エラー");
		lno0=-lno0;
		alertView = [[UIAlertView alloc]  initWithTitle:NSLocalStr(@"STARTUPERROR")
												message:[NSString stringWithFormat:NSLocalStr(@"STARTUPERR_MESSAGE"),lno0]
											   delegate:self
									  cancelButtonTitle:NSLocalStr(@"OK")
									  otherButtonTitles:nil
					 ];
		[alertView show];
	} else {
		nslog(@"ユーザーソース内エラー");
		// エラー情報を展開
		[self errorInfo2View:errorCnt];
		// エラー数を表示
		lblErrors.text=[NSString stringWithFormat:@"%d%@",errorCnt,NSLocalStr(@"ERRCNT")];
		[self errorViewHidden:NO];
		// 最初のエラーに飛ぶ
		// ここに来る以前にすべてのエラー行に印をつけてあるので、ここでは行番号再作成は不要
		if (lno0!=LINENO_SPECIAL) {
			nslog(@"最初のエラーに飛ぶ");
			[self gotoLineNo:lno0+1 fremake:NO];
		} // システムエラーの時はジャンプできない
	}
}

// alertViewのボタンが押された
-(void)alertView:(UIAlertView *)alertView_ clickedButtonAtIndex:(NSInteger)buttonIndex
{
	nslog(@"alertView:ボタン=%zd",buttonIndex);
	clickSound();
	if (buttonIndex==alertView_.cancelButtonIndex) { // キャンセル
		// alertView自体が画面外タップなどでキャンセルされた時もここに来る
		nslog(@"キャンセル");
		// この中では特にすることはない
	}
	alertView=nil; // 開放した印
}

//-------------------------------------------------------------------

// エラー情報からテキストを作成する
// startup内のエラーlno<0は与えないこと
-(void)errorInfo2View:(int)cnt
{
	nslog(@"errorInfo2View:cnt=%d",cnt);
	//
	NSMutableArray *enos=[[NSMutableArray alloc]init];
	NSMutableString *errorText=[[NSMutableString alloc]initWithFormat:@"<html>\n"
								@"<body bgcolor='#ffccff'>\n"
								@"<div style='"
								@"font-family:\x22System\x22; "
								@"font-size:%.0fpx; "
								@"font-weight:normal; "
								@"font-style:normal; "
								@"text-align:left;"
								@"vertical-align:middle; "
								@"color: #f000000 "
								@"';>\n"
								,(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad? 14.0:10.0) // フォントサイズ
								];
	
	int i;
	uwLineNO lno0=LINENO_SPECIAL; // 特定できる最初の行
	if (cnt>errorLimit) cnt=errorLimit; // 上限以上のエラーについては情報は記録されていない
	for (i=0;i<cnt;i++) {
		XB_ERRORNO	eno=xb_errLine_info[i].eno;
		uwLineNO	lno=xb_errLine_info[i].lno; // 0〜
		char	 *exmes=xb_errLine_info[i].mes;
		
		BOOL frecord;
		[errorText appendString:make1ErrorMessage(eno,lno,exmes,NO,YES,&frecord)]; // startup.bas内エラーはここには来ない
		if (frecord) {
			// エラー行番号を記録
			[enos addObject:[NSValue valueWithBytes:&lno objCType:@encode(uwLineNO)]];
			if (lno0==LINENO_SPECIAL) lno0=lno; // 最初の行を記録
		}
	} // for
	
	[errorText appendString:@"</div>\n"
	 @"</body>\n"
	 @"</html>\n"
	 ];
#ifdef DEBUG
	[errorText writeToFile:TestFile(@"error.html") atomically:YES encoding:NSUTF8StringEncoding error:nil];
#endif
	[_errorView loadHTMLString:errorText baseURL:nil];
	
	nslog(@"enos数=%d/%@",(int)[enos count],enos);
	[self remakeLineNo:enos];
}

//-------------------------------------------------------------------

// コンパイル開始前処理
-(void)compilePrepare
{
	[_errorView loadHTMLString:@"" baseURL:nil]; 	// 内容を消す
	setErrorInfoBuff(MAX_ERRORS,xb_errLine_info);
}

//-------------------------------------------------------------------

-(IBAction)buttonAction:(id)sender
{
	UIButton *btn=(UIButton *)sender;
	printSelf();
	nslog(@"btn.tag=%zd",btn.tag);
	clickSound();
	switch (btn.tag) {
		case BUTTON_ERRCLOSE:
			nslog(@"ButtonErrClose");
			[self errorViewHidden:YES];
			break;
			
		case BUTTON_LINENO:
			nslog(@"ButtonLineNo");
			[self searchLineJump];
			break;
	}
}

//-------------------------------------------------------------------

@end
