#include "stdafx.h"
#include "ip_doc.h"
using namespace editwing;
using namespace editwing::doc;
//=========================================================================
//---- ip_text.cpp 文字列操作・他
//
// 文字列を挿入したり削除したり…という辺りの処理が
// このファイルにまとめてある。外向けのインターフェイスの
// 実装もついでにここで。
//
//---- ip_parse.cpp キーワード解析
//---- ip_wrap.cpp 折り返し
//---- ip_scroll.cpp スクロール
//---- ip_draw.cpp 描画・他
//---- ip_cursor.cpp カーソルコントロール
//=========================================================================
//-------------------------------------------------------------------------
// 公開インターフェイス
//-------------------------------------------------------------------------
Document::Document() : busy_(false)
{ impl_ = new DocImpl( *this ); }
Document::~Document()
{}
void Document::Execute( const Command& c )
{ impl_->Execute( c ); }
void Document::SetKeyword( const unicode* b, ulong s )
{ impl_->SetKeyword( b, s ); }
void Document::AddHandler( DocEvHandler* h )
{ impl_->AddHandler( h ); }
void Document::DelHandler( DocEvHandler* h )
{ impl_->DelHandler( h ); }
void Document::OpenFile( aptr<TextFileR> t )
{ impl_->OpenFile( t ); }
void Document::SaveFile( TextFileW& t )
{ impl_->SaveFile( t ); }
void Document::ClearAll()
{ impl_->ClearAll(); }
ulong Document::tln() const
{ return impl_->tln(); }
const unicode* Document::tl( ulong i ) const
{ return impl_->tl( i ); }
ulong Document::len( ulong i ) const
{ return impl_->len( i ); }
ulong Document::getRangeLength( const DPos& s, const DPos& e ) const
{ return impl_->getRangeLength( s, e ); }
void Document::getText( unicode* b, const DPos& s, const DPos& e ) const
{ impl_->getText( b, s, e ); }
bool Document::isUndoAble() const
{ return impl_->isUndoAble(); }
bool Document::isRedoAble() const
{ return impl_->isRedoAble(); }
bool Document::isModified() const
{ return impl_->isModified(); }
void Document::ClearModifyFlag()
{ impl_->ClearModifyFlag(); }
void Document::Undo()
{ impl_->Undo(); }
void Document::Redo()
{ impl_->Redo(); }
void Document::SetUndoLimit( long lim )
{ impl_->SetUndoLimit( lim ); }
//-------------------------------------------------------------------------
// イベントハンドラ処理
//-------------------------------------------------------------------------
void DocImpl::AddHandler( DocEvHandler* eh )
{
// ハンドラ追加
pEvHan_.Add( eh );
}
void DocImpl::DelHandler( DocEvHandler* eh )
{
// 後ろから見て行って…
const int last = pEvHan_.size() - 1;
// …見つけたら削除
for( int i=last; i>=0; --i )
if( pEvHan_[i] == eh )
{
pEvHan_[i] = pEvHan_[last];
pEvHan_.ForceSize( last );
break;
}
}
void DocImpl::Fire_TEXTUPDATE
( const DPos& s, const DPos& e, const DPos& e2, bool reparsed, bool nmlcmd )
{
AutoLock lk(this);
// 全部にイベント通知
for( ulong i=0, ie=pEvHan_.size(); i<ie; ++i )
pEvHan_[i]->on_text_update( s, e, e2, reparsed, nmlcmd );
}
void DocImpl::Fire_KEYWORDCHANGE()
{
AutoLock lk(this);
// 全部にイベント通知
for( ulong i=0, ie=pEvHan_.size(); i<ie; ++i )
pEvHan_[i]->on_keyword_change();
}
void DocImpl::Fire_MODIFYFLAGCHANGE()
{
AutoLock lk(this);
// 全部にイベント通知
bool b = urdo_.isModified();
for( ulong i=0, ie=pEvHan_.size(); i<ie; ++i )
pEvHan_[i]->on_dirtyflag_change( b );
}
//-------------------------------------------------------------------------
// UnDo,ReDo 処理
//-------------------------------------------------------------------------
UnReDoChain::Node::Node( Command* c, Node* p, Node* n )
: cmd_ ( c )
, prev_( p )
, next_( n )
{
}
UnReDoChain::Node::Node()
{
next_ = prev_ = this;
cmd_ = NULL;
}
UnReDoChain::Node::~Node()
{
delete cmd_;
}
void UnReDoChain::Node::ResetCommand( Command* cmd )
{
delete cmd_;
cmd_ = cmd;
}
UnReDoChain::UnReDoChain()
: lastOp_ ( &headTail_ )
, savedPos_( &headTail_ )
, num_ ( 0 )
, limit_ ( static_cast<ulong>(-1) )
{
}
UnReDoChain::~UnReDoChain()
{
Clear();
}
ulong UnReDoChain::Node::ChainDelete(Node*& savedPos_ref)
{
if( cmd_ == NULL )
return 0;
if( savedPos_ref == this )
savedPos_ref = NULL;
dptr<Node> d(this);
return 1 + next_->ChainDelete(savedPos_ref);
}
void UnReDoChain::Clear()
{
headTail_.next_->ChainDelete(savedPos_);
headTail_.next_ = headTail_.prev_ = lastOp_ = savedPos_ = &headTail_;
num_ = 0;
}
void UnReDoChain::SetLimit( long lim )
{
limit_ = Max( 1UL, ulong(lim) );
}
inline void UnReDoChain::Undo( Document& doc )
{
lastOp_->ResetCommand( (*lastOp_->cmd_)(doc) );
lastOp_ = lastOp_->prev_;
}
inline void UnReDoChain::Redo( Document& doc )
{
lastOp_ = lastOp_->next_;
lastOp_->ResetCommand( (*lastOp_->cmd_)(doc) );
}
void UnReDoChain::NewlyExec( const Command& cmd, Document& doc )
{
Command* nCmd = cmd(doc);
if( nCmd != NULL )
{
num_ -= (lastOp_->next_->ChainDelete(savedPos_) - 1);
lastOp_ = lastOp_->next_ = new Node(nCmd,lastOp_,&headTail_);
while( limit_ < num_ )
{
// 回数制限を越えたので、古い物を削除
Node* old = headTail_.next_;
headTail_.next_ = old->next_;
old->next_->prev_ = &headTail_;
if( old != &headTail_ )
delete old;
if( savedPos_ == &headTail_ )
savedPos_ = NULL;
else if( savedPos_ == old )
savedPos_ = &headTail_;
--num_;
}
}
}
bool DocImpl::isUndoAble() const
{ return urdo_.isUndoAble(); }
bool DocImpl::isRedoAble() const
{ return urdo_.isRedoAble(); }
bool DocImpl::isModified() const
{ return urdo_.isModified(); }
void DocImpl::SetUndoLimit( long lim )
{ urdo_.SetLimit( lim ); }
void DocImpl::ClearModifyFlag()
{
bool b = urdo_.isModified();
urdo_.SavedHere();
if( b != urdo_.isModified() )
Fire_MODIFYFLAGCHANGE();
}
void DocImpl::Undo()
{
if( urdo_.isUndoAble() )
{
bool b = urdo_.isModified();
urdo_.Undo(doc_);
if( b != urdo_.isModified() )
Fire_MODIFYFLAGCHANGE();
}
}
void DocImpl::Redo()
{
if( urdo_.isRedoAble() )
{
bool b = urdo_.isModified();
urdo_.Redo(doc_);
if( b != urdo_.isModified() )
Fire_MODIFYFLAGCHANGE();
}
}
void DocImpl::Execute( const Command& cmd )
{
bool b = urdo_.isModified();
urdo_.NewlyExec( cmd, doc_ );
if( b != urdo_.isModified() )
Fire_MODIFYFLAGCHANGE();
}
//-------------------------------------------------------------------------
// カーソル移動ヘルパー
//-------------------------------------------------------------------------
DPos DocImpl::leftOf( const DPos& dp, bool wide ) const
{
if( dp.ad == 0 )
{
// 行の先頭だが、ファイルの先頭ではない場合
// 一つ前の行の行末へ
if( dp.tl > 0 )
return DPos( dp.tl-1, len(dp.tl-1) );
return dp;
}
else if( !wide )
{
// 行の途中で、普通に1文字戻る場合
const unicode* l = tl(dp.tl);
if( dp.ad>=2 && isLowSurrogate(l[dp.ad-1]) && isHighSurrogate(l[dp.ad-2]) )
return DPos( dp.tl, dp.ad-2 );
return DPos( dp.tl, dp.ad-1 );
}
else
{
// 行の途中で、1単語分戻る場合
const uchar* f = pl(dp.tl);
ulong s = dp.ad-1;
while( (f[s]>>5)==0 && 0<=s )
--s;
return DPos( dp.tl, s );
}
}
DPos DocImpl::rightOf( const DPos& dp, bool wide ) const
{
if( dp.ad == len(dp.tl) )
{
// 行末だが、ファイルの終わりではない場合
// 一つ後の行の先頭へ
if( dp.tl < tln()-1 )
return DPos( dp.tl+1, 0 );
return dp;
}
else if( !wide )
{
// 行の途中で、普通に1文字進む場合
const unicode* l = tl(dp.tl);
// 番兵 0x007f が l の末尾にいるので長さチェックは不要
if( isHighSurrogate(l[dp.ad]) && isLowSurrogate(l[dp.ad+1]) )
return DPos( dp.tl, dp.ad+2 );
return DPos( dp.tl, dp.ad+1 );
}
else
{
// 行の途中で、普通に1単語進む場合
const uchar* f = pl(dp.tl);
const ulong e = len(dp.tl);
ulong s = dp.ad;
const ulong t = (f[s]>>5);
s += t;
if( s >= e )
s = e;
else if( t==7 || t==0 )
while( (f[s]>>5)==0 && s<e )
++s;
return DPos( dp.tl, s );
}
}
DPos DocImpl::wordStartOf( const DPos& dp ) const
{
if( dp.ad == 0 )
{
// 行の先頭
return dp;
}
else
{
// 行の途中
const uchar* f = pl(dp.tl);
ulong s = dp.ad;
while( (f[s]>>5)==0 && 0<=s )
--s;
return DPos( dp.tl, s );
}
}
//-------------------------------------------------------------------------
// 挿入・削除等の作業用関数群
//-------------------------------------------------------------------------
ulong DocImpl::getRangeLength( const DPos& s, const DPos& e )
{
// とりあえず全部足す
ulong ans=0, tl=s.tl, te=e.tl;
for( ; tl<=te; ++tl )
ans += len(tl);
// 先頭行の分を引く
ans -= s.ad;
// 最終行の分を引く
ans -= len(te) - e.ad;
// 改行コード(CRLF)の分を加える
ans += (e.tl-s.tl) * 2;
// おしまい
return ans;
}
void DocImpl::getText( unicode* buf, const DPos& s, const DPos& e )
{
if( s.tl == e.tl )
{
// 一行だけの場合
text_[s.tl].CopyAt( s.ad, e.ad-s.ad, buf );
buf[e.ad-s.ad] = L'\0';
}
else
{
// 先頭行の後ろをコピー
buf += text_[s.tl].CopyToTail( s.ad, buf );
*buf++ = L'\r', *buf++ = L'\n';
// 途中をコピー
for( ulong i=s.tl+1; i<e.tl; i++ )
{
buf += text_[i].CopyToTail( 0, buf );
*buf++ = L'\r', *buf++ = L'\n';
}
// 終了行の先頭をコピー
buf += text_[e.tl].CopyAt( 0, e.ad, buf );
*buf = L'\0';
}
}
void DocImpl::CorrectPos( DPos& pos )
{
// 正常範囲に収まるように修正
pos.tl = Min( pos.tl, tln()-1 );
pos.ad = Min( pos.ad, len(pos.tl) );
}
void DocImpl::CorrectPos( DPos& s, DPos& e )
{
// 必ずs<=eになるように修正
if( s > e )
{
ulong t;
t=s.ad, s.ad=e.ad, e.ad=t;
t=s.tl, s.tl=e.tl, e.tl=t;
}
}
bool DocImpl::DeletingOperation
( DPos& s, DPos& e, unicode*& undobuf, ulong& undosiz )
{
AutoLock lk( this );
// 位置補正
CorrectPos( s );
CorrectPos( e );
CorrectPos( s, e );
// 削除される量をカウント
undosiz = getRangeLength( s, e );
// Undo操作用バッファ確保
undobuf = new unicode[undosiz+1];
getText( undobuf, s, e );
// 削除る
if( s.tl == e.tl )
{
// 一行内削除
text_[s.tl].RemoveAt( s.ad, e.ad-s.ad );
}
else
{
// 先頭行の後ろを削除
text_[s.tl].RemoveToTail( s.ad );
// 終了行の残り部分をくっつける
text_[s.tl].InsertToTail( tl(e.tl)+e.ad, len(e.tl)-e.ad );
// いらん行を削除
text_.RemoveAt( s.tl+1, e.tl-s.tl );
}
// 再解析
return ReParse( s.tl, s.tl );
}
bool DocImpl::InsertingOperation
( DPos& s, const unicode* str, ulong len, DPos& e )
{
AutoLock lk( this );
// 位置補正
CorrectPos( s );
// よーい、どん!
e.ad = s.ad;
e.tl = s.tl;
// 指定文字列を改行で切り分ける準備
const unicode* lineStr;
ulong lineLen;
UniReader r( str, len, &lineStr, &lineLen );
// 一行目…
r.getLine();
text_[e.tl].InsertAt( e.ad, lineStr, lineLen );
e.ad += lineLen;
if( !r.isEmpty() )
{
// 二行目〜最終行
do
{
r.getLine();
text_.InsertAt( ++e.tl, new Line(lineStr,lineLen) );
} while( !r.isEmpty() );
// 一行目の最後尾に残ってた文字列を最終行へ
Line& fl = text_[s.tl];
Line& ed = text_[e.tl];
const ulong ln = fl.size()-e.ad;
if( ln )
{
ed.InsertToTail( fl.str()+e.ad, ln );
fl.RemoveToTail( e.ad );
}
// 終了位置記録
e.ad = lineLen;
}
// 再解析
return ReParse( s.tl, e.tl );
}
//-------------------------------------------------------------------------
// 挿入コマンド
//-------------------------------------------------------------------------
Insert::Insert( const DPos& s, const unicode* str, ulong len, bool del )
: stt_( s )
, buf_( str )
, len_( len )
, del_( del )
{
}
Insert::~Insert()
{
if( del_ )
delete const_cast<unicode*>(buf_);
}
Command* Insert::operator()( Document& d ) const
{
DocImpl& di = d.impl();
// 挿入
DPos s=stt_, e;
bool aa = di.InsertingOperation( s, buf_, len_, e );
// イベント発火
di.Fire_TEXTUPDATE( s, s, e, aa, true );
// 逆操作オブジェクトを返す
return new Delete( s, e );
}
//-------------------------------------------------------------------------
// 削除コマンド
//-------------------------------------------------------------------------
Delete::Delete( const DPos& s, const DPos& e )
: stt_( s )
, end_( e )
{
}
Command* Delete::operator()( Document& d ) const
{
DocImpl& di = d.impl();
// 削除
unicode* buf;
ulong siz;
DPos s = stt_, e=end_;
bool aa = di.DeletingOperation( s, e, buf, siz );
// イベント発火
di.Fire_TEXTUPDATE( s, e, s, aa, true );
// 逆操作オブジェクトを返す
return new Insert( s, buf, siz, true );
}
//-------------------------------------------------------------------------
// 置換コマンド
//-------------------------------------------------------------------------
Replace::Replace(
const DPos& s, const DPos& e, const unicode* str, ulong len, bool del )
: stt_( s )
, end_( e )
, buf_( str )
, len_( len )
, del_( del )
{
}
Replace::~Replace()
{
if( del_ )
delete const_cast<unicode*>(buf_);
}
Command* Replace::operator()( Document& d ) const
{
DocImpl& di = d.impl();
// 削除
unicode* buf;
ulong siz;
DPos s=stt_, e=end_;
bool aa = di.DeletingOperation( s, e, buf, siz );
// 挿入
DPos e2;
aa = (di.InsertingOperation( s, buf_, len_, e2 ) || aa);
// イベント発火
di.Fire_TEXTUPDATE( s, e, e2, aa, true );
// 逆操作オブジェクトを返す
return new Replace( s, e2, buf, siz, true );
}
//-------------------------------------------------------------------------
// マクロコマンド
//-------------------------------------------------------------------------
Command* MacroCommand::operator()( Document& doc ) const
{
doc.setBusyFlag();
MacroCommand* undo = new MacroCommand;
undo->arr_.ForceSize( size() );
for( ulong i=0,e=arr_.size(); i<e; ++i )
undo->arr_[e-i-1] = (*arr_[i])(doc);
doc.setBusyFlag(false);
return undo;
}
//-------------------------------------------------------------------------
// ファイルに保存
//-------------------------------------------------------------------------
void DocImpl::SaveFile( TextFileW& tf )
{
urdo_.SavedHere();
for( ulong i=0,iLast=tln()-1; i<=iLast; ++i )
tf.WriteLine( tl(i), len(i), i==iLast );
}
//-------------------------------------------------------------------------
// バッファの内容を全部破棄(暫定)
//-------------------------------------------------------------------------
void DocImpl::ClearAll()
{
// 全部削除
Execute( Delete( DPos(0,0), DPos(0xffffffff,0xffffffff) ) );
// Undo-Redoチェインをクリア
urdo_.Clear();
urdo_.SavedHere();
Fire_MODIFYFLAGCHANGE();
}
//-------------------------------------------------------------------------
// ファイルを開く(暫定)
//-------------------------------------------------------------------------
void DocImpl::OpenFile( aptr<TextFileR> tf )
{
// ToDo: マルチスレッド化
//currentOpeningFile_ = tf;
//thd().Run( *this );
// 挿入
DPos e(0,0);
unicode buf[1024];
for( ulong i=0; tf->state(); )
{
if( size_t L = tf->ReadLine( buf, countof(buf) ) )
{
DPos p(i,0xffffffff);
InsertingOperation( p, buf, (ulong)L, e );
i = tln() - 1;
}
if( tf->state() == 1 )
{
DPos p(i++,0xffffffff);
InsertingOperation( p, L"\n", 1, e );
}
}
// イベント発火
Fire_TEXTUPDATE( DPos(0,0), DPos(0,0), e, true, false );
}
void DocImpl::StartThread()
{
/*
// ToDo:
aptr<TextFileR> tf = currentOpeningFile_;
// 挿入
unicode buf[1024];
while( !isExitRequested() && tf->state() )
{
ulong i, j;
DPos s(i=tln()-1, j=len(i)), e;
if( ulong L = tf->ReadLine( buf, countof(buf) ) )
InsertingOperation( s, buf, L, e );
if( tf->state() == 1 )
InsertingOperation( DPos(i,0xffffffff), L"\n", 1, e );
// イベント発火
Fire_TEXTUPDATE( s, s, e, true, false );
}
*/
}