#ifndef _EDITWING_IP_DOC_H_
#define _EDITWING_IP_DOC_H_
#include "ewDoc.h"
using namespace ki;
#ifndef __ccdoc__
namespace editwing {
namespace doc {
#endif
//@{ @pkg editwing.Doc.Impl //@}
class Parser;
//=========================================================================
//@{
// 行バッファ構造体
//
// UCS-2ベタの形式でテキストデータを保持する。またそれと同時に、
// キーワードファイルによって指定された強調語を区別するための
// 解析処理結果用バッファも管理する。文字データに終端NULは
// 付けないが、解析作業の高速化のため、終端 U+007f が入る。
//@}
//=========================================================================
class Line : public Object
{
public:
//@{ 指定テキストで初期化 //@}
Line( const unicode* str, ulong len )
: alen_( 10>len ? 10 : len )
, len_ ( len )
, str_ ( static_cast<unicode*>( mem().Alloc((alen_+1)*2+alen_) ) )
, flg_ ( reinterpret_cast<uchar*>(str_+alen_+1) )
, commentBitReady_( false )
, isLineHeadCommented_( 0 )
{
memmove( str_, str, len*2 );
str_[ len ] = 0x007f;
}
~Line()
{
mem().DeAlloc( str_, (alen_+1)*2+alen_ );
}
//@{ テキスト挿入(指定位置に指定サイズ) //@}
void InsertAt( ulong at, const unicode* buf, ulong siz )
{
if( len_+siz > alen_ )
{
// バッファ拡張
ulong psiz = (alen_+1)*2+alen_;
alen_ = Max( alen_<<1, len_+siz );
unicode* tmpS =
static_cast<unicode*>( mem().Alloc((alen_+1)*2+alen_) );
uchar* tmpF =
reinterpret_cast<uchar*>(tmpS+alen_+1);
// コピー
memmove( tmpS, str_, at*2 );
memmove( tmpS+at+siz, str_+at, (len_-at+1)*2 );
memmove( tmpF, flg_, at );
// 古いのを削除
mem().DeAlloc( str_, psiz );
str_ = tmpS;
flg_ = tmpF;
}
else
{
memmove( str_+at+siz, str_+at, (len_-at+1)*2 );
memmove( flg_+at+siz, flg_+at, (len_-at) );
}
memmove( str_+at, buf, siz*sizeof(unicode) );
len_ += siz;
}
//@{ テキスト挿入(末尾に) //@}
void InsertToTail( const unicode* buf, ulong siz )
{
InsertAt( len_, buf, siz );
}
//@{ テキスト削除(指定位置から指定サイズ) //@}
void RemoveAt( ulong at, ulong siz )
{
memmove( str_+at, str_+at+siz, (len_-siz-at+1)*2 );
memmove( flg_+at, flg_+at+siz, (len_-siz-at) );
len_ -= siz;
}
//@{ テキスト削除(指定位置から末尾まで) //@}
void RemoveToTail( ulong at )
{
if( at < len_ )
str_[ len_=at ] = 0x007f;
}
//@{ バッファにコピー(指定位置から指定サイズ) //@}
ulong CopyAt( ulong at, ulong siz, unicode* buf )
{
memmove( buf, str_+at, siz*sizeof(unicode) );
return siz;
}
//@{ バッファにコピー(指定位置から末尾まで) //@}
ulong CopyToTail( ulong at, unicode* buf )
{
return CopyAt( at, len_-at, buf );
}
//@{ 長さ //@}
ulong size() const
{ return len_; }
//@{ テキスト //@}
unicode* str()
{ return str_; }
//@{ テキスト(const) //@}
const unicode* str() const
{ return str_; }
//@{ 解析結果 //@}
uchar* flg()
{ return flg_; }
//@{ 解析結果(const) //@}
const uchar* flg() const
{ return flg_; }
// ask
bool isCmtBitReady() const
{ return commentBitReady_; }
uchar isLineHeadCmt() const
{ return isLineHeadCommented_; }
// for doc
uchar TransitCmt( uchar start )
{
isLineHeadCommented_ = start;
commentBitReady_ = false;
return (commentTransition_>>start)&1;
}
// for parser
void SetTransitFlag( uchar flag )
{ commentTransition_ = flag; }
void CommentBitUpdated()
{ commentBitReady_ = true; }
private:
ulong alen_;
ulong len_;
unicode* str_;
uchar* flg_;
uchar isLineHeadCommented_;
uchar commentTransition_;
bool commentBitReady_;
};
//=========================================================================
//@{
// Unicodeテキスト切り分け君
//
// 行単位で処理を行うことが多いので、その行毎に分ける処理を
// 切り出した。getLine() するたびに、指定したポインタと整数変数へ
// 先頭から順に行のデータを格納して行く。
//@}
//=========================================================================
class UniReader
{
public:
//@{ 読みとり元バッファを与えて初期化 //@}
UniReader(
const unicode* str, ulong len,
const unicode** ansstr, ulong* anslen )
: ptr_ ( str )
, end_ ( str+len )
, ans_ ( ansstr )
, aln_ ( anslen )
, empty_( false ) {}
//@{ 読み終わったかどうかチェック //@}
bool isEmpty()
{ return empty_; }
//@{ 一行取得 //@}
void getLine()
{
// 次の改行の位置を取得
const unicode *p=ptr_, *e=end_;
for( ; p<e; ++p )
if( *p == L'\r' || *p == L'\n' )
break;
// 記録
*ans_ = ptr_;
*aln_ = int(p-ptr_);
// 改行コードスキップ
if( p == e )
empty_ = true;
else
if( *(p++)==L'\r'&& p<e && *p==L'\n' )
++p;
ptr_ = p;
}
private:
bool empty_;
const unicode *ptr_, *end_, **ans_;
ulong *aln_;
};
//=========================================================================
//@{
// Undo/Redo用に、Commandオブジェクトを保存しておくクラス
//@}
//=========================================================================
class UnReDoChain : public Object
{
public:
//@{ コンストラクタ //@}
UnReDoChain();
//@{ デストラクタ //@}
~UnReDoChain();
//@{ Undo実行 //@}
void Undo( Document& doc );
//@{ Redo実行 //@}
void Redo( Document& doc );
//@{ 新しいコマンドを実行 //@}
void NewlyExec( const Command& cmd, Document& doc );
//@{ 初期状態に戻す //@}
void Clear();
//@{ 保存位置フラグを現在位置にセット //@}
void SavedHere();
//@{ Undo/Redoの回数制限を指定。-1 = Infinite //@}
void SetLimit( long lim );
public:
//@{ Undo操作が可能か? //@}
bool isUndoAble() const;
//@{ Redo操作が可能か? //@}
bool isRedoAble() const;
//@{ 保存後、変更されているか? //@}
bool isModified() const;
private:
struct Node : public Object
{
Node();
Node( Command*, Node*, Node* );
~Node();
void ResetCommand( Command* cmd );
ulong ChainDelete(Node*& savedPos_ref);
Node *next_, *prev_;
Command *cmd_;
};
private:
Node headTail_;
Node* savedPos_;
Node* lastOp_;
ulong num_;
ulong limit_;
private:
NOCOPY(UnReDoChain);
};
//-------------------------------------------------------------------------
#ifndef __ccdoc__
inline void UnReDoChain::SavedHere()
{ savedPos_ = lastOp_; }
inline bool UnReDoChain::isUndoAble() const
{ return (lastOp_ != &headTail_); }
inline bool UnReDoChain::isRedoAble() const
{ return (lastOp_->next_ != &headTail_); }
inline bool UnReDoChain::isModified() const
{ return (lastOp_ != savedPos_); }
#endif // __ccdoc__
//=========================================================================
//@{
// Documentクラスの実装部分
//@}
//=========================================================================
class DocImpl : public Object, EzLockable, Runnable
{
public:
DocImpl( Document& theDoc );
~DocImpl();
//@{ 操作コマンド実行 //@}
void Execute( const Command& cmd );
//@{ キーワード定義切り替え //@}
void SetKeyword( const unicode* defbuf, ulong siz );
//@{ イベントハンドラ登録 //@}
void AddHandler( DocEvHandler* eh );
//@{ イベントハンドラ解除 //@}
void DelHandler( DocEvHandler* eh );
//@{ ファイルを開く //@}
void OpenFile( aptr<TextFileR> tf );
//@{ ファイルを保存 //@}
void SaveFile( TextFileW& tf );
//@{ 内容破棄 //@}
void ClearAll();
//@{ アンドゥ //@]
void Undo();
//@{ リドゥ //@]
void Redo();
//@{ アンドゥ回数制限 //@]
void SetUndoLimit( long lim );
//@{ 変更フラグをクリア //@}
void ClearModifyFlag();
public:
//@{ 行数 //@}
ulong tln() const;
//@{ 行バッファ //@}
const unicode* tl( ulong i ) const;
//@{ 行解析結果バッファ //@}
const uchar* pl( ulong i ) const;
//@{ 行文字数 //@}
ulong len( ulong i ) const;
//@{ 指定範囲のテキストの長さ //@}
ulong getRangeLength( const DPos& stt, const DPos& end );
//@{ 指定範囲のテキスト //@}
void getText( unicode* buf, const DPos& stt, const DPos& end );
//@{ 指定位置の単語の先頭を取得 //@}
DPos wordStartOf( const DPos& dp ) const;
//@{ 指定位置の一つ左の位置を取得 //@}
DPos leftOf( const DPos& dp, bool wide=false ) const;
//@{ 指定位置の一つ右の位置を取得 //@}
DPos rightOf( const DPos& dp, bool wide=false ) const;
//@{ アンドゥ可能? //@}
bool isUndoAble() const;
//@{ リドゥ可能? //@}
bool isRedoAble() const;
//@{ 変更済み? //@}
bool isModified() const;
private:
Document& doc_; // 自分
aptr<Parser> parser_; // 文字列解析役
gapbufobj<Line> text_; // テキストデータ
mutable storage<DocEvHandler*> pEvHan_; // イベント通知先
UnReDoChain urdo_; // アンドゥリドゥ
aptr<TextFileR> currentOpeningFile_;
private:
// 変更通知
void Fire_KEYWORDCHANGE();
void Fire_MODIFYFLAGCHANGE();
void Fire_TEXTUPDATE( const DPos& s,
const DPos& e, const DPos& e2, bool reparsed, bool nmlcmd );
// ヘルパー関数
bool ReParse( ulong s, ulong e );
void SetCommentBit( const Line& x ) const;
void CorrectPos( DPos& pos );
void CorrectPos( DPos& stt, DPos& end );
// 挿入・削除作業
bool InsertingOperation(
DPos& stt, const unicode* str, ulong len, DPos& undoend );
bool DeletingOperation(
DPos& stt, DPos& end, unicode*& undobuf, ulong& undosiz );
// パラレルリード
virtual void StartThread();
private:
NOCOPY(DocImpl);
friend class Insert;
friend class Delete;
friend class Replace;
};
//-------------------------------------------------------------------------
inline ulong DocImpl::tln() const
{ return text_.size(); }
inline const unicode* DocImpl::tl( ulong i ) const
{ return text_[i].str(); }
inline ulong DocImpl::len( ulong i ) const
{ return text_[i].size(); }
inline const uchar* DocImpl::pl( ulong i ) const
{
const Line& x = text_[i];
if( !x.isCmtBitReady() )
SetCommentBit( x );
return x.flg();
}
//=========================================================================
}} // namespace editwing::doc
#endif // _EDITWING_IP_DOC_H_