ADDED ConfigManager.cpp Index: ConfigManager.cpp ================================================================== --- ConfigManager.cpp +++ ConfigManager.cpp @@ -0,0 +1,926 @@ + +#include "stdafx.h" +#include "ConfigManager.h" +#include "rsrc/resource.h" +#include "RSearch.h" +using namespace ki; +using namespace editwing; + + + +void BootNewProcess( const TCHAR* cmd ); // in GpMain.cpp + +//------------------------------------------------------------------------- +// 設定項目管理。 +// SetDocTypeで切り替えると、文書タイプ依存の項目を自動で +// 入れ替えたり色々。 +//------------------------------------------------------------------------- + +ConfigManager::ConfigManager() +{ + // デフォルトのレイアウト設定は何よりも先に読んでおく + DocType d; + d.name.Load( IDS_DEFAULT ); + d.layfile = TEXT("default.lay"); + LoadLayout( &d ); + dtList_.Add( d ); + curDt_ = dtList_.begin(); + + // ini読み込み + LoadIni(); + SetDocTypeByName( newfileDoctype_ ); +} + +ConfigManager::~ConfigManager() +{ + // ini保存 + SaveIni(); +} + +void ConfigManager::SetDocTypeByName( const ki::String& nam ) +{ + curDt_ = dtList_.begin(); + DtList::iterator b = dtList_.begin(); + DtList::iterator e = dtList_.end(); + for( ; b!=e; ++b ) + if( b->name == nam ) + { + curDt_ = b; + break; + } + LoadLayout( &*curDt_ ); +} + +int ConfigManager::SetDocType( const Path& fname ) +{ + const unicode* uname = fname.ConvToWChar(); + + int ct = 1; + DtList::iterator i=dtList_.begin(), e=dtList_.end(); + if( fname.len() > 0 ) + { + for( ++i; i!=e; ++i, ++ct ) + if( i->pattern.len() > 0 ) + { + const unicode* upat = i->pattern.ConvToWChar(); + bool b = MatchDocType( uname, upat ); + i->pattern.FreeWCMem( upat ); + if( b ) break; + } + if( i == e ) + ct=0, i=dtList_.begin(); // 適切なのが見つからなければ[標準]。 + } + else + { + ct = 0; + } + + SetDocTypeByName( i->name ); + + fname.FreeWCMem( uname ); + return ct; +} + +bool ConfigManager::MatchDocType + ( const unicode* fname, const unicode* pat ) +{ + // pattern と fname とのマッチをとって判定… + return reg_match( pat, fname, false ); +} + + + +//------------------------------------------------------------------------- +// 設定ダイアログ関連 +//------------------------------------------------------------------------- + +struct ConfigDlg : public ki::DlgImpl +{ +private: + typedef ConfigManager::DtList::iterator DTI; + ulong curSel_; + void SaveDt() + { + DTI p=myDtl_.begin(), e=myDtl_.end(); + for( ulong ct=0; p!=e && ct!=curSel_; ++ct,++p ); + if( p==e ) return; + + TCHAR buf[256]; + SendMsgToItem(IDC_DT_PAT, WM_GETTEXT, + countof(buf),reinterpret_cast(buf)); + p->pattern = buf; + + SendMsgToItem(IDC_PAT_KWD, CB_GETLBTEXT, + SendMsgToItem(IDC_PAT_KWD, CB_GETCURSEL), + reinterpret_cast(buf) ); + p->kwdfile = buf; + + SendMsgToItem(IDC_PAT_LAY, CB_GETLBTEXT, + SendMsgToItem(IDC_PAT_LAY, CB_GETCURSEL), + reinterpret_cast(buf) ); + p->layfile = buf; + } + void SelDt(ulong i) + { + DTI p=myDtl_.begin(), e=myDtl_.end(); + for( ulong ct=0; p!=e && ct!=i; ++ct,++p ); + if( p==e ) return; + + curSel_ = i; + SendMsgToItem(IDC_DT_PAT, WM_SETTEXT, p->pattern.c_str()); + if( p->kwdfile.len()==0 ) + SendMsgToItem(IDC_PAT_KWD, CB_SETCURSEL); + else + SendMsgToItem(IDC_PAT_KWD,CB_SELECTSTRING,p->kwdfile.c_str()); + SendMsgToItem(IDC_PAT_LAY,CB_SELECTSTRING,p->layfile.c_str()); + } + void on_deldoctype() + { + ulong ct; + DTI p=myDtl_.begin(), e=myDtl_.end(); + for( ct=0; p!=e && ct!=curSel_; ++ct,++p ); + if( p==e ) return; + + String msg = TEXT("["); + msg += p->name, msg += TEXT("]"), msg += String(IDS_OKTODEL); + if( IDNO == + MsgBox( msg.c_str(), String(IDS_APPNAME).c_str(), MB_YESNO ) ) + return; + + myDtl_.Del(p); + SendMsgToItem( IDC_DOCTYPELIST, LB_DELETESTRING, ct ); + SelDt(0); + if( ct+1 == (ulong)SendMsgToItem(IDC_NEWDT,CB_GETCURSEL) ) + SendMsgToItem( IDC_NEWDT, CB_SETCURSEL ); + SendMsgToItem( IDC_NEWDT, CB_DELETESTRING, ct+1 ); + } + void on_newdoctype() + { + struct NewDocTypeDlg : public DlgImpl + { + NewDocTypeDlg(HWND wnd) + : DlgImpl(IDD_ADDDOCTYPE) { GoModal(wnd); } + virtual bool on_ok() + { + TCHAR buf[MAX_PATH]; + SendMsgToItem(IDC_NAME, WM_GETTEXT, + countof(buf),reinterpret_cast(buf)); + name = buf; + SendMsgToItem(IDC_EXT, WM_GETTEXT, + countof(buf),reinterpret_cast(buf)); + ext=buf; + return true; + } + String name; + String ext; + } dlg( hwnd() ); + if( IDOK == dlg.endcode() ) + { + ConfigManager::DocType ndt; + TCHAR buf[200]; + SendMsgToItem(IDC_PAT_KWD, CB_GETLBTEXT, + SendMsgToItem(IDC_PAT_KWD, CB_GETCURSEL), + reinterpret_cast(buf) ); + ndt.kwdfile = buf; + SendMsgToItem(IDC_PAT_LAY, CB_GETLBTEXT, + SendMsgToItem(IDC_PAT_LAY, CB_GETCURSEL), + reinterpret_cast(buf) ); + ndt.layfile = buf; + ndt.name = dlg.name; + ndt.pattern = TEXT(".*\\.")+dlg.ext+TEXT("$"); + myDtl_.Add(ndt); + SendMsgToItem( IDC_DOCTYPELIST, LB_ADDSTRING, + ndt.name.c_str() ); + SendMsgToItem( IDC_NEWDT, CB_ADDSTRING, + ndt.name.c_str() ); + } + } + +public: + ConfigDlg( ConfigManager& cfg, HWND wnd ) + : cfg_( cfg ) + , DlgImpl( IDD_CONFIG ) + , curSel_( 0xffffffff ) + { + for( DTI i=++cfg_.dtList_.begin(); i!=cfg_.dtList_.end(); ++i ) + myDtl_.Add( *i ); + GoModal( wnd ); + } + +private: + void on_init() + { + SendMsgToItem(IDC_LATEST_NUM, WM_SETTEXT, + String().SetInt(cfg_.mrus_).c_str() ); + if( cfg_.undoLimit() == -1 ) + { + SendMsgToItem(IDC_UNDOLIM1, BM_SETCHECK, BST_CHECKED); + SendMsgToItem(IDC_UNDO_CT, WM_SETTEXT, TEXT("20") ); + } + else + { + SendMsgToItem(IDC_UNDOLIM2, BM_SETCHECK, BST_CHECKED); + SendMsgToItem(IDC_UNDO_CT, WM_SETTEXT, + String().SetInt(cfg_.undoLimit()).c_str() ); + } + if( cfg_.countByUnicode() ) + { + SendMsgToItem(IDC_COUNTBYLETTER, BM_SETCHECK, BST_CHECKED); + SendMsgToItem(IDC_COUNTBYLETTER2, BM_SETCHECK); + } + else + { + SendMsgToItem(IDC_COUNTBYLETTER, BM_SETCHECK); + SendMsgToItem(IDC_COUNTBYLETTER2, BM_SETCHECK, BST_CHECKED); + } + + SendMsgToItem(IDC_TXTFILT, WM_SETTEXT, + cfg_.txtFileFilter().c_str() ); + SendMsgToItem(IDC_EXTGREP, WM_SETTEXT, + cfg_.grepExe().c_str() ); + + if( cfg_.openSame() ) + SendMsgToItem(IDC_OPENSAME, BM_SETCHECK, BST_CHECKED); + if( cfg_.rememberWindowSize_ ) + SendMsgToItem(IDC_REMSIZE, BM_SETCHECK, BST_CHECKED); + if( cfg_.rememberWindowPlace_ ) + SendMsgToItem(IDC_REMPLACE, BM_SETCHECK, BST_CHECKED); + + CharSetList& csl = cfg_.GetCharSetList(); + for(ulong i=1; iname.c_str() ); + for( DTI i=myDtl_.begin(), e=myDtl_.end(); i!=e; ++i,++nfd_cnt ) + { + SendMsgToItem( IDC_DOCTYPELIST, LB_ADDSTRING, + i->name.c_str() ); + SendMsgToItem( IDC_NEWDT, CB_ADDSTRING, + i->name.c_str() ); + if( i->name == cfg_.newfileDoctype_ ) + nfd_idx = nfd_cnt; + } + SendMsgToItem( IDC_NEWDT, CB_SETCURSEL, nfd_idx ); + + FindFile f; + WIN32_FIND_DATA fd; + f.Begin( (Path(Path::Exe)+=TEXT("type\\*.kwd")).c_str() ); + SendMsgToItem( IDC_PAT_KWD, CB_ADDSTRING, TEXT("") ); + while( f.Next(&fd) ) + SendMsgToItem( IDC_PAT_KWD, CB_ADDSTRING, fd.cFileName ); + f.Begin( (Path(Path::Exe)+=TEXT("type\\*.lay")).c_str() ); + while( f.Next(&fd) ) + SendMsgToItem( IDC_PAT_LAY, CB_ADDSTRING, fd.cFileName ); + + SelDt(0); + + SetCenter( hwnd(), ::GetParent(hwnd()) ); + } + + bool on_command( UINT cmd, UINT id, HWND ctrl ) + { + switch( cmd ) + { + case LBN_SELCHANGE: + SaveDt(); + SelDt( (ulong)SendMsgToItem( IDC_DOCTYPELIST, LB_GETCURSEL ) ); + break; + default: + TCHAR buf[256]; + switch( id ) + { + case IDC_EDITKWD: + SendMsgToItem(IDC_PAT_KWD, CB_GETLBTEXT, + SendMsgToItem(IDC_PAT_KWD, CB_GETCURSEL), + reinterpret_cast(buf) ); + if( buf[0] != TEXT('\0') ) + BootNewProcess( (TEXT("\"")+Path(Path::Exe)+ + TEXT("type\\")+buf+TEXT("\"") ).c_str()); + break; + case IDC_EDITLAY: + SendMsgToItem(IDC_PAT_LAY, CB_GETLBTEXT, + SendMsgToItem(IDC_PAT_LAY, CB_GETCURSEL), + reinterpret_cast(buf) ); + BootNewProcess( (TEXT("\"")+Path(Path::Exe)+ + TEXT("type\\")+buf+TEXT("\"") ).c_str()); + break; + case IDC_NEWDOCTYPE: + on_newdoctype(); + break; + case IDC_DELDOCTYPE: + on_deldoctype(); + break; + default: + return false; + } + break; + } + return true; + } + + bool on_ok() + { + TCHAR buf[100]; + SendMsgToItem(IDC_LATEST_NUM, WM_GETTEXT, + countof(buf),reinterpret_cast(buf)); + cfg_.mrus_ = String::GetInt(buf); + cfg_.mrus_ = Min(Max(0, cfg_.mrus_), 20); + + if( BST_CHECKED == SendMsgToItem(IDC_UNDOLIM1, BM_GETCHECK) ) + { + cfg_.undoLimit_ = -1; + } + else + { + SendMsgToItem(IDC_UNDO_CT, WM_GETTEXT, + countof(buf),reinterpret_cast(buf)); + cfg_.undoLimit_ = String::GetInt(buf); + } + + SendMsgToItem(IDC_TXTFILT, WM_GETTEXT, + countof(buf),reinterpret_cast(buf)); + cfg_.txtFilter_ = buf; + + SendMsgToItem(IDC_EXTGREP, WM_GETTEXT, + countof(buf),reinterpret_cast(buf)); + cfg_.grepExe_ = buf; + + cfg_.openSame_ = + ( BST_CHECKED==SendMsgToItem(IDC_OPENSAME, BM_GETCHECK) ); + cfg_.rememberWindowSize_ = + ( BST_CHECKED==SendMsgToItem(IDC_REMSIZE, BM_GETCHECK) ); + cfg_.rememberWindowPlace_ = + ( BST_CHECKED==SendMsgToItem(IDC_REMPLACE, BM_GETCHECK) ); + + cfg_.countbyunicode_ = + ( BST_CHECKED==SendMsgToItem(IDC_COUNTBYLETTER, BM_GETCHECK) ); + + cfg_.newfileCharset_ = cfg_.GetCharSetList()[1+SendMsgToItem(IDC_NEWCS, CB_GETCURSEL)].ID; + cfg_.newfileLB_ = (lbcode) SendMsgToItem(IDC_NEWLB, CB_GETCURSEL); + size_t nfd_idx=SendMsgToItem(IDC_NEWDT, CB_GETCURSEL), nfd_cnt=1; + cfg_.newfileDoctype_ = String(); + + SaveDt(); + cfg_.dtList_.DelAfter( ++cfg_.dtList_.begin() ); + for( DTI i=myDtl_.begin(), e=myDtl_.end(); i!=e; ++i, ++nfd_cnt ) + { + cfg_.dtList_.Add( *i ); + if( nfd_idx == nfd_cnt ) + cfg_.newfileDoctype_ = i->name; + } + return true; + } + +private: + ConfigManager& cfg_; + ConfigManager::DtList myDtl_; +}; + +bool ConfigManager::DoDialog( const ki::Window& parent ) +{ + LoadIni(); + { + ConfigDlg dlg(*this, parent.hwnd()); + if( IDOK != dlg.endcode() ) + return false; + curDt_ = dtList_.begin(); // とりあえず + } + SaveIni(); + return true; +} + + + +//------------------------------------------------------------------------- +// *.lay ファイルからの読み込み処理 +//------------------------------------------------------------------------- + +namespace { + static ulong ToByte( unicode* str ) + { + ulong c = str[0]; + if( L'a' <= str[0] ) c -= (L'a' - 10); + else if( L'A' <= str[0] ) c -= (L'A' - 10); + else c -= L'0'; + c = c*16 + str[1]; + if( L'a' <= str[1] ) c -= (L'a' - 10); + else if( L'A' <= str[1] ) c -= (L'A' - 10); + else c -= L'0'; + return c; + } + static ulong GetColor( unicode* str ) + { + return ToByte(str) + (ToByte(str+2)<<8) + (ToByte(str+4)<<16); + } + static int GetInt( unicode* str ) + { + int c = 0; + int s = 1; + if( *str == L'-' ) + s=-1, ++str; + for( ; *str!=L'\0'; ++str ) + c = c * 10 + *str - L'0'; + return c*s; + } +} + +void ConfigManager::LoadLayout( ConfigManager::DocType* dt ) +{ + // 1.省略値として… + + DtList::iterator ref = dtList_.begin(); + if( ref != dtList_.end() ) + { + // default.layがロードされていればそれを使う + dt->vc = ref->vc; + dt->wrapWidth = ref->wrapWidth; + dt->wrapType = ref->wrapType; + dt->showLN = ref->showLN; + } + else + { + // 組み込みのデフォルト設定をロード + dt->vc.SetFont( TEXT("FixedSys"), 14 ); + dt->vc.SetTabStep( 4 ); + dt->vc.color[TXT] = RGB(0,0,0); + dt->vc.color[KWD] = RGB(0,90,230); + dt->vc.color[CMT] = RGB(0,0,0); + dt->vc.color[BG] = RGB(255,255,255); + dt->vc.color[CTL] = RGB(240,200,240); + dt->vc.color[LN] = RGB(0,0,0); + dt->vc.sc[scEOF] = dt->vc.sc[scEOL]=true; + dt->vc.sc[scHSP] = dt->vc.sc[scZSP]=dt->vc.sc[scTAB]=false; + dt->wrapWidth = 80; + dt->wrapType = -1; + dt->showLN = false; + } + + // 2.*.layファイルからの読み込み + + TextFileR tf( UTF16LE ); + if( tf.Open( (Path(Path::Exe)+TEXT("type\\")+dt->layfile).c_str() ) ) + { + String fontname; + int fontsize=0; + int x; + bool clfound = false; + + unicode buf[1024], *ptr=buf+3; + while( tf.state() != 0 ) // !EOF + { + size_t len = tf.ReadLine( buf, countof(buf)-1 ); + if( len<=3 || buf[2]!=L'=' ) + continue; + buf[len] = L'\0'; + + switch( (buf[0]<<16)|buf[1] ) + { + case 0x00630074: // ct: COLOR-TEXT + dt->vc.color[TXT] = GetColor(ptr); + break; + case 0x0063006B: // ck: COLOR-KEYWORD + dt->vc.color[KWD] = GetColor(ptr); + break; + case 0x00630062: // cb: COLOR-BACKGROUND + dt->vc.color[BG ] = GetColor(ptr); + break; + case 0x00630063: // cc: COLOR-COMMENT + dt->vc.color[CMT] = GetColor(ptr); + break; + case 0x0063006E: // cn: COLOR-CONTROL + dt->vc.color[CTL] = GetColor(ptr); + break; + case 0x0063006C: // cl: COLOR-LINE + clfound = true; + dt->vc.color[LN] = GetColor(ptr); + break; + case 0x00660074: // ft: FONT + fontname = ptr; + break; + case 0x0073007A: // sz: SIZE + fontsize = GetInt(ptr); + break; + case 0x00740062: // tb: TAB + dt->vc.SetTabStep( GetInt(ptr) ); + break; + case 0x00730063: // sc: SPECIAL-CHAR + x = GetInt(ptr); + dt->vc.sc[scZSP] = (0!=x%10); x/=10; + dt->vc.sc[scHSP] = (0!=x%10); x/=10; + dt->vc.sc[scTAB] = (0!=x%10); x/=10; + dt->vc.sc[scEOL] = (0!=x%10); x/=10; + dt->vc.sc[scEOF] = (0!=x%10); + break; + case 0x00770070: // wp: WRAP-TYPE + dt->wrapType = GetInt(ptr); + break; + case 0x00770077: // ww: WRAP-WIDTH + dt->wrapWidth = GetInt(ptr); + break; + case 0x006C006E: // ln: LINE-NO + dt->showLN = (0!=GetInt(ptr)); + break; + } + } + + if( !clfound ) + dt->vc.color[LN] = dt->vc.color[TXT]; + if( fontname.len()!=0 && fontsize!=0 ) + dt->vc.SetFont( fontname.c_str(), fontsize ); + } +} + + + +//------------------------------------------------------------------------- +// *.ini ファイルからの読み込み/書き込み処理 +//------------------------------------------------------------------------- + +static const TCHAR s_sharedConfigSection[] = TEXT("SharedConfig"); + +void ConfigManager::LoadIni() +{ + { + FileW fp; + Path inipath(Path::Exe); + inipath+=Path(Path::ExeName).body(); + inipath+=TEXT(".ini"); + if( !inipath.exist() && fp.Open(inipath.c_str()) ) + { + static const char s_defaultIni[] = + "[DocType]\r\n" + "1=C/C++\r\n" + "2=C#\r\n" + "3=D\r\n" + "4=Java\r\n" + "5=HTML\r\n" + "6=CSS\r\n" + "7=Perl\r\n" + "8=Ruby\r\n" + "9=PHP\r\n" + "10=Python\r\n" + "11=Haskell\r\n" + "12=OCaml\r\n" + "13=INI\r\n" + "14=UnicodeText\r\n" + "\r\n" + "[C/C++]\r\n" + "Pattern=.*(\\.(c|cpp|cxx|cc|h|hpp)|include\\\\[^\\.]+)$\r\n" + "Keyword=C.kwd\r\n" + "Layout=program.lay\r\n" + "\r\n" + "[Java]\r\n" + "Pattern=.*\\.java$\r\n" + "Keyword=Java.kwd\r\n" + "Layout=program.lay\r\n" + "\r\n" + "[HTML]\r\n" + "Pattern=.*(\\.html|\\.htm|temporary internet files\\\\.+)$\r\n" + "Keyword=HTML.kwd\r\n" + "Layout=html.lay\r\n" + "\r\n" + "[CSS]\r\n" + "Pattern=.*\\.css$\r\n" + "Keyword=CSS.kwd\r\n" + "Layout=program.lay\r\n" + "\r\n" + "[PHP]\r\n" + "Pattern=.*\\.(php|php3|php4)$\r\n" + "Keyword=PHP.kwd\r\n" + "Layout=program.lay\r\n" + "\r\n" + "[Python]\r\n" + "Pattern=.*\\.py$\r\n" + "Keyword=Python.kwd\r\n" + "Layout=program.lay\r\n" + "\r\n" + "[Ruby]\r\n" + "Pattern=.*\\.rb$\r\n" + "Keyword=Ruby.kwd\r\n" + "Layout=program.lay\r\n" + "\r\n" + "[D]\r\n" + "Pattern=.*\\.d$\r\n" + "Keyword=D.kwd\r\n" + "Layout=program.lay\r\n" + "\r\n" + "[Haskell]\r\n" + "Pattern=.*\\.l?hs$\r\n" + "Keyword=Haskell.kwd\r\n" + "Layout=program.lay\r\n" + "\r\n" + "[OCaml]\r\n" + "Pattern=.*\\.mli?$\r\n" + "Keyword=OCaml.kwd\r\n" + "Layout=program.lay\r\n" + "\r\n" + "[C#]\r\n" + "Pattern=.*\\.cs$\r\n" + "Keyword=C#.kwd\r\n" + "Layout=program.lay\r\n" + "\r\n" + "[INI]\r\n" + "Pattern=.*\\.ini$\r\n" + "Keyword=ini.kwd\r\n" + "\r\n" + "[Perl]\r\n" + "Pattern=.*\\.(pl|pm|cgi)$\r\n" + "Keyword=perl.kwd\r\n" + "Layout=program.lay\r\n" + "\r\n" + "[UnicodeText]\r\n" + "Layout=unitext.lay\r\n" + "\r\n"; + fp.Write( s_defaultIni, sizeof(s_defaultIni)-1 ); + } + } + + // 共通の設定の読み取りセクション + sharedConfigMode_ = ini_.HasSectionEnabled( s_sharedConfigSection ); + if( sharedConfigMode_ ) + ini_.SetSection( s_sharedConfigSection ); + else + ini_.SetSectionAsUserName(); + + // 共通の設定 + undoLimit_ = ini_.GetInt( TEXT("UndoLimit"), -1 ); + txtFilter_ = ini_.GetStr( TEXT("TxtFilter"), + TEXT("*.txt;*.htm;*.html;*.css;*.js;*.d;*.c;*.cpp;*.cc;*.cxx;*.h;*.hpp;*.php;*.php3;*.ini") ); + grepExe_ = ini_.GetStr( TEXT("GrepExe"), TEXT("") ); + openSame_ = ini_.GetBool( TEXT("OpenSame"), false ); + countbyunicode_ = ini_.GetBool( TEXT("CountUni"), false ); + showStatusBar_ = ini_.GetBool( TEXT("StatusBar"), true ); + + // wnd + rememberWindowSize_ = ini_.GetBool( TEXT("RememberWindowSize"), false ); + rememberWindowPlace_ = ini_.GetBool( TEXT("RememberWindowPos"), false ); + wndX_ = ini_.GetInt( TEXT("WndX"), CW_USEDEFAULT ); + wndY_ = ini_.GetInt( TEXT("WndY"), CW_USEDEFAULT ); + wndW_ = ini_.GetInt( TEXT("WndW"), CW_USEDEFAULT ); + wndH_ = ini_.GetInt( TEXT("WndH"), CW_USEDEFAULT ); + wndM_ = ini_.GetBool( TEXT("WndM"), false ); + + // TODO: MRU + mrus_ = ini_.GetInt( TEXT("MRU"), 4 ); + mrus_ = Min(Max(0, mrus_), 20); + + // 新規ファイル関係 + newfileCharset_ = ini_.GetInt( TEXT("NewfileCharset"), charSets_.defaultCs() ); + if(newfileCharset_ == -1) newfileCharset_ = 1252; // 1.07.4 bugfix + newfileDoctype_ = ini_.GetStr( TEXT("NewfileDoctype"), String() ); + newfileLB_ = (lbcode) ini_.GetInt( TEXT("NewfileLB"), CRLF ); + + // 文書タイプリストの0番以外のクリア + dtList_.DelAfter( ++dtList_.begin() ); + + String s, r; + for( int i=1; true; ++i ) + { + // 文書タイプ名を読み込み + ini_.SetSection( TEXT("DocType") ); + s.SetInt(i); + r = ini_.GetStr( s.c_str(), String() ); + if( r.len() == 0 ) + break; + + // その文書タイプを実際に読み込み + ini_.SetSection( r.c_str() ); + { + DocType d; + d.name = r; + d.layfile = ini_.GetStr( TEXT("Layout"),TEXT("default.lay")); + d.kwdfile = ini_.GetStr( TEXT("Keyword"), String() ); + d.pattern = ini_.GetStr( TEXT("Pattern"), String() ); + dtList_.Add( d ); + } + } +} + +void ConfigManager::SaveIni() +{ + { + Path inipath(Path::Exe); + inipath+=Path(Path::ExeName).body(); + inipath+=TEXT(".ini"); + if( inipath.isReadOnly() ) + return; + } + + // 共通の設定の書き込みセクション + if( sharedConfigMode_ ) + ini_.SetSection( s_sharedConfigSection ); + else + ini_.SetSectionAsUserName(); + + // 共通の設定 + ini_.PutInt( TEXT("UndoLimit"), undoLimit_ ); + ini_.PutStr( TEXT("TxtFilter"), txtFilter_.c_str() ); + ini_.PutStr( TEXT("GrepExe"), grepExe_.c_str() ); + ini_.PutBool( TEXT("OpenSame"), openSame_ ); + ini_.PutBool( TEXT("CountUni"), countbyunicode_ ); + ini_.PutBool( TEXT("StatusBar"), showStatusBar_ ); + + // Wnd + ini_.PutBool( TEXT("RememberWindowSize"), rememberWindowSize_ ); + ini_.PutBool( TEXT("RememberWindowPos"), rememberWindowPlace_ ); + ini_.PutInt( TEXT("WndX"), wndX_ ); + ini_.PutInt( TEXT("WndY"), wndY_ ); + ini_.PutInt( TEXT("WndW"), wndW_ ); + ini_.PutInt( TEXT("WndH"), wndH_ ); + ini_.PutBool( TEXT("WndM"), wndM_ ); + + // 新規ファイル関係 + ini_.PutInt( TEXT("NewfileCharset"), newfileCharset_ ); + ini_.PutStr( TEXT("NewfileDoctype"), newfileDoctype_.c_str() ); + ini_.PutInt( TEXT("NewfileLB"), newfileLB_ ); + + // MRU + ini_.PutInt( TEXT("MRU"), mrus_ ); + + // DocType + for(DtList::iterator i=++dtList_.begin(); i!=dtList_.end(); ++i ) + { + ini_.SetSection( i->name.c_str() ); + ini_.PutStr( TEXT("Pattern"), i->pattern.c_str() ); + ini_.PutStr( TEXT("Keyword"), i->kwdfile.c_str() ); + ini_.PutStr( TEXT("Layout"), i->layfile.c_str() ); + } + + ulong ct=1; + ini_.SetSection( TEXT("DocType") ); + for(DtList::iterator i=++dtList_.begin(); i!=dtList_.end(); ++i,++ct) + ini_.PutStr( String().SetInt(ct).c_str(), i->name.c_str() ); + ini_.PutStr( String().SetInt(ct).c_str(), TEXT("") ); +} + + + +//------------------------------------------------------------------------- +// [最近使ったファイル]関係 +//------------------------------------------------------------------------- + +namespace { + static const TCHAR* const s_mrulock = TEXT("GreenPad_MRUMutex"); +} + +void ConfigManager::AddMRU( const ki::Path& fname ) +{ + Mutex mx(s_mrulock); + + // メモリ内のMRUリストを更新 + { + int i; + for( i=0; i0; --i ) + mru_[i] = mru_[i-1]; + mru_[0] = fname; + } + + // iniへ保存 + { + ini_.SetSectionAsUserName(); + const String key = TEXT("MRU"); + for( int i=0; i=mrus_ || mru_[i].len()==0 ) + { + if( i==0 ) + { + mi.fMask |= MIIM_STATE; + mi.wID = id; + mi.fState = MFS_DISABLED; + mi.dwTypeData = TEXT("no files"); + mi.cch = 0; + ::InsertMenuItem( m, 0, MF_BYPOSITION, &mi ); + } + break; + } + String cpt = mru_[i].CompactIfPossible(60); + mi.wID = id + i; + mi.dwTypeData = const_cast(cpt.c_str()); + mi.cch = cpt.len(); + ::InsertMenuItem( m, i, MF_BYPOSITION, &mi ); + } +} + +Path ConfigManager::GetMRU( int no ) const +{ + return (0<=no && nogetPos(&rc); + WINDOWPLACEMENT wp = {sizeof(wp)}; + ::GetWindowPlacement( wnd->hwnd(), &wp ); + + if( wp.showCmd==SW_SHOWNORMAL || wp.showCmd == SW_MAXIMIZE ) + wndM_ = (wp.showCmd == SW_MAXIMIZE); + if( wp.showCmd==SW_SHOWNORMAL ) + { + wndX_ = rc.left; + wndY_ = rc.top; + wndW_ = rc.right- rc.left; + wndH_ = rc.bottom - rc.top; + } + //if( this->rememberWindowPlace_ || this->rememberWindowSize_ ) + // SaveIni(); +} + +//------------------------------------------------------------------------- +// [文書タイプ]サブメニューの作成 +//------------------------------------------------------------------------- + +void ConfigManager::SetDocTypeMenu( HMENU m, UINT idstart ) +{ + // 全項目を削除 + while( ::DeleteMenu( m, 0, MF_BYPOSITION ) ); + + // 順に追加 + MENUITEMINFO mi = { sizeof(MENUITEMINFO) }; + mi.fMask = MIIM_ID | MIIM_STATE | MIIM_TYPE; + mi.fType = MFT_STRING | MFT_RADIOCHECK; + + DtList::iterator i=dtList_.begin(), e=dtList_.end(); + for( int ct=0; i!=e; ++i, ++ct ) + { + mi.wID = idstart + ct; + mi.fState = (i==curDt_ ? MFS_CHECKED : MFS_UNCHECKED); + mi.dwTypeData = const_cast(i->name.c_str()); + mi.cch = i->name.len(); + ::InsertMenuItem( m, ct, MF_BYPOSITION, &mi ); + } +} + +void ConfigManager::SetDocTypeByMenu( int pos, HMENU m ) +{ + MENUITEMINFO mi = { sizeof(MENUITEMINFO) }; + mi.fMask = MIIM_STATE; + + DtList::iterator i=dtList_.begin(), e=dtList_.end(); + for( int ct=0; i!=e; ++i, ++ct ) + { + mi.fState = (ct==pos ? MFS_CHECKED : MFS_UNCHECKED); + ::SetMenuItemInfo( m, ct, MF_BYPOSITION, &mi ); + if( ct == pos ) + { + curDt_ = i; + LoadLayout( &*curDt_ ); + } + } +} + +void ConfigManager::CheckMenu( HMENU m, int pos ) +{ + MENUITEMINFO mi = { sizeof(MENUITEMINFO) }; + mi.fMask = MIIM_STATE; + + DtList::iterator i=dtList_.begin(), e=dtList_.end(); + for( int ct=0; i!=e; ++i, ++ct ) + { + mi.fState = (ct==pos ? MFS_CHECKED : MFS_UNCHECKED); + ::SetMenuItemInfo( m, ct, MF_BYPOSITION, &mi ); + } +} + ADDED ConfigManager.h Index: ConfigManager.h ================================================================== --- ConfigManager.h +++ ConfigManager.h @@ -0,0 +1,247 @@ +#ifndef AFX_ONFIGMANAGER_H__9243DE9D_0F70_40F8_8F90_55436B952B37__INCLUDED_ +#define AFX_ONFIGMANAGER_H__9243DE9D_0F70_40F8_8F90_55436B952B37__INCLUDED_ +#include "editwing/editwing.h" +#include "OpenSaveDlg.h" + + + +// アプリケーションメッセージ +#define GPM_MRUCHANGED WM_APP+0 + +//========================================================================= +//@{ @pkg Gp.Main //@} +//@{ +// 設定の一元管理 +// +// SetDocTypeで切り替えると、文書タイプ依存の項目を内部で +// 適切に切り替えたり色々します。 +//@} +//========================================================================= + +class ConfigManager : public ki::Object +{ +public: + + ConfigManager(); + ~ConfigManager(); + + //@{ 指定した名前のファイル用の文書タイプをロード //@} + int SetDocType( const ki::Path& fname ); + + //@{ 指定した番号の文書タイプをロード //@} + void SetDocTypeByMenu( int pos, HMENU m ); + + //@{ 指定した名前の文書タイプをロード //@} + void SetDocTypeByName( const ki::String& nam ); + + //@{ メニュー項目作成 //@} + void SetDocTypeMenu( HMENU m, UINT idstart ); + + //@{ メニュー項目のチェック修正 //@} + void CheckMenu( HMENU m, int pos ); + + //@{ 設定ダイアログ表示 //@} + bool DoDialog( const ki::Window& parent ); + + //@{ 生のiniファイル操作オブジェクトを取得 //@} + ki::IniFile& getImpl(); + +public: + + //@{ Undo回数制限値 //@} + int undoLimit() const; + + //@{ 文字数のカウント方法 //@} + bool countByUnicode() const; + + //@{ 開く/保存ダイアログに出すフィルタの設定 //@} + const ki::String& txtFileFilter() const; + + //@{ 文字数指定時の折り返し文字数 //@} + int wrapWidth() const; + + //@{ 折り返し方法 //@} + int wrapType() const; + + //@{ 行番号表示する? //@} + bool showLN() const; + + //@{ 表示色・フォントなど //@} + const editwing::VConfig& vConfig() const; + + //@{ キーワードファイル名(フルパス) //@} + ki::Path kwdFile() const; + + //@{ Grep用外部実行ファイル名 //@} + const ki::Path& grepExe() const; + + //@{ 同じウインドウで開くモード //@} + bool openSame() const; + + //@{ ステータスバー表示 //@} + bool showStatusBar() const; + void ShowStatusBarSwitch(); + +public: + //@{ 新規ファイルの文字コードindex //@} + int GetNewfileCsi() const; + + //@{ 新規ファイルの改行コード //@} + ki::lbcode GetNewfileLB() const; + +public: + //@{ [最近使ったファイル]へ追加 //@} + void AddMRU( const ki::Path& fname ); + + //@{ [最近使ったファイル]メニューの構築 //@} + void SetUpMRUMenu( HMENU m, UINT id ); + + //@{ [最近使ったファイル]取得 //@} + ki::Path GetMRU( int no ) const; + + //@{ 対応文字セットリスト取得 //@} + CharSetList& GetCharSetList(); + +public: + //@{ ウインドウ位置・サイズ復元処理 //@} + int GetWndX() const; + int GetWndY() const; + int GetWndW() const; + int GetWndH() const; + bool GetWndM() const; + void RememberWnd( ki::Window* wnd ); + +private: + + ki::IniFile ini_; + bool sharedConfigMode_; + CharSetList charSets_; + + // 全体的な設定 + int undoLimit_; + ki::String txtFilter_; + ki::Path grepExe_; + bool openSame_; + bool countbyunicode_; + bool showStatusBar_; + bool rememberWindowSize_; + bool rememberWindowPlace_; + + // ウインドウサイズ記憶 + bool wndM_; // maximized? + int wndX_, wndY_, wndW_, wndH_; + + // 文書タイプのリスト + struct DocType + { + // 定義ファイル名など + ki::String name; + ki::String pattern; + ki::String kwdfile; + ki::String layfile; + + // 設定項目 + editwing::VConfig vc; + int wrapType; + int wrapWidth; + bool showLN; + }; + typedef ki::olist DtList; + + DtList dtList_; + DtList::iterator curDt_; + + // 最近使ったファイルのリスト + int mrus_; + ki::Path mru_[20]; + + // 新規ファイル関係 + int newfileCharset_; + ki::String newfileDoctype_; + ki::lbcode newfileLB_; + +private: + + void LoadIni(); + void SaveIni(); + void LoadLayout( DocType* dt ); + bool MatchDocType( const unicode* fname, const unicode* pat ); + +private: + + friend struct ConfigDlg; + NOCOPY(ConfigManager); +}; + + + +//------------------------------------------------------------------------- +#ifndef __ccdoc__ + +inline int ConfigManager::undoLimit() const + { return undoLimit_; } + +inline const ki::String& ConfigManager::txtFileFilter() const + { return txtFilter_; } + +inline int ConfigManager::wrapWidth() const + { return curDt_->wrapWidth; } + +inline int ConfigManager::wrapType() const + { return curDt_->wrapType>0 ? wrapWidth() : curDt_->wrapType; } + +inline bool ConfigManager::showLN() const + { return curDt_->showLN; } + +inline const editwing::VConfig& ConfigManager::vConfig() const + { return curDt_->vc; } + +inline ki::Path ConfigManager::kwdFile() const + { return ki::Path(ki::Path::Exe)+TEXT("type\\")+curDt_->kwdfile; } + +inline const ki::Path& ConfigManager::grepExe() const + { return grepExe_; } + +inline bool ConfigManager::openSame() const + { return openSame_; } + +inline bool ConfigManager::showStatusBar() const + { return showStatusBar_; } + +inline void ConfigManager::ShowStatusBarSwitch() + { showStatusBar_ = !showStatusBar_; SaveIni(); } + +inline bool ConfigManager::countByUnicode() const + { return countbyunicode_; } + +inline ki::IniFile& ConfigManager::getImpl() + { return ini_; } + +inline CharSetList& ConfigManager::GetCharSetList() + { return charSets_; } + +inline int ConfigManager::GetNewfileCsi() const + { return charSets_.findCsi( newfileCharset_ ); } + +inline ki::lbcode ConfigManager::GetNewfileLB() const + { return newfileLB_; } + +inline int ConfigManager::GetWndX() const + { return rememberWindowPlace_ ? wndX_ : CW_USEDEFAULT; } + +inline int ConfigManager::GetWndY() const + { return rememberWindowPlace_ ? wndY_ : CW_USEDEFAULT; } + +inline int ConfigManager::GetWndW() const + { return rememberWindowSize_ ? wndW_ : CW_USEDEFAULT; } + +inline int ConfigManager::GetWndH() const + { return rememberWindowSize_ ? wndH_ : CW_USEDEFAULT; } + +inline bool ConfigManager::GetWndM() const + { return rememberWindowSize_ & wndM_; } + +//========================================================================= + +#endif // __ccdoc__ +#endif // AFX_ONFIGMANAGER_H__9243DE9D_0F70_40F8_8F90_55436B952B37__INCLUDED_ ADDED GpMain.cpp Index: GpMain.cpp ================================================================== --- GpMain.cpp +++ GpMain.cpp @@ -0,0 +1,987 @@ +#include "stdafx.h" +#include "rsrc/resource.h" +#include "GpMain.h" +using namespace ki; +using namespace editwing; + + + +//------------------------------------------------------------------------- +// 新規プロセス起動 +//------------------------------------------------------------------------- + +void BootNewProcess( const TCHAR* cmd = TEXT("") ) +{ + STARTUPINFO sti; + PROCESS_INFORMATION psi; + ::GetStartupInfo( &sti ); + + String fcmd = Path(Path::ExeName).BeShortStyle(); + fcmd += ' '; + fcmd += cmd; + + if( ::CreateProcess( NULL, const_cast(fcmd.c_str()), + NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, + &sti, &psi ) ) + { + ::CloseHandle( psi.hThread ); + ::CloseHandle( psi.hProcess ); + } +} + + + +//------------------------------------------------------------------------- +// ステータスバー制御 +//------------------------------------------------------------------------- + +inline GpStBar::GpStBar() + : str_(NULL) + , lb_(2) +{ +} + +inline void GpStBar::SetCsText( const TCHAR* str ) +{ + // 文字コード表示領域にSetTextする + SetText( str_=str, 1 ); +} + +inline void GpStBar::SetLbText( int lb ) +{ + // 改行コード表示領域にSetTextする + static const TCHAR* const lbstr[] = {TEXT("CR"),TEXT("LF"),TEXT("CRLF")}; + SetText( lbstr[lb_=lb], 2 ); +} + +int GpStBar::AutoResize( bool maximized ) +{ + // 文字コード表示領域を確保しつつリサイズ + int h = StatusBar::AutoResize( maximized ); + int w[] = { width()-5, width()-5, width()-5 }; + + HDC dc = ::GetDC( hwnd() ); + SIZE s; + if( ::GetTextExtentPoint32( dc, TEXT("BBBBM"), 5, &s ) ) + w[1] = w[2] - s.cx; + if( ::GetTextExtentPoint32( dc, TEXT("BBBBB"), 5, &s ) ) + w[0] = w[1] - s.cx; + ::ReleaseDC( hwnd(), dc ); + + SetParts( countof(w), w ); + SetCsText( str_ ); + SetLbText( lb_ ); + return h; +} + + + +//------------------------------------------------------------------------- +// ディスパッチャ +//------------------------------------------------------------------------- + +LRESULT GreenPadWnd::on_message( UINT msg, WPARAM wp, LPARAM lp ) +{ + switch( msg ) + { + // アクティブ化。EditCtrlにフォーカスを。 + case WM_ACTIVATE: + if( LOWORD(wp) != WA_INACTIVE ) + edit_.SetFocus(); + break; + + // サイズ変更。子窓を適当に移動。 + case WM_SIZE: + if( wp==SIZE_MAXIMIZED || wp==SIZE_RESTORED ) + { + int ht = stb_.AutoResize( wp==SIZE_MAXIMIZED ); + edit_.MoveTo( 0, 0, LOWORD(lp), HIWORD(lp)-ht ); + cfg_.RememberWnd(this); + } + break; + + // ウインドウ移動 + case WM_MOVE: + { + RECT rc; + getPos(&rc); + cfg_.RememberWnd(this); + } + break; + + // システムコマンド。終了ボタンとか。 + case WM_SYSCOMMAND: + if( wp==SC_CLOSE || wp==SC_DEFAULT ) + on_exit(); + else + return WndImpl::on_message( msg, wp, lp ); + break; + + // 右クリックメニュー + case WM_CONTEXTMENU: + if( reinterpret_cast(wp) == edit_.hwnd() ) + ::TrackPopupMenu( + ::GetSubMenu( ::GetMenu(hwnd()), 1 ), // 編集メニュー表示 + TPM_LEFTALIGN|TPM_TOPALIGN|TPM_LEFTBUTTON|TPM_RIGHTBUTTON, + LOWORD(lp), HIWORD(lp), 0, hwnd(), NULL ); + else + return WndImpl::on_message( msg, wp, lp ); + break; + + // メニューのグレーアウト処理 + case WM_INITMENU: + case WM_INITMENUPOPUP: + on_initmenu( reinterpret_cast(wp), msg==WM_INITMENUPOPUP ); + break; + + // D&D + case WM_DROPFILES: + on_drop( reinterpret_cast(wp) ); + break; + + // MRU + case GPM_MRUCHANGED: + SetupMRUMenu(); + break; + + // その他 + default: + return WndImpl::on_message( msg, wp, lp ); + } + + return 0; +} + +bool GreenPadWnd::on_command( UINT id, HWND ctrl ) +{ + switch( id ) + { + // Window + case ID_CMD_NEXTWINDOW: on_nextwnd(); break; + case ID_CMD_PREVWINDOW: on_prevwnd(); break; + + // File + case ID_CMD_NEWFILE: on_newfile(); break; + case ID_CMD_OPENFILE: on_openfile(); break; + case ID_CMD_REOPENFILE: on_reopenfile();break; + case ID_CMD_SAVEFILE: on_savefile(); break; + case ID_CMD_SAVEFILEAS: on_savefileas();break; + case ID_CMD_EXIT: on_exit(); break; + + // Edit + case ID_CMD_UNDO: edit_.getDoc().Undo(); break; + case ID_CMD_REDO: edit_.getDoc().Redo(); break; + case ID_CMD_CUT: edit_.getCursor().Cut(); break; + case ID_CMD_COPY: edit_.getCursor().Copy(); break; + case ID_CMD_PASTE: edit_.getCursor().Paste(); break; + case ID_CMD_DELETE: if( edit_.getCursor().isSelected() ) + edit_.getCursor().Del(); break; + case ID_CMD_SELECTALL: edit_.getCursor().Home(true,false); + edit_.getCursor().End(true,true); break; + case ID_CMD_DATETIME: on_datetime(); break; + + // Search + case ID_CMD_FIND: search_.ShowDlg(); break; + case ID_CMD_FINDNEXT: search_.FindNext(); break; + case ID_CMD_FINDPREV: search_.FindPrev(); break; + case ID_CMD_JUMP: on_jump(); break; + case ID_CMD_GREP: on_grep();break; + + // View + case ID_CMD_NOWRAP: edit_.getView().SetWrapType( wrap_=-1 ); break; + case ID_CMD_WRAPWIDTH: edit_.getView().SetWrapType( wrap_=cfg_.wrapWidth() ); break; + case ID_CMD_WRAPWINDOW: edit_.getView().SetWrapType( wrap_=0 ); break; + case ID_CMD_CONFIG: on_config(); break; + case ID_CMD_STATUSBAR: on_statusBar(); break; + + // DocType + default: if( ID_CMD_DOCTYPE <= id ) { + on_doctype( id - ID_CMD_DOCTYPE ); + break; + } else if( ID_CMD_MRU <= id ) { + on_mru( id - ID_CMD_MRU ); + break; + } + + // Default + return false; + } + return true; +} + +bool GreenPadWnd::PreTranslateMessage( MSG* msg ) +{ + // 苦肉の策^^; + if( search_.TrapMsg(msg) ) + return true; + // キーボードショートカット処理 + return 0 != ::TranslateAccelerator( hwnd(), accel_, msg ); +} + + + +//------------------------------------------------------------------------- +// コマンド処理 +//------------------------------------------------------------------------- + +void GreenPadWnd::on_dirtyflag_change( bool ) +{ + UpdateWindowName(); +} + +void GreenPadWnd::on_newfile() +{ + BootNewProcess(); +} + +void GreenPadWnd::on_openfile() +{ + Path fn; + int cs; + if( ShowOpenDlg( &fn, &cs ) ) + Open( fn, cs ); +} + +void GreenPadWnd::on_reopenfile() +{ + if( !isUntitled() ) + { + ReopenDlg dlg( charSets_, csi_ ); + dlg.GoModal( hwnd() ); + if( dlg.endcode()==IDOK && AskToSave() ) + OpenByMyself( filename_, charSets_[dlg.csi()].ID, false ); + } +} + +void GreenPadWnd::on_savefile() +{ + Save_showDlgIfNeeded(); +} + +void GreenPadWnd::on_savefileas() +{ + if( ShowSaveDlg() ) + { + Save(); + ReloadConfig(); // 文書タイプに応じて表示を更新 + } +} + +void GreenPadWnd::on_exit() +{ + search_.SaveToINI( cfg_.getImpl() ); + if( AskToSave() ) + Destroy(); +} + +void GreenPadWnd::on_initmenu( HMENU menu, bool editmenu_only ) +{ + LOGGER("GreenPadWnd::ReloadConfig on_initmenu begin"); + MENUITEMINFO mi = { sizeof(MENUITEMINFO), MIIM_STATE }; + + mi.fState = + (edit_.getCursor().isSelected() ? MFS_ENABLED : MFS_DISABLED); + SetMenuItemInfo( menu, ID_CMD_CUT, FALSE, &mi ); + SetMenuItemInfo( menu, ID_CMD_COPY, FALSE, &mi ); + SetMenuItemInfo( menu, ID_CMD_DELETE, FALSE, &mi ); + + mi.fState = + (edit_.getDoc().isUndoAble() ? MFS_ENABLED : MFS_DISABLED); + SetMenuItemInfo( menu, ID_CMD_UNDO, FALSE, &mi ); + + mi.fState = + (edit_.getDoc().isRedoAble() ? MFS_ENABLED : MFS_DISABLED); + SetMenuItemInfo( menu, ID_CMD_REDO, FALSE, &mi ); + + if( editmenu_only ) + { + LOGGER("GreenPadWnd::ReloadConfig on_initmenu end"); + return; + } + + mi.fState = (isUntitled() || edit_.getDoc().isModified() + ? MFS_ENABLED : MFS_DISABLED); + SetMenuItemInfo( menu, ID_CMD_SAVEFILE, FALSE, &mi ); + + mi.fState = + (!isUntitled() ? MFS_ENABLED : MFS_DISABLED); + SetMenuItemInfo( menu, ID_CMD_REOPENFILE, FALSE, &mi ); + + mi.fState = + (cfg_.grepExe().len()>0 ? MFS_ENABLED : MFS_DISABLED); + SetMenuItemInfo( menu, ID_CMD_GREP, FALSE, &mi ); + + UINT id = (wrap_==-1 ? ID_CMD_NOWRAP + : (wrap_>0 ? ID_CMD_WRAPWIDTH : ID_CMD_WRAPWINDOW)); + ::CheckMenuRadioItem( + menu, ID_CMD_NOWRAP, ID_CMD_WRAPWINDOW, id, MF_BYCOMMAND ); + + ::CheckMenuItem( menu, ID_CMD_STATUSBAR, + cfg_.showStatusBar()?MFS_CHECKED:MFS_UNCHECKED ); + LOGGER("GreenPadWnd::ReloadConfig on_initmenu end"); +} + +void GreenPadWnd::on_drop( HDROP hd ) +{ + UINT iMax = ::DragQueryFile( hd, 0xffffffff, NULL, 0 ); + for( UINT i=0; i(fcmd.c_str()), + NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, + &sti, &psi ) ) + { + ::CloseHandle( psi.hThread ); + ::CloseHandle( psi.hProcess ); + } + } +} + +void GreenPadWnd::on_datetime() +{ + TCHAR buf[255], tmp[255]; + ::GetTimeFormat + ( LOCALE_USER_DEFAULT, 0, NULL, TEXT("HH:mm "), buf, countof(buf)); + ::GetDateFormat + ( LOCALE_USER_DEFAULT, 0, NULL, TEXT("yy/MM/dd"),tmp,countof(tmp)); + ::lstrcat( buf, tmp ); + edit_.getCursor().Input( buf, ::lstrlen(buf) ); +} + +void GreenPadWnd::on_doctype( int no ) +{ + if( HMENU m = ::GetSubMenu( ::GetSubMenu(::GetMenu(hwnd()),3),4 ) ) + { + cfg_.SetDocTypeByMenu( no, m ); + ReloadConfig( true ); + } +} + +void GreenPadWnd::on_config() +{ + if( cfg_.DoDialog(*this) ) + { + SetupSubMenu(); + SetupMRUMenu(); + ReloadConfig(false); + } +} + +static inline void MyShowWnd( HWND wnd ) +{ + if( ::IsIconic(wnd) ) + ::ShowWindow( wnd, SW_RESTORE ); + ::BringWindowToTop( wnd ); +} + +void GreenPadWnd::on_nextwnd() +{ + if( HWND next = ::FindWindowEx( NULL, hwnd(), className_, NULL ) ) + { + HWND last=next, pos; + while( last != NULL ) + last = ::FindWindowEx( NULL, pos=last, className_, NULL ); + if( pos != next ) + ::SetWindowPos( hwnd(), pos, + 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW ); + MyShowWnd( next ); + } +} + +void GreenPadWnd::on_prevwnd() +{ + HWND pos=NULL, next=::FindWindowEx( NULL,NULL,className_,NULL ); + if( next==hwnd() ) + { + while( next != NULL ) + next = ::FindWindowEx( NULL,pos=next,className_,NULL ); + if( pos!=hwnd()) + MyShowWnd( pos ); + } + else + { + while( next!=hwnd() && next!=NULL ) + next = ::FindWindowEx( NULL,pos=next,className_,NULL ); + if( next!=NULL ) + MyShowWnd( pos ); + } +} + +void GreenPadWnd::on_statusBar() +{ + stb_.SetStatusBarVisible( !stb_.isStatusBarVisible() ); + cfg_.ShowStatusBarSwitch(); + + WINDOWPLACEMENT wp = {sizeof(wp)}; + ::GetWindowPlacement( hwnd(), &wp ); + if( wp.showCmd != SW_MINIMIZE ) + { + const int ht = stb_.AutoResize( wp.showCmd == SW_MAXIMIZE ); + RECT rc; + getClientRect(&rc); + edit_.MoveTo( 0, 0, rc.right, rc.bottom-ht ); + } +} + +void GreenPadWnd::on_move( const DPos& c, const DPos& s ) +{ + static int busy_cnt = 0; + if( edit_.getDoc().isBusy() && ((++busy_cnt)&0xff) ) + return; + + ulong cad = c.ad; + if( ! cfg_.countByUnicode() ) + { + // ShiftJIS風のByte数カウント + const unicode* cu = edit_.getDoc().tl(c.tl); + const ulong tab = cfg_.vConfig().tabstep; + cad = 0; + for( ulong i=0; i filt = OpenFileDlg::ConnectWithNull(flst,countof(flst)); + + OpenFileDlg ofd( charSets_ ); + bool ok = ofd.DoModal( hwnd(), filt.get(), filename_.c_str() ); + if( ok ) + { + *fn = ofd.filename(); + *cs = charSets_[ofd.csi()].ID; + } + + return ok; +} + +bool GreenPadWnd::Open( const ki::Path& fn, int cs ) +{ + if( isUntitled() && !edit_.getDoc().isModified() ) + { + // 無題で無変更だったら自分で開く + return OpenByMyself( fn, cs ); + } + else + { + // 同じ窓で開くモードならそうする + if( cfg_.openSame() ) + return ( AskToSave() ? OpenByMyself( fn, cs ) : true ); + + // そうでなければ他へ回す + String + cmd = TEXT("-c"); + cmd += String().SetInt( cs ); + cmd += TEXT(" \""); + cmd += fn; + cmd += TEXT('\"'); + BootNewProcess( cmd.c_str() ); + return true; + } +} + +bool GreenPadWnd::OpenByMyself( const ki::Path& fn, int cs, bool needReConf ) +{ + // ファイルを開けなかったらそこでおしまい。 + aptr tf( new TextFileR(cs) ); + if( !tf->Open( fn.c_str() ) ) + { + // ERROR! + MsgBox( String(IDS_OPENERROR).c_str() ); + return false; + } + + // 自分内部の管理情報を更新 + if( fn[0]==TEXT('\\') || fn[1]==TEXT(':') ) + filename_ = fn; + else + filename_ = Path( Path::Cur ) + fn; + if( tf->size() ) + { + csi_ = charSets_.findCsi( tf->codepage() ); + if( tf->nolb_found() ) + lb_ = cfg_.GetNewfileLB(); + else + lb_ = tf->linebreak(); + } + else + { // 空ファイルの場合は新規作成と同じ扱い + csi_ = cfg_.GetNewfileCsi(); + lb_ = cfg_.GetNewfileLB(); + } + filename_.BeShortLongStyle(); + + // カレントディレクトリを、ファイルのある位置以外にしておく + // (こうしないと、開いているファイルのあるディレクトリが削除できない) + ::SetCurrentDirectory( Path(filename_).BeDriveOnly().c_str() ); + + // 文書タイプに応じて表示を更新 + if( needReConf ) + ReloadConfig(); + + // 開く + edit_.getDoc().ClearAll(); + edit_.getDoc().OpenFile( tf ); + + // タイトルバー更新 + UpdateWindowName(); + + // [最近使ったファイル]へ追加 + cfg_.AddMRU( filename_ ); + HWND wnd = NULL; + while( NULL!=(wnd=::FindWindowEx( NULL, wnd, className_, NULL )) ) + SendMessage( wnd, GPM_MRUCHANGED, 0, 0 ); + + return true; +} + + + +//------------------------------------------------------------------------- +// 保存処理 +//------------------------------------------------------------------------- + +bool GreenPadWnd::ShowSaveDlg() +{ + // [Save][Cancel] 保存先ファイル名指定ダイアログを表示 + + String flst[] = { + String(IDS_ALLFILES), + String(TEXT("*.*")) + }; + aarr filt = SaveFileDlg::ConnectWithNull( flst, countof(flst) ); + + SaveFileDlg sfd( charSets_, csi_, lb_ ); + if( !sfd.DoModal( hwnd(), filt.get(), filename_.c_str() ) ) + return false; + + filename_ = sfd.filename(); + csi_ = sfd.csi(); + lb_ = sfd.lb(); + + return true; +} + +bool GreenPadWnd::Save_showDlgIfNeeded() +{ + bool wasUntitled = isUntitled(); + + // [Save][Cancel] ファイル名未定ならダイアログ表示 + if( isUntitled() ) + if( !ShowSaveDlg() ) + return false; + if( Save() ) + { + if( wasUntitled ) + ReloadConfig(); // 文書タイプに応じて表示を更新 + return true; + } + return false; +} + +bool GreenPadWnd::AskToSave() +{ + // 変更されていたら、 + // [Yes][No][Cancel] 保存するかどうか尋ねる。 + // 保存するなら + // [Save][Cancel] ファイル名未定ならダイアログ表示 + + if( edit_.getDoc().isModified() ) + { + int answer = MsgBox( + String(IDS_ASKTOSAVE).c_str(), + String(IDS_APPNAME).c_str(), + MB_YESNOCANCEL|MB_ICONQUESTION + ); + if( answer == IDYES ) return Save_showDlgIfNeeded(); + if( answer == IDCANCEL ) return false; + } + return true; +} + +bool GreenPadWnd::Save() +{ + TextFileW tf( charSets_[csi_].ID, lb_ ); + if( tf.Open( filename_.c_str() ) ) + { + // 無事ファイルに保存できた場合 + edit_.getDoc().SaveFile( tf ); + UpdateWindowName(); + // [最近使ったファイル]更新 + cfg_.AddMRU( filename_ ); + HWND wnd = NULL; + while( NULL!=(wnd=::FindWindowEx( NULL, wnd, className_, NULL )) ) + SendMessage( wnd, GPM_MRUCHANGED, 0, 0 ); + return true; + } + + // Error! + MsgBox( String(IDS_SAVEERROR).c_str() ); + return false; +} + + + +//------------------------------------------------------------------------- +// メインウインドウの初期化 +//------------------------------------------------------------------------- + +GreenPadWnd::ClsName GreenPadWnd::className_ = TEXT("GreenPad MainWnd"); + +GreenPadWnd::GreenPadWnd() + : WndImpl ( className_, WS_OVERLAPPEDWINDOW, WS_EX_ACCEPTFILES ) + , charSets_( cfg_.GetCharSetList() ) + , csi_ ( cfg_.GetNewfileCsi() ) + , lb_ ( cfg_.GetNewfileLB() ) + , search_ ( *this, edit_ ) +{ + LOGGER( "GreenPadWnd::Construct begin" ); + + static WNDCLASSEX wc; + wc.hIcon = app().LoadIcon( IDR_MAIN ); + wc.hCursor = app().LoadOemCursor( IDC_ARROW ); + wc.lpszMenuName = MAKEINTRESOURCE( IDR_MAIN ); + wc.lpszClassName = className_; + WndImpl::Register( &wc ); + + ime().EnableGlobalIME( true ); + + LOGGER( "GreenPadWnd::Construct end" ); +} + +void GreenPadWnd::on_create( CREATESTRUCT* cs ) +{ + LOGGER("GreenPadWnd::on_create begin"); + + accel_ = app().LoadAccel( IDR_MAIN ); + stb_.Create( hwnd() ); + edit_.Create( NULL, hwnd(), 0, 0, 100, 100 ); + LOGGER("GreenPadWnd::on_create edit created"); + edit_.getDoc().AddHandler( this ); + edit_.getCursor().AddHandler( this ); + stb_.SetStatusBarVisible( cfg_.showStatusBar() ); + + LOGGER("GreenPadWnd::on_create halfway"); + + search_.LoadFromINI( cfg_.getImpl() ); + SetupSubMenu(); + SetupMRUMenu(); + + LOGGER("GreenPadWnd::on_create menu"); +} + +bool GreenPadWnd::StartUp( const Path& fn, int cs, int ln ) +{ + LOGGER( "GreenPadWnd::StartUp begin" ); + Create( 0, 0, cfg_.GetWndX(), cfg_.GetWndY(), cfg_.GetWndW(), cfg_.GetWndH(), 0 ); + LOGGER( "GreenPadWnd::Created" ); + if( fn.len()==0 || !OpenByMyself( fn, cs ) ) + { + LOGGER( "for new file..." ); + + // ファイルを開か(け)なかった場合 + ReloadConfig( fn.len()==0 ); + LOGGER( "GreenPadWnd::StartUp reloadconfig end" ); + UpdateWindowName(); + LOGGER( "GreenPadWnd::StartUp updatewindowname end" ); + } + + // 指定の行へジャンプ + if( ln != -1 ) + JumpToLine( ln ); + + LOGGER( "GreenPadWnd::StartUp end" ); + return true; +} + +void GreenPadWnd::ShowUp2() +{ + Window::ShowUp( cfg_.GetWndM() ? SW_MAXIMIZE : SW_SHOW ); +} + + +//------------------------------------------------------------------------- +// スタートアップルーチン +// コマンドラインの解析を行う +//------------------------------------------------------------------------- + +int kmain() +{ + LOGGER( "kmain() begin" ); + + Argv arg; + ulong i; + + LOGGER( "argv processed" ); + + //-- まずオプションスイッチを処理 + + int optL = -1; + int optC = 0; + + for( i=1; i nul + -@del /Q release\*.exe 2> nul + +############################################################################ + +DMAK = make# Hey, why no $(MAKE)???? + +gcc: + $(MAKE) -f Makefiles/gcc.mak +dmc: + $(DMAK) -f Makefiles/dmc.mak +vcc: + $(MAKE) -f Makefiles/vcc.mak +bcc: + $(MAKE) -f Makefiles/bcc.mak ADDED Makefiles/bcc.mak Index: Makefiles/bcc.mak ================================================================== --- Makefiles/bcc.mak +++ Makefiles/bcc.mak @@ -0,0 +1,73 @@ +NAME = bcc +OBJ_SUFFIX = obj + +############################################################################### +TARGET = release\GreenPad_$(NAME).exe +INTDIR = obj\$(NAME) + +all: PRE $(TARGET) + +OBJS = \ + $(INTDIR)\thread.$(OBJ_SUFFIX) \ + $(INTDIR)\log.$(OBJ_SUFFIX) \ + $(INTDIR)\winutil.$(OBJ_SUFFIX) \ + $(INTDIR)\textfile.$(OBJ_SUFFIX) \ + $(INTDIR)\path.$(OBJ_SUFFIX) \ + $(INTDIR)\cmdarg.$(OBJ_SUFFIX) \ + $(INTDIR)\file.$(OBJ_SUFFIX) \ + $(INTDIR)\find.$(OBJ_SUFFIX) \ + $(INTDIR)\ctrl.$(OBJ_SUFFIX) \ + $(INTDIR)\registry.$(OBJ_SUFFIX) \ + $(INTDIR)\window.$(OBJ_SUFFIX) \ + $(INTDIR)\string.$(OBJ_SUFFIX) \ + $(INTDIR)\memory.$(OBJ_SUFFIX) \ + $(INTDIR)\app.$(OBJ_SUFFIX) \ + $(INTDIR)\ip_cursor.$(OBJ_SUFFIX) \ + $(INTDIR)\ip_scroll.$(OBJ_SUFFIX) \ + $(INTDIR)\ip_wrap.$(OBJ_SUFFIX) \ + $(INTDIR)\ip_draw.$(OBJ_SUFFIX) \ + $(INTDIR)\ip_ctrl1.$(OBJ_SUFFIX) \ + $(INTDIR)\ip_text.$(OBJ_SUFFIX) \ + $(INTDIR)\ip_parse.$(OBJ_SUFFIX) \ + $(INTDIR)\GpMain.$(OBJ_SUFFIX) \ + $(INTDIR)\OpenSaveDlg.$(OBJ_SUFFIX) \ + $(INTDIR)\Search.$(OBJ_SUFFIX) \ + $(INTDIR)\RSearch.$(OBJ_SUFFIX) \ + $(INTDIR)\ConfigManager.$(OBJ_SUFFIX) + +LIBS = \ + kernel32.lib \ + user32.lib \ + gdi32.lib \ + shell32.lib \ + advapi32.lib \ + comdlg32.lib \ + comctl32.lib \ + ole32.lib \ + imm32.lib + +PRE: + -@if not exist release mkdir release + -@if not exist obj mkdir obj + -@if not exist $(INTDIR) mkdir $(INTDIR) +############################################################################### + +RES = $(INTDIR)\gp_rsrc.res + +.autodepend +.path.cpp = .;kilib;editwing +.path.rc = rsrc + +COPT = -c -W -AT -d -O -O1 -Oc -Oi -Ov -x- -RT- -Ve -VM -w-par -w-inl -w-pia -H=$(INTDIR)\stdafx.pch -Hh=stdafx.h +LOPT = -aa -Tpe -x -Iobj\bcc -w-rty +ROPT = -m -c932 -l0x411 + +$(TARGET): $(OBJS) $(RES) + @ilink32 $(LOPT) c0w32.obj $(OBJS), $@,, cw32.lib import32.lib,, $(RES) + -@del release\GreenPad_bcc.tds + +.rc.res: + @brcc32 $(ROPT) -fo$@ $< + +.cpp.obj: + @bcc32 $(COPT) -o$@ $< ADDED Makefiles/dmc.mak Index: Makefiles/dmc.mak ================================================================== --- Makefiles/dmc.mak +++ Makefiles/dmc.mak @@ -0,0 +1,95 @@ +NAME = dmc +OBJ_SUFFIX = obj + +############################################################################### +TARGET = release\GreenPad_$(NAME).exe +INTDIR = obj\$(NAME) + +all: PRE $(TARGET) + +OBJS = \ + $(INTDIR)\thread.$(OBJ_SUFFIX) \ + $(INTDIR)\log.$(OBJ_SUFFIX) \ + $(INTDIR)\winutil.$(OBJ_SUFFIX) \ + $(INTDIR)\textfile.$(OBJ_SUFFIX) \ + $(INTDIR)\path.$(OBJ_SUFFIX) \ + $(INTDIR)\cmdarg.$(OBJ_SUFFIX) \ + $(INTDIR)\file.$(OBJ_SUFFIX) \ + $(INTDIR)\find.$(OBJ_SUFFIX) \ + $(INTDIR)\ctrl.$(OBJ_SUFFIX) \ + $(INTDIR)\registry.$(OBJ_SUFFIX) \ + $(INTDIR)\window.$(OBJ_SUFFIX) \ + $(INTDIR)\string.$(OBJ_SUFFIX) \ + $(INTDIR)\memory.$(OBJ_SUFFIX) \ + $(INTDIR)\app.$(OBJ_SUFFIX) \ + $(INTDIR)\ip_cursor.$(OBJ_SUFFIX) \ + $(INTDIR)\ip_scroll.$(OBJ_SUFFIX) \ + $(INTDIR)\ip_wrap.$(OBJ_SUFFIX) \ + $(INTDIR)\ip_draw.$(OBJ_SUFFIX) \ + $(INTDIR)\ip_ctrl1.$(OBJ_SUFFIX) \ + $(INTDIR)\ip_text.$(OBJ_SUFFIX) \ + $(INTDIR)\ip_parse.$(OBJ_SUFFIX) \ + $(INTDIR)\GpMain.$(OBJ_SUFFIX) \ + $(INTDIR)\OpenSaveDlg.$(OBJ_SUFFIX) \ + $(INTDIR)\Search.$(OBJ_SUFFIX) \ + $(INTDIR)\RSearch.$(OBJ_SUFFIX) \ + $(INTDIR)\ConfigManager.$(OBJ_SUFFIX) + +LIBS = \ + kernel32.lib \ + user32.lib \ + gdi32.lib \ + shell32.lib \ + advapi32.lib \ + comdlg32.lib \ + comctl32.lib \ + ole32.lib \ + imm32.lib + +PRE: + -@if not exist release mkdir release + -@if not exist obj mkdir obj + -@if not exist $(INTDIR) mkdir $(INTDIR) +############################################################################### + +RC = rsrc\gp_rsrc.rc +RES = $(INTDIR)\gp_rsrc.res + +COPT = -Bj -j0 -Ab -w2 -w7 -o -c +LOPT = -Bj -mn -WA -L/su:Windows:4.0/exet:NT/onerror:noexe +ROPT = -j -32 -l0411 + +$(TARGET) : SETI $(OBJS) $(RES) + dmc $(LOPT) -o$(TARGET) $(RES) $(OBJS) $(LIBS) + -@del GreenPad_dmc.map + brc32 $(RES) $(TARGET) +SETI: + @set INCLUDE=kilib;$(INCLUDE) + +OBJ\dmc\gp_rsrc.res : rsrc\gp_rsrc.rc ; brcc32 -m -c932 -l0x411 -fo$@ $** +OBJ\dmc\thread.obj : kilib\thread.cpp ; dmc $(COPT) -o$@ $** +OBJ\dmc\log.obj : kilib\log.cpp ; dmc $(COPT) -o$@ $** +OBJ\dmc\winutil.obj : kilib\winutil.cpp ; dmc $(COPT) -o$@ $** +OBJ\dmc\textfile.obj : kilib\textfile.cpp ; dmc $(COPT) -o$@ $** +OBJ\dmc\path.obj : kilib\path.cpp ; dmc $(COPT) -o$@ $** +OBJ\dmc\cmdarg.obj : kilib\cmdarg.cpp ; dmc $(COPT) -o$@ $** +OBJ\dmc\file.obj : kilib\file.cpp ; dmc $(COPT) -o$@ $** +OBJ\dmc\find.obj : kilib\find.cpp ; dmc $(COPT) -o$@ $** +OBJ\dmc\ctrl.obj : kilib\ctrl.cpp ; dmc $(COPT) -o$@ $** +OBJ\dmc\registry.obj : kilib\registry.cpp ; dmc $(COPT) -o$@ $** +OBJ\dmc\window.obj : kilib\window.cpp ; dmc $(COPT) -o$@ $** +OBJ\dmc\string.obj : kilib\string.cpp ; dmc $(COPT) -o$@ $** +OBJ\dmc\memory.obj : kilib\memory.cpp ; dmc $(COPT) -o$@ $** +OBJ\dmc\app.obj : kilib\app.cpp ; dmc $(COPT) -o$@ $** +OBJ\dmc\ip_cursor.obj : editwing\ip_cursor.cpp ; dmc $(COPT) -o$@ $** +OBJ\dmc\ip_scroll.obj : editwing\ip_scroll.cpp ; dmc $(COPT) -o$@ $** +OBJ\dmc\ip_wrap.obj : editwing\ip_wrap.cpp ; dmc $(COPT) -o$@ $** +OBJ\dmc\ip_draw.obj : editwing\ip_draw.cpp ; dmc $(COPT) -o$@ $** +OBJ\dmc\ip_ctrl1.obj : editwing\ip_ctrl1.cpp ; dmc $(COPT) -o$@ $** +OBJ\dmc\ip_text.obj : editwing\ip_text.cpp ; dmc $(COPT) -o$@ $** +OBJ\dmc\ip_parse.obj : editwing\ip_parse.cpp ; dmc $(COPT) -o$@ $** +OBJ\dmc\GpMain.obj : GpMain.cpp ; dmc $(COPT) -o$@ $** +OBJ\dmc\OpenSaveDlg.obj : OpenSaveDlg.cpp ; dmc $(COPT) -o$@ $** +OBJ\dmc\Search.obj : Search.cpp ; dmc $(COPT) -o$@ $** +OBJ\dmc\RSearch.obj : RSearch.cpp ; dmc $(COPT) -o$@ $** +OBJ\dmc\ConfigManager.obj : ConfigManager.cpp ; dmc $(COPT) -o$@ $** ADDED Makefiles/gcc.mak Index: Makefiles/gcc.mak ================================================================== --- Makefiles/gcc.mak +++ Makefiles/gcc.mak @@ -0,0 +1,68 @@ + +NAME = gcc +OBJ_SUFFIX = o + +############################################################################### +TARGET = release/GreenPad_$(NAME).exe +INTDIR = obj\$(NAME) + +all: PRE $(TARGET) + +OBJS = \ + $(INTDIR)/thread.$(OBJ_SUFFIX) \ + $(INTDIR)/log.$(OBJ_SUFFIX) \ + $(INTDIR)/winutil.$(OBJ_SUFFIX) \ + $(INTDIR)/textfile.$(OBJ_SUFFIX) \ + $(INTDIR)/path.$(OBJ_SUFFIX) \ + $(INTDIR)/cmdarg.$(OBJ_SUFFIX) \ + $(INTDIR)/file.$(OBJ_SUFFIX) \ + $(INTDIR)/find.$(OBJ_SUFFIX) \ + $(INTDIR)/ctrl.$(OBJ_SUFFIX) \ + $(INTDIR)/registry.$(OBJ_SUFFIX) \ + $(INTDIR)/window.$(OBJ_SUFFIX) \ + $(INTDIR)/string.$(OBJ_SUFFIX) \ + $(INTDIR)/memory.$(OBJ_SUFFIX) \ + $(INTDIR)/app.$(OBJ_SUFFIX) \ + $(INTDIR)/ip_cursor.$(OBJ_SUFFIX) \ + $(INTDIR)/ip_scroll.$(OBJ_SUFFIX) \ + $(INTDIR)/ip_wrap.$(OBJ_SUFFIX) \ + $(INTDIR)/ip_draw.$(OBJ_SUFFIX) \ + $(INTDIR)/ip_ctrl1.$(OBJ_SUFFIX) \ + $(INTDIR)/ip_text.$(OBJ_SUFFIX) \ + $(INTDIR)/ip_parse.$(OBJ_SUFFIX) \ + $(INTDIR)/GpMain.$(OBJ_SUFFIX) \ + $(INTDIR)/OpenSaveDlg.$(OBJ_SUFFIX) \ + $(INTDIR)/Search.$(OBJ_SUFFIX) \ + $(INTDIR)/RSearch.$(OBJ_SUFFIX) \ + $(INTDIR)/ConfigManager.$(OBJ_SUFFIX) + +LIBS = \ + -lkernel32 \ + -luser32 \ + -lgdi32 \ + -lshell32 \ + -ladvapi32 \ + -lcomdlg32 \ + -lcomctl32 \ + -lole32 \ + -limm32 + +PRE: + -@if not exist release mkdir release + -@if not exist obj mkdir obj + -@if not exist $(INTDIR) mkdir $(INTDIR) +############################################################################### + +RES = $(INTDIR)/gp_rsrc.o + +VPATH = editwing:kilib +CXXFLAGS = -mno-cygwin -O2 -idirafter kilib -c --input-charset=cp932 +LOPT = -mwindows -mno-cygwin + +$(TARGET) : $(OBJS) $(RES) + g++ $(LOPT) -o$(TARGET) $(OBJS) $(RES) $(LIBS) + strip -s $(TARGET) +$(INTDIR)/%.o: rsrc/%.rc + windres -l=0x411 -I rsrc $< $@ +$(INTDIR)/%.o: %.cpp + g++ $(CXXFLAGS) -o$@ $< ADDED Makefiles/vcc.mak Index: Makefiles/vcc.mak ================================================================== --- Makefiles/vcc.mak +++ Makefiles/vcc.mak @@ -0,0 +1,76 @@ +NAME = vcc +OBJ_SUFFIX = obj + +############################################################################### +TARGET = release\GreenPad_$(NAME).exe +INTDIR = obj\$(NAME) + +all: PRE $(TARGET) + +OBJS = \ + $(INTDIR)\thread.$(OBJ_SUFFIX) \ + $(INTDIR)\log.$(OBJ_SUFFIX) \ + $(INTDIR)\winutil.$(OBJ_SUFFIX) \ + $(INTDIR)\textfile.$(OBJ_SUFFIX) \ + $(INTDIR)\path.$(OBJ_SUFFIX) \ + $(INTDIR)\cmdarg.$(OBJ_SUFFIX) \ + $(INTDIR)\file.$(OBJ_SUFFIX) \ + $(INTDIR)\find.$(OBJ_SUFFIX) \ + $(INTDIR)\ctrl.$(OBJ_SUFFIX) \ + $(INTDIR)\registry.$(OBJ_SUFFIX) \ + $(INTDIR)\window.$(OBJ_SUFFIX) \ + $(INTDIR)\string.$(OBJ_SUFFIX) \ + $(INTDIR)\memory.$(OBJ_SUFFIX) \ + $(INTDIR)\app.$(OBJ_SUFFIX) \ + $(INTDIR)\ip_cursor.$(OBJ_SUFFIX) \ + $(INTDIR)\ip_scroll.$(OBJ_SUFFIX) \ + $(INTDIR)\ip_wrap.$(OBJ_SUFFIX) \ + $(INTDIR)\ip_draw.$(OBJ_SUFFIX) \ + $(INTDIR)\ip_ctrl1.$(OBJ_SUFFIX) \ + $(INTDIR)\ip_text.$(OBJ_SUFFIX) \ + $(INTDIR)\ip_parse.$(OBJ_SUFFIX) \ + $(INTDIR)\GpMain.$(OBJ_SUFFIX) \ + $(INTDIR)\OpenSaveDlg.$(OBJ_SUFFIX) \ + $(INTDIR)\Search.$(OBJ_SUFFIX) \ + $(INTDIR)\RSearch.$(OBJ_SUFFIX) \ + $(INTDIR)\ConfigManager.$(OBJ_SUFFIX) + +LIBS = \ + kernel32.lib \ + user32.lib \ + gdi32.lib \ + shell32.lib \ + advapi32.lib \ + comdlg32.lib \ + comctl32.lib \ + ole32.lib \ + imm32.lib + +PRE: + -@if not exist release mkdir release + -@if not exist obj mkdir obj + -@if not exist $(INTDIR) mkdir $(INTDIR) +############################################################################### + +RES = $(INTDIR)\gp_rsrc.res +PCH = $(INTDIR)\gp.pch +DEF = /D NDEBUG /D UNICODE /D _UNICODE /D USEGLOBALIME + +COPT = /nologo $(DEF) /O1isyb1 /GA /GF /FD /Zc:wchar_t /Yu"stdafx.h" /Fp$(PCH) /Fd$(INTDIR) /W3 /MT /c +LOPT = /nologo /manifest:no bufferoverflowU.lib +ROPT = $(DEF) /L 0x411 /I "rsrc" + +$(TARGET): PRE $(PCH) $(OBJS) $(RES) + link $(LOPT) /OUT:$(TARGET) $(OBJS) $(RES) $(LIBS) + +{rsrc}.rc{$(INTDIR)}.res: + rc $(ROPT) /Fo$@ $** + +{.}.cpp{$(INTDIR)}.obj: + cl $(COPT) /Fo$@ $** +{kilib}.cpp{$(INTDIR)}.obj: + cl $(COPT) /Fo$@ $** +{editwing}.cpp{$(INTDIR)}.obj: + cl $(COPT) /Fo$@ $** +$(PCH): kilib\stdafx.cpp + cl $(COPT) /Fo$(INTDIR)\stdafx.obj /Yc"stdafx.h" $** ADDED NSearch.h Index: NSearch.h ================================================================== --- NSearch.h +++ NSearch.h @@ -0,0 +1,191 @@ +#ifndef AFX_NSEARCH_H__8336E133_90C5_4059_8605_6066BD37D042__INCLUDED_ +#define AFX_NSEARCH_H__8336E133_90C5_4059_8605_6066BD37D042__INCLUDED_ +#include "Search.h" + + + +//========================================================================= +//@{ @pkg Gp.Search //@} +// BM法検索用ポリシーs +//========================================================================= + +//@{ 大文字小文字を区別するポリシー //@} +struct CaseSensitive +{ + static unicode map( unicode c ) + { return c; } + static bool not_equal( unicode c1, unicode c2 ) + { return c1!=c2; } +}; + +//@{ 大文字小文字を区別しないポリシー //@} +struct IgnoreCase +{ + static unicode map( unicode c ) + { return (L'a'<=c && c<=L'z' ? c-L'a'+L'A' : c); } + static bool not_equal( unicode c1, unicode c2 ) + { return map(c1)!=map(c2); } +}; + + + +//========================================================================= +//@{ +// BM法による普通の正方向検索 +//@} +//========================================================================= + +template class BMSearch +{ +public: + BMSearch( const unicode* key ) + : keylen_( my_lstrlenW(key) ) + , key_( my_lstrcpyW( new unicode[keylen_+1], key ) ) + { + memFF( lastAppearance_, sizeof(lastAppearance_) ); + for( int i=0, e=keylen_; it?j-t:1) ) + { + for( j=keylen_-1; j>=0; --j ) + { + if( ComparisonPolicy::not_equal( key_[j], str[i+j] ) ) + break; + } + if( j < 0 ) + return i; + t = lastAppearance_[ ComparisonPolicy::map(str[i+j]) ]; + } + return -1; + } + + int keylen() const { return keylen_; } + +private: + int keylen_; + unicode* key_; + int lastAppearance_[65536]; +}; + + + +//========================================================================= +//@{ +// BM法による逆方向検索 +//@} +//========================================================================= + +template class BMSearchRev +{ +public: + BMSearchRev( const unicode* key ) + : keylen_( my_lstrlenW(key) ) + , key_( my_lstrcpyW( new unicode[keylen_+1], key ) ) + { + memFF( firstAppearance_, sizeof(firstAppearance_) ); + for( int i=keylen_-1; i>=0; --i ) + firstAppearance_[ ComparisonPolicy::map(key[i]) ] = i; + } + + ~BMSearchRev() + { + delete [] key_; + } + + int Search( const unicode* str, int strlen ) + { + for( int i=strlen-keylen_-1, j, e, t; i>=0; i-=(t>j?t-j:1) ) + { + for( j=0, e=keylen_; j= e ) + return i; + t = firstAppearance_[ ComparisonPolicy::map(str[i+j]) ]; + if( t == -1 ) t = keylen_; + } + return -1; + } + + int keylen() const { return keylen_; } + +private: + int keylen_; + unicode* key_; + int firstAppearance_[65536]; +}; + + + +//========================================================================= +//@{ +// Searhcableとしての実装(正方向検索) +//@} +//========================================================================= + +template +class NSearch : public Searchable +{ +public: + NSearch( const unicode* key ) : s_(key) {} + +private: + bool Search( + const unicode* str, ulong len, ulong stt, ulong* mbg, ulong* med ) + { + int n = s_.Search( str+stt, len-stt ); + if( n < 0 ) + return false; + *mbg = stt + n; + *med = stt + n + s_.keylen(); + return true; + } + +private: + BMSearch s_; +}; + + + +//========================================================================= +//@{ +// Searhcableとしての実装(逆方向検索) +//@} +//========================================================================= + +template +class NSearchRev : public Searchable +{ +public: + NSearchRev( const unicode* key ) : s_(key) {} + +private: + bool Search( + const unicode* str, ulong len, ulong stt, ulong* mbg, ulong* med ) + { + int n = s_.Search( str, stt+s_.keylen() ); + if( n < 0 ) + return false; + *mbg = n; + *med = n + s_.keylen(); + return true; + } + +private: + BMSearchRev s_; +}; + + + +//========================================================================= + +#endif ADDED OpenSaveDlg.cpp Index: OpenSaveDlg.cpp ================================================================== --- OpenSaveDlg.cpp +++ OpenSaveDlg.cpp @@ -0,0 +1,434 @@ +#include "stdafx.h" +#include "rsrc/resource.h" +#include "kilib/kilib.h" +#include "OpenSaveDlg.h" +using namespace ki; + + + +//------------------------------------------------------------------------ +// 文字コードリスト +//------------------------------------------------------------------------ + +CharSetList::CharSetList() + : list_( 30 ) +{ + static const TCHAR* const lnmJp[] = { + TEXT("自動判定"), + TEXT("日本語(ShiftJIS)"), + TEXT("日本語(EUC)"), + TEXT("日本語(ISO-2022-JP)"), + TEXT("UTF-5"), + TEXT("UTF-7"), + TEXT("UTF-8"), + TEXT("UTF-8N"), + TEXT("UTF-16BE(BOM)"), + TEXT("UTF-16LE(BOM)"), + TEXT("UTF-16BE"), + TEXT("UTF-16LE"), + TEXT("UTF-32BE(BOM)"), + TEXT("UTF-32LE(BOM)"), + TEXT("UTF-32BE"), + TEXT("UTF-32LE"), + TEXT("欧米"), + TEXT("中欧"), + TEXT("韓国語(EUC-KR)"), + TEXT("韓国語(ISO-2022-KR)"), + TEXT("韓国語(Johab)"), + TEXT("中国語(GB2312)"), + TEXT("中国語(ISO-2022-CN)"), + TEXT("中国語(HZ)"), + TEXT("中国語(Big5)"), + TEXT("キリル語(Windows)"), + TEXT("キリル語(KOI8-R)"), + TEXT("キリル語(KOI8-U)"), + TEXT("タイ語"), + TEXT("トルコ語"), + TEXT("バルト語"), + TEXT("ベトナム語"), + TEXT("ギリシャ語"), + TEXT("MSDOS(us)") + }; + static const TCHAR* const lnmEn[] = { + TEXT("AutoDetect"), + TEXT("Japanese(ShiftJIS)"), + TEXT("Japanese(EUC)"), + TEXT("Japanese(ISO-2022-JP)"), + TEXT("UTF-5"), + TEXT("UTF-7"), + TEXT("UTF-8"), + TEXT("UTF-8N"), + TEXT("UTF-16BE(BOM)"), + TEXT("UTF-16LE(BOM)"), + TEXT("UTF-16BE"), + TEXT("UTF-16LE"), + TEXT("UTF-32BE(BOM)"), + TEXT("UTF-32LE(BOM)"), + TEXT("UTF-32BE"), + TEXT("UTF-32LE"), + TEXT("Latin-1"), + TEXT("Latin-2"), + TEXT("Korean(EUC-KR)"), + TEXT("Korean(ISO-2022-KR)"), + TEXT("Korean(Johab)"), + TEXT("Chinese(GB2312)"), + TEXT("Chinese(ISO-2022-CN)"), + TEXT("Chinese(HZ)"), + TEXT("Chinese(Big5)"), + TEXT("Cyrillic(Windows)"), + TEXT("Cyrillic(KOI8-R)"), + TEXT("Cyrillic(KOI8-U)"), + TEXT("Thai"), + TEXT("Turkish"), + TEXT("Baltic"), + TEXT("Vietnamese"), + TEXT("Greek"), + TEXT("MSDOS(us)") + }; + static const TCHAR* const snm[] = { + TEXT(""), + TEXT("SJIS"), + TEXT("EUC"), + TEXT("JIS"), + TEXT("UTF5"), + TEXT("UTF7"), + TEXT("UTF8"), + TEXT("UTF8"), + TEXT("U16B"), + TEXT("U16L"), + TEXT("U16B"), + TEXT("U16L"), + TEXT("U32B"), + TEXT("U32L"), + TEXT("U32B"), + TEXT("U32L"), + TEXT("LTN1"), + TEXT("LTN2"), + TEXT("UHC"), + TEXT("I2KR"), + TEXT("Jhb"), + TEXT("GBK"), + TEXT("I2CN"), + TEXT("HZ"), + TEXT("BIG5"), + TEXT("CYRL"), + TEXT("KO8R"), + TEXT("KO8U"), + TEXT("THAI"), + TEXT("TRK"), + TEXT("BALT"), + TEXT("VTNM"), + TEXT("GRK"), + TEXT("DOS") + }; + + // 日本語環境なら日本語表示を選ぶ + const TCHAR* const * lnm = (::GetACP()==932 ? lnmJp : lnmEn); + + // いちいち書くの面倒なので短縮表記(^^; + CsInfo cs; + #define Enroll(_id,_nm) cs.ID=_id, \ + cs.longName=lnm[_nm], cs.shortName=snm[_nm], \ + cs.type=LOAD|SAVE, list_.Add( cs ) + #define EnrollS(_id,_nm) cs.ID=_id, \ + cs.longName=lnm[_nm], cs.shortName=snm[_nm], \ + cs.type=SAVE, list_.Add( cs ) + #define EnrollL(_id,_nm) cs.ID=_id, \ + cs.longName=lnm[_nm], cs.shortName=snm[_nm], \ + cs.type=LOAD, list_.Add( cs ) + + // 適宜登録 + EnrollL( AutoDetect,0 ); + if( ::IsValidCodePage(932) ) Enroll( SJIS, 1 ), + Enroll( EucJP, 2 ), + Enroll( IsoJP, 3 ); + /* if( always ) */ Enroll( UTF5, 4 ); + Enroll( UTF7, 5 ); + Enroll( UTF8, 6 ); + EnrollS( UTF8N, 7 ); + EnrollS( UTF16b, 8 ); + EnrollS( UTF16l, 9 ); + Enroll( UTF16BE, 10 ); + Enroll( UTF16LE, 11 ); + EnrollS( UTF32b, 12 ); + EnrollS( UTF32l, 13 ); + Enroll( UTF32BE, 14 ); + Enroll( UTF32LE, 15 ); + Enroll( Western, 16 ); + if( ::IsValidCodePage(28592) ) Enroll( Central, 17 ); + if( ::IsValidCodePage(949) ) Enroll( UHC, 18 ), + Enroll( IsoKR, 19 ); + if( ::IsValidCodePage(1361) ) Enroll( Johab, 20 ); + if( ::IsValidCodePage(936) ) Enroll( GBK, 21 ), + Enroll( IsoCN, 22 ), + Enroll( HZ , 23 ); + if( ::IsValidCodePage(950) ) Enroll( Big5 , 24 ); + if( ::IsValidCodePage(28595) ) Enroll( Cyrillic, 25 ); + if( ::IsValidCodePage(20866) ) Enroll( Koi8R, 26 ); + if( ::IsValidCodePage(21866) ) Enroll( Koi8U, 27 ); + if( ::IsValidCodePage(874) ) Enroll( Thai, 28 ); + if( ::IsValidCodePage(1254) ) Enroll( Turkish, 29 ); + if( ::IsValidCodePage(1257) ) Enroll( Baltic, 30 ); + if( ::IsValidCodePage(1258) ) Enroll( Vietnamese,31 ); + if( ::IsValidCodePage(28597) ) Enroll( Greek, 32 ); + Enroll( DOSUS, 33 ); + + // 終了 + #undef Enroll + #undef EnrollS + #undef EnrollL +} + +int CharSetList::defaultCs() const +{ + return ::GetACP(); +/* + switch( ::GetACP() ) + { + case 932: return SJIS; + case 936: return GBK; + case 949: return UHC; + case 950: return Big5; + default: return Western; + } +*/ +} + +ulong CharSetList::defaultCsi() const +{ + return findCsi( defaultCs() ); +} + +ulong CharSetList::findCsi( int cs ) const +{ + for( ulong i=0,ie=list_.size(); icsl_; + for( ulong i=0; i(lp)->code==CDN_FILEOK ) + { + ulong j=0, i=ComboBox(dlg,IDC_CODELIST).GetCurSel(); + for(;;++j,--i) + { + while( !(pThis->csl_[j].type & 2) ) // !LOAD + ++j; + if( i==0 ) + break; + } + pThis->csIndex_ = j; + } + } + return FALSE; +} + + + +//------------------------------------------------------------------------ +// 「保存」ダイアログ +//------------------------------------------------------------------------ + +SaveFileDlg* SaveFileDlg::pThis; + +bool SaveFileDlg::DoModal( HWND wnd, const TCHAR* fltr, const TCHAR* fnm ) +{ + CurrentDirRecovery cdr; + + if( fnm == NULL ) + filename_[0] = TEXT('\0'); + else + ::lstrcpy( filename_, fnm ); + + OPENFILENAME ofn = {sizeof(ofn)}; + ofn.hwndOwner = wnd; + ofn.hInstance = app().hinst(); + ofn.lpstrFilter = fltr; + ofn.lpstrFile = filename_; + ofn.nMaxFile = countof(filename_); + ofn.lpTemplateName = MAKEINTRESOURCE(IDD_SAVEFILEHOOK); + ofn.lpfnHook = OfnHook; + ofn.Flags = OFN_HIDEREADONLY | + OFN_PATHMUSTEXIST | + OFN_EXPLORER | + OFN_ENABLESIZING | + OFN_ENABLEHOOK | + OFN_ENABLETEMPLATE | + OFN_OVERWRITEPROMPT; + + pThis = this; + return ( ::GetSaveFileName(&ofn) != 0 ); +} + +UINT_PTR CALLBACK SaveFileDlg::OfnHook( HWND dlg, UINT msg, WPARAM, LPARAM lp ) +{ + if( msg==WM_INITDIALOG ) + { + // コンボボックスを埋めて、適切なのを選ぶ + { + ComboBox cb( dlg, IDC_CODELIST ); + const CharSetList& csl = pThis->csl_; + + for( ulong i=0; icsIndex_].longName ); + } + { + ComboBox cb( dlg, IDC_CRLFLIST ); + static const TCHAR* const lbList[] = { + TEXT("CR"), + TEXT("LF"), + TEXT("CRLF") + }; + + for( ulong i=0; ilb_] ); + } + } + else if( msg==WM_NOTIFY ) + { + if( reinterpret_cast(lp)->code==CDN_FILEOK ) + { + // OKが押されたら、文字コードの選択状況を記録 + ulong j=0, i=ComboBox(dlg,IDC_CODELIST).GetCurSel(); + for(;;++j,--i) + { + while( !(pThis->csl_[j].type & 1) ) // !SAVE + ++j; + if( i==0 ) + break; + } + pThis->csIndex_ = j; + // 改行コードも + pThis->lb_ = ComboBox(dlg,IDC_CRLFLIST).GetCurSel(); + } + } + return FALSE; +} + + + +//------------------------------------------------------------------------ +// ユーティリティー +//------------------------------------------------------------------------ + +ki::aarr OpenFileDlg::ConnectWithNull( String lst[], int num ) +{ + int TtlLen = 1; + for( int i=0; i a( new TCHAR[TtlLen] ); + + TCHAR* p = a.get(); + for( int i=0; i list_; +}; + + + +//======================================================================== +//@{ +// 「ファイルを開く」ダイアログ +// +// Windows共通のダイアログの下に、文字コードの選択欄を +// 付け加えたものを表示する。 +//@} +//======================================================================== + +class OpenFileDlg +{ +public: + explicit OpenFileDlg( const CharSetList& csl ); + bool DoModal( HWND wnd, const TCHAR* filter, const TCHAR* fnm ); + +public: + const TCHAR* filename() const; + int csi() const; + +public: + static ki::aarr ConnectWithNull( ki::String lst[], int num ); + +private: + const CharSetList& csl_; + TCHAR filename_[MAX_PATH]; + int csIndex_; + +private: + static OpenFileDlg* pThis; // マルチスレッド禁止! + static UINT_PTR CALLBACK OfnHook( HWND, UINT, WPARAM, LPARAM ); +}; + + + +//------------------------------------------------------------------------ +#ifndef __ccdoc__ + +inline OpenFileDlg::OpenFileDlg( const CharSetList& csl ) + : csl_(csl) {} + +inline const TCHAR* OpenFileDlg::filename() const + { return filename_; } + +inline int OpenFileDlg::csi() const + { return csIndex_; } + + + +#endif // __ccdoc__ +//======================================================================== +//@{ +// 「ファイルを保存」ダイアログ +// +// Windows共通のダイアログの下に、文字コードの選択欄と +// 改行コードの選択欄を付け加えたものを表示する。 +//@} +//======================================================================== + +class SaveFileDlg +{ +public: + explicit SaveFileDlg( const CharSetList& csl, int cs, int lb ); + bool DoModal( HWND wnd, const TCHAR* filter, const TCHAR* fnm ); + +public: + const TCHAR* filename() const; + int csi() const; + int lb() const; + +public: + static ki::aarr ConnectWithNull( ki::String lst[], int num ); + +private: + const CharSetList& csl_; + TCHAR filename_[MAX_PATH]; + int csIndex_; + int lb_; + +private: + static SaveFileDlg* pThis; // マルチスレッド禁止! + static UINT_PTR CALLBACK OfnHook( HWND, UINT, WPARAM, LPARAM ); +}; + +//------------------------------------------------------------------------ +#ifndef __ccdoc__ + +inline SaveFileDlg::SaveFileDlg( const CharSetList& csl, int cs, int lb ) + : csl_(csl), csIndex_(cs), lb_(lb) {} + +inline const TCHAR* SaveFileDlg::filename() const + { return filename_; } + +inline int SaveFileDlg::csi() const + { return csIndex_; } + +inline int SaveFileDlg::lb() const + { return lb_; } + +inline ki::aarr SaveFileDlg::ConnectWithNull + ( ki::String lst[], int num ) + { return OpenFileDlg::ConnectWithNull( lst, num ); } + + + +#endif // __ccdoc__ +//======================================================================== +//@{ +// 「開き直す」ダイアログ +// +// 文字コード選択欄表示 +//@} +//======================================================================== + +class ReopenDlg : public ki::DlgImpl +{ +public: + ReopenDlg( const CharSetList& csl, int csi ); + int csi() const; + +private: + void on_init(); + bool on_ok(); + +private: + const CharSetList& csl_; + int csIndex_; +}; + +//------------------------------------------------------------------------ +#ifndef __ccdoc__ + +inline int ReopenDlg::csi() const + { return csIndex_; } + + + +//======================================================================== + +#endif // __ccdoc__ +#endif // _GREENPAD_OPENSAVEDLG_H_ ADDED README.txt Index: README.txt ================================================================== --- README.txt +++ README.txt @@ -0,0 +1,121 @@ + +=<> +=<> GreenPad ver 1.08+ Source Code +=<> 2008/07/11 + + + Windows用簡易テキストエディタ GreenPad のソースコードです。 + 下にあげるいくつかのC++コンパイラでコンパイルできます。 + + Source code for GreenPad - a simple text editor for Windows. + Can be built by the following compilers. + + - Visual C++ .NET 2005 Express Edition + - Visual C++ 2003 Toolkit + - Microsoft Platform SDK for Windows Server 2003 R2 - March 2006 Edition + - Borland C++ BuilderX + - Borland C++ Compiler 5.5.1 + - Digital Mars C++ 8.49 + - MinGW (g++ 3.4.2) + + + +:: Visual C++ .NET 2005 :: + + - "kilib.sln" を開いて、「ビルド」メニューの「ソリューションのビルド」 + + - Open "kilib.sln" and build the main project. + + + +:: Visual C++ @ Command Prompt (Platform SDK / Toolkit 2003) :: + + - ソースコードのルートディレクトリで "nmake vcc" と打つ + + - Type "nmake vcc" at the root directory of the source archive + + + +:: Visual C++ 6.0 :: + + - 対応しなくなりました。 + 作者は確認していませんが、一応 kilib.dsw と kilib.dsp を使えば + もしかしたらビルドできるかも。 + + - No longer supported + kilib.dsw and kilib.dsp MAY work. + + + +:: Borland C++ @ Command Prompt (5.5.1 / BuilderX) :: + + - ソースコードのルートディレクトリで "make bcc" と打つ。 + 必ずBorland製のmakeコマンドを使用すること + + - Type "make bcc" at the root directory of the source archive + Make sure to use Borland make. + + + +:: Digital Mars C++ :: + + - ソースコードのルートディレクトリで "make dmc" と打つ。 + 必ずDigitalmars製のmakeコマンドを使用すること。 + なお、Digital Mars 製のリソースコンパイラは力不足のため、 + リソースのコンパイルに Borland のコンパイラが必要です。 + Borland C++ 5.5 についてくるので入手してください。 + あと、imm32.dll をリンクするため imm32.lib が必要です。 + お手元で生成するか、http://www.kmonos.net/alang/dmc/ から + 入手してください。 + + - Type "make dmc" at the root directory of the source archive + Make sure to use Digitalmars make. + Since DM's resource compiler is pretty poor, you additionaly + need Borland's resource compiler to build GreenPad. + You also need imm32.lib to build. You can generate it by + coff2omf command or something, or you can download it from + http://www.kmonos.net/alang/dmc/ . + + + +:: gcc (MinGW) :: + + - ソースコードのルートディレクトリで "make gcc" と打つ。 + 必ずGNU製のmakeコマンドを使用すること。 + MinGW に最初から付属してくるリソースコンパイラ windres は + 日本語対応でないことがあります。MinGW のページから binutils + を別途ダウンロードして、そちらの windres をご利用下さい。 + + - Type "make gcc" at the root directory of the source archive + Make sure to use GNU make. + MinGW version of windres (resource compiler) seem not to + support Japanese resources. So you need to separately download + binutils from the MinGW page and use the windres in it. + + + +- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +:: ライセンス / License :: + + NYSL Version 0.9982 http://www.kmonos.net/nysl/ + + A. 本ソフトウェアは Everyone'sWare です。このソフトを手にした一人一人が、 + ご自分の作ったものを扱うのと同じように、自由に利用することが出来ます。 + + A-1. フリーウェアです。作者からは使用料等を要求しません。 + A-2. 有料無料や媒体の如何を問わず、自由に転載・再配布できます。 + A-3. いかなる種類の 改変・他プログラムでの利用 を行っても構いません。 + A-4. 変更したものや部分的に使用したものは、あなたのものになります。 + 公開する場合は、あなたの名前の下で行って下さい。 + + B. このソフトを利用することによって生じた損害等について、作者は + 責任を負わないものとします。各自の責任においてご利用下さい。 + + C. 著作者人格権は K.INABA に帰属します。著作権は放棄します。 + + D. 以上の3項は、ソース・実行バイナリの双方に適用されます。 + + +--------------------------------------------------------------------------- + by k.inaba( http://www.kmonos.net/ ) ADDED RSearch.cpp Index: RSearch.cpp ================================================================== --- RSearch.cpp +++ RSearch.cpp @@ -0,0 +1,736 @@ + +#include "stdafx.h" +#include "RSearch.h" +#include "kilib/ktlaptr.h" +using namespace ki; + + + +//========================================================================= +//@{ +// 文字の種類 +//@} +//========================================================================= + +enum RegToken +{ + R_Char, // 普通の文字 + R_Any, // '.' + R_Lcl, // '[' + R_Rcl, // ']' + R_Ncl, // '^' + R_Range, // '-' + R_Lbr, // '(' + R_Rbr, // ')' + R_Bar, // '|' + R_Star, // '*' + R_Plus, // '+' + R_Quest, // '?' + R_End // '\0' +}; + + + +//========================================================================= +//@{ +// トークンに分解 +// +// 行頭を表す^と行末を表す$については上位層で頑張る +//@} +//========================================================================= + +class RegLexer +{ +public: + RegLexer( const wchar_t* pat, ulong len ); + RegToken GetToken(); + wchar_t GetChar() { return chr_; } + +private: + const wchar_t* pat_; + const wchar_t* end_; + const wchar_t* sub_; + wchar_t chr_; +}; + + + +//========================================================================= +//@{ +// トークンに分解:実装 +//@} +//========================================================================= + +inline RegLexer::RegLexer( const wchar_t* pat, ulong len ) + : pat_( pat ) + , end_( pat+len ) + , sub_( L"" ) +{ +} + +RegToken RegLexer::GetToken() +{ + const wchar_t*& x = (*sub_ ? sub_ : pat_); + if( x == end_ ) return R_End; + switch( *x++ ) + { + case L'.': return R_Any; + case L'[': return R_Lcl; + case L']': return R_Rcl; + case L'^': return R_Ncl; + case L'-': return R_Range; + case L'(': return R_Lbr; + case L')': return R_Rbr; + case L'|': return R_Bar; + case L'*': return R_Star; + case L'+': return R_Plus; + case L'?': return R_Quest; + case L'\\': if( x==end_ ) return R_End; switch( *x++ ) { + case L't': chr_=L'\t'; return R_Char; + case L'w': sub_=L"[0-9a-zA-Z_]"; return GetToken(); + case L'W': sub_=L"[^0-9a-zA-Z_]"; return GetToken(); + case L'd': sub_=L"[0-9]"; return GetToken(); + case L'D': sub_=L"[^0-9]"; return GetToken(); + case L's': sub_=L"[\t ]"; return GetToken(); + case L'S': sub_=L"[^\t ]"; return GetToken(); + } // fall through... + default: + chr_ = *(x-1); + return R_Char; + } +} + + + +//========================================================================= +//@{ +// 構文木のノードに振られる値の種類 +//@} +//========================================================================= + +enum RegType +{ + N_Char, // 普通の文字 (ch) + N_Class, // [...] など (cls) + N_Concat, // 連接 (left, right) + N_Or, // | (left, right) + N_Closure, // * (left) + N_Closure1, // + (left) + N_01, // ? (left) + N_Empty // 空 (--) +}; + +struct RegClass +{ + struct OneRange + { + wchar_t stt; + wchar_t end; + }; + OneRange range; + aptr next; + RegClass( wchar_t s, wchar_t e, RegClass* n ) + { aptr an(n); range.stt=s, range.end=e, next=an; } +}; + +struct RegNode +{ + RegType type; // このノードの種類 + wchar_t ch; // 文字 + aptr cls; // 文字集合 + bool cmpcls; // ↑補集合かどうか + dptr left; // 左の子 + dptr right; // 右の子 +}; + + + +//========================================================================= +//@{ +// 構文木作成 +//@} +//========================================================================= + +class RegParser +{ +public: + RegParser( const unicode* pat ); + RegNode* root() { return root_.get(); } + bool err() { return err_; } + bool isHeadType() const { return isHeadType_; } + bool isTailType() const { return isTailType_; } + +private: + RegNode* make_empty_leaf(); + RegNode* make_char_leaf( wchar_t c ); + RegNode* make_node( RegType t, RegNode* lft, RegNode* rht ); + void eat_token(); + RegNode* expr(); + RegNode* term(); + RegNode* factor(); + RegNode* primary(); + RegNode* reclass(); + +private: + bool err_; + bool isHeadType_; + bool isTailType_; + dptr root_; + + RegLexer lex_; + RegToken nextToken_; +}; + + + +//========================================================================= +//@{ +// 構文木作成:実装 +//@} +//========================================================================= + +namespace { static int tmp; } + +inline RegParser::RegParser( const unicode* pat ) + : err_ ( false ) + , isHeadType_( *pat==L'^' ) + , isTailType_( (tmp=my_lstrlenW(pat), tmp && pat[tmp-1]==L'$') ) + , lex_( + (isHeadType_ ? pat+1 : pat), + (my_lstrlenW(pat) - (isHeadType_ ? 1 : 0) + - (isTailType_ ? 1 : 0)) ) +{ + eat_token(); + root_ = expr(); +} + +inline void RegParser::eat_token() +{ + nextToken_ = lex_.GetToken(); +} + +inline RegNode* RegParser::make_empty_leaf() +{ + RegNode* node = new RegNode; + node->type = N_Empty; + return node; +} + +inline RegNode* RegParser::make_char_leaf( wchar_t c ) +{ + RegNode* node = new RegNode; + node->type = N_Char; + node->ch = c; + return node; +} + +RegNode* RegParser::make_node( RegType t, RegNode* lft, RegNode* rht ) +{ + RegNode* node = new RegNode; + node->type = t; + node->left = lft; + node->right= rht; + return node; +} + +RegNode* RegParser::reclass() +{ +// CLASS ::= '^'? CHAR (CHAR | -CHAR)* + + bool neg = false; + if( nextToken_ == R_Ncl ) + neg=true, eat_token(); + + RegClass* cls = NULL; + while( nextToken_ == R_Char ) + { + wchar_t ch = lex_.GetChar(); + eat_token(); + if( nextToken_ == R_Range ) + { + eat_token(); + if( nextToken_ != R_Char ) + err_ = true; + else + { + wchar_t ch2 = lex_.GetChar(); + cls = new RegClass( Min(ch,ch2), Max(ch,ch2), cls ); + eat_token(); + } + } + else + { + cls = new RegClass( ch, ch, cls ); + } + } + + RegNode* node = new RegNode; + node->type = N_Class; + aptr ncls(cls); + node->cls = ncls; + node->cmpcls = neg; + return node; +} + +RegNode* RegParser::primary() +{ +// PRIMARY ::= CHAR +// '.' +// '[' CLASS ']' +// '(' REGEXP ')' + + RegNode* node; + switch( nextToken_ ) + { + case R_Char: + node = make_char_leaf( lex_.GetChar() ); + eat_token(); + break; + case R_Any:{ + node = new RegNode; + node->type = N_Class; + aptr ncls(new RegClass( 0, 65535, NULL )); + node->cls = ncls; + node->cmpcls = false; + eat_token(); + }break; + case R_Lcl: + eat_token(); + node = reclass(); + if( nextToken_ == R_Rcl ) + eat_token(); + else + err_ = true; + break; + case R_Lbr: + eat_token(); + node = expr(); + if( nextToken_ == R_Rbr ) + eat_token(); + else + err_ = true; + break; + default: + node = make_empty_leaf(); + err_ = true; + break; + } + return node; +} + +RegNode* RegParser::factor() +{ +// FACTOR ::= PRIMARY +// PRIMARY '*' +// PRIMARY '+' +// PRIMARY '?' + + RegNode* node = primary(); + switch( nextToken_ ) + { + case R_Star: node=make_node(N_Closure,node,NULL); eat_token();break; + case R_Plus: node=make_node(N_Closure1,node,NULL);eat_token();break; + case R_Quest:node=make_node(N_01,node,NULL ); eat_token();break; + } + return node; +} + +RegNode* RegParser::term() +{ +// TERM ::= EMPTY +// FACTOR TERM + + if( nextToken_ == R_End ) + return make_empty_leaf(); + + RegNode* node = factor(); + if( nextToken_==R_Lbr || nextToken_==R_Lcl + || nextToken_==R_Char|| nextToken_==R_Any ) + node = make_node( N_Concat, node, term() ); + return node; +} + +RegNode* RegParser::expr() +{ +// REGEXP ::= TERM +// TERM '|' REGEXP + + RegNode* node = term(); + if( nextToken_ == R_Bar ) + { + eat_token(); + node = make_node( N_Or, node, expr() ); + } + return node; +} + + + +//========================================================================= +//@{ +// 状態遷移 +//@} +//========================================================================= + +struct RegTrans +{ + enum { + Epsilon, + Class, + Char + } type; + aptr cls; // この文字集合 + // orEpsilon が来たら + bool cmpcls; + int to; // 状態番号toの状態へ遷移 + + aptr next; // 連結リスト +/* + template + bool match_i( wchar_t c, Cmp ) + { + c = Cmp::map(c); + RegClass* p = cls.get(); + while( p ) + if( Cmp::map(p->range.stt)<=c && c<=Cmp::map(p->range.end) ) + return true; + else + p = p->next.get(); + return false; + } +*/ + bool match_c( wchar_t c ) + { + for( RegClass* p=cls.get(); p; p=p->next.get() ) + if( p->range.stt<=c && c<=p->range.end ) + return true; + return false; + } + + bool match_i( wchar_t c ) + { + c = IgnoreCase::map(c); + for( RegClass* p=cls.get(); p; p=p->next.get() ) + if( IgnoreCase::map(p->range.stt)<=c + && c<=IgnoreCase::map(p->range.end) ) + return true; + return false; + } + + bool match( wchar_t c, bool caseS ) + { + bool m = caseS ? match_c( c ) : match_i( c ); + return cmpcls ? !m : m; + } +}; + + + +//========================================================================= +//@{ +// 構文木->NFA変換 +//@} +//========================================================================= + +class RegNFA +{ +public: + RegNFA( const wchar_t* pat ); + ~RegNFA(); + + int match( const wchar_t* str, int len, bool caseS ); + bool isHeadType() const { return parser.isHeadType(); } + bool isTailType() const { return parser.isTailType(); } + +private: + // マッチング処理 + int dfa_match( const wchar_t* str, int len, bool caseS ); + + struct st_ele { int st, ps; }; + void push(storage& stack, int curSt, int pos); + st_ele pop(storage& stack); + +private: + void add_transition( int from, wchar_t ch, int to ); + void add_transition( int from, aptr cls, bool cmp, int to ); + void add_e_transition( int from, int to ); + int gen_state(); + void gen_nfa( int entry, RegNode* t, int exit ); + +private: + RegParser parser; + storage st; + int start, final; +}; + +RegNFA::RegNFA( const wchar_t* pat ) + : parser( pat ) +{ + start = gen_state(); + final = gen_state(); + gen_nfa( start, parser.root(), final ); +} + +inline RegNFA::~RegNFA() +{ + for( ulong i=0,e=st.size(); i cls, bool cmp, int to ) +{ + RegTrans* x = new RegTrans; + aptr nn( st[from] ); + x->next = nn; + x->to = to; + x->type = RegTrans::Class; + x->cls = cls; + x->cmpcls= cmp; + st[from] = x; +} + +inline void RegNFA::add_transition( int from, wchar_t ch, int to ) +{ + aptr cls(new RegClass(ch,ch,NULL)); + add_transition( from, cls, false, to ); +} + +inline void RegNFA::add_e_transition( int from, int to ) +{ + RegTrans* x = new RegTrans; + aptr nn( st[from] ); + x->next = nn; + x->to = to; + x->type = RegTrans::Epsilon; + st[from] = x; +} + +inline int RegNFA::gen_state() +{ + st.Add( NULL ); + return st.size() - 1; +} + +void RegNFA::gen_nfa( int entry, RegNode* t, int exit ) +{ + switch( t->type ) + { + case N_Char: + // ch + // entry ----> exit + add_transition( entry, t->ch, exit ); + break; + case N_Class: + // cls + // entry -----> exit + add_transition( entry, t->cls, t->cmpcls, exit ); + break; + case N_Concat: { + // left right + // entry ------> step -------> exit + int step = gen_state(); + gen_nfa( entry, t->left.get(), step ); + gen_nfa( step, t->right.get(), exit ); + } break; + case N_Or: + // left + // ------> + // entry ------->--> exit + // right + gen_nfa( entry, t->left.get(), exit ); + gen_nfa( entry, t->right.get(), exit ); + break; + case N_Closure: + // e + // e <------ e + // entry ---> before ------> after ---> exit + // | left ^ + // >------->------------------->------>-| + // e + case N_Closure1: { + // e + // e <------ e + // entry ---> before ------> after ---> exit + // left + int before = gen_state(); + int after = gen_state(); + add_e_transition( entry, before ); + add_e_transition( after, exit ); + add_e_transition( after, before ); + gen_nfa( before, t->left.get(), after ); + if( t->type != N_Closure1 ) + add_e_transition( entry, exit ); + } break; + case N_01: + // e + // ------> + // entry ------> exit + // left + add_e_transition( entry, exit ); + gen_nfa( entry, t->left.get(), exit ); + break; + case N_Empty: + // e + // entry ---> exit + add_e_transition( entry, exit ); + break; + } +} + + + +//========================================================================= +//@{ +// マッチング +//@} +//========================================================================= + +void RegNFA::push(storage& stack, int curSt, int pos) +{ + // ε無限ループ防止策。同じ状態には戻らないように… + for( int i=stack.size()-1; i>=0; --i ) + if( stack[i].ps != pos ) + break; + else if( stack[i].st == curSt ) + return; + + st_ele nw = {curSt,pos}; + stack.Add( nw ); +} + +RegNFA::st_ele RegNFA::pop(storage& stack) +{ + st_ele se = stack[stack.size()-1]; + stack.ForceSize( stack.size()-1 ); + return se; +} + +int RegNFA::match( const wchar_t* str, int len, bool caseS ) +{ + if( parser.err() ) + return -1; // エラー状態なのでmatchとかできません + //if( st.size() <= 31 ) + // return dfa_match(str,len,caseS); // 状態数が少なければDFAを使う、かも + + int matchpos = -1; + + storage stack; + push(stack, start, 0); + while( stack.size() > 0 ) + { + // スタックからpop + st_ele se = pop(stack); + int curSt = se.st; + int pos = se.ps; + + // マッチ成功してたら記録 + if( curSt == final ) // 1==終状態 + if( matchpos < pos ) + matchpos = pos; + + // さらに先の遷移を調べる + if( matchpos < len ) + for( RegTrans* tr=st[curSt]; tr!=NULL; tr=tr->next.get() ) + if( tr->type == RegTrans::Epsilon ) + push(stack, tr->to, pos); + else if( posmatch( str[pos], caseS ) ) + push(stack, tr->to, pos+1); + } + + return matchpos; +} + +int RegNFA::dfa_match( const wchar_t* str, int len, bool caseS ) +{ + int matchpos = -1; + + unsigned int StateSet = (1<next.get() ) + if( tr->type == RegTrans::Epsilon ) + NewSS |= 1u << tr->to; + DifSS = (NewSS|StateSet) ^ StateSet; + StateSet |= NewSS; + } + + // 受理状態を含んでるかどうか判定 + if( StateSet & (1<next.get() ) + if( tr->type!=RegTrans::Epsilon && tr->match(str[pos], caseS) ) + NewSS |= 1u << tr->to; + StateSet = NewSS; + } + + return matchpos; +} + +////////////////////////////////////////////////////////////////////// + +bool reg_match( const wchar_t* pat, const wchar_t* str, bool caseS ) +{ + int len = my_lstrlenW(str); + + RegNFA re( pat ); + return len == re.match( str, len, caseS ); +} + + + +//========================================================================= +//@{ +// GreenPad用検索オブジェクト +//@} +//========================================================================= + +RSearch::RSearch( const unicode* key, bool caseS, bool down ) + : re_ ( new RegNFA(key) ) + , caseS_ ( caseS ) + , down_ ( down ) +{ +} + +bool RSearch::Search( + const unicode* str, ulong len, ulong stt, ulong* mbg, ulong* med ) +{ + if( down_ && re_->isHeadType() && stt>0 ) + return false; + + const int d = (down_ ? 1 : -1); + int s = (!down_ && re_->isHeadType() ? 0 : stt); + const int e = (down_ ? (re_->isHeadType() ? 1 : (long)len) : -1); + + for( ; s!=e; s+=d ) + { + const int L = re_->match( str+s, len-s, caseS_ ); + if( L > 0 ) + { + if( re_->isTailType() && L!=static_cast(len-s) ) + continue; + *mbg = static_cast(s); + *med = static_cast(s+L); + return true; + } + } + + return false; +} + + ADDED RSearch.h Index: RSearch.h ================================================================== --- RSearch.h +++ RSearch.h @@ -0,0 +1,45 @@ +#ifndef AFX_RSEARCH_H__5A9346D4_3152_4923_8EFC_38264A456364__INCLUDED_ +#define AFX_RSEARCH_H__5A9346D4_3152_4923_8EFC_38264A456364__INCLUDED_ +#include "kilib/ktlaptr.h" +#include "NSearch.h" + + +//========================================================================= +//@{ @pkg Gp.Search //@} +//@{ +// 超簡易正規表現マッチング関数。 +// +// patとstr全体がマッチするならtrue、ダメならfalseを返す +//@} +//========================================================================= + +bool reg_match( const wchar_t* pat, const wchar_t* str, bool caseS ); + + +class RegNFA; +//========================================================================= +//@{ +// Searhcableとしての実装 +//@} +//========================================================================= + +class RSearch : public Searchable +{ +public: + RSearch( const unicode* key, bool caseS, bool down ); + +private: + virtual bool Search( const unicode* str, ulong len, ulong stt, + ulong* mbg, ulong* med ); + +private: + ki::dptr re_; + bool caseS_; + bool down_; +}; + + + + + +#endif ADDED Search.cpp Index: Search.cpp ================================================================== --- Search.cpp +++ Search.cpp @@ -0,0 +1,471 @@ + +#include "stdafx.h" +#include "rsrc/resource.h" +#include "Search.h" +#include "NSearch.h" +#include "RSearch.h" +using namespace ki; +using namespace editwing; +using view::VPos; + + + +//------------------------------------------------------------------------- + +SearchManager::SearchManager( ki::Window& w, editwing::EwEdit& e ) + : searcher_( NULL ) + , edit_( e ) + , DlgImpl( IDD_FINDREPLACE ) + , bIgnoreCase_( true ) // 1.08 default true + , bRegExp_( false ) + , bDownSearch_( true ) + , mainWnd_( w ) +{ +} + +SearchManager::~SearchManager() +{ +} + +void SearchManager::SaveToINI( ki::IniFile& ini ) +{ + ini.SetSectionAsUserName(); + ini.PutBool( TEXT("SearchIgnoreCase"), bIgnoreCase_ ); + ini.PutBool( TEXT("SearchRegExp"), bRegExp_ ); +} + +void SearchManager::LoadFromINI( ki::IniFile& ini ) +{ + ini.SetSectionAsUserName(); + bIgnoreCase_ = ini.GetBool( TEXT("SearchIgnoreCase"), bIgnoreCase_ ); + bRegExp_ = ini.GetBool( TEXT("SearchRegExp"), bRegExp_ ); +} + +//------------------------------------------------------------------------- +// ダイアログ関係 +//------------------------------------------------------------------------- + +void SearchManager::ShowDlg() +{ +// GoModal( ::GetParent(edit_.hwnd()) ); + if( isAlive() ) + { + SetFront(); + } + else + { + GoModeless( ::GetParent(edit_.hwnd()) ); + ShowUp(); + } +} + +bool SearchManager::TrapMsg(MSG* msg) +{ + if( ! isAlive() || type()==MODAL ) + return false; + return DlgImpl::PreTranslateMessage(msg); +} + +void SearchManager::on_init() +{ + if( bIgnoreCase_ ) + SendMsgToItem( IDC_IGNORECASE, BM_SETCHECK, BST_CHECKED ); + if( bRegExp_ ) + SendMsgToItem( IDC_REGEXP, BM_SETCHECK, BST_CHECKED ); + + if( edit_.getCursor().isSelected() ) + { + // 選択されている状態では、基本的にそれをボックスに表示 + ulong dmy; + aarr str = edit_.getCursor().getSelectedStr(); + + ulong len=0; + for( ; str[len]!=L'\0' && str[len]!=L'\n'; ++len ); + str[len] = L'\0'; + + if( searcher_.isValid() && + searcher_->Search( str.get(), len, 0, &dmy, &dmy ) ) + { + SendMsgToItem( IDC_FINDBOX, WM_SETTEXT, 0, + reinterpret_cast(findStr_.c_str()) ); + } + else + { + #ifdef _UNICODE + SendMsgToItem( IDC_FINDBOX, WM_SETTEXT, 0, + reinterpret_cast(str.get()) ); + #else + ki::aarr ab( new TCHAR[(len+1)*3] ); + ::WideCharToMultiByte( CP_ACP, 0, str.get(), -1, + ab.get(), (len+1)*3, NULL, NULL ); + SendMsgToItem( IDC_FINDBOX, WM_SETTEXT, 0, + reinterpret_cast(ab.get()) ); + #endif + } + } + else + { + SendMsgToItem( IDC_FINDBOX, WM_SETTEXT, 0, + reinterpret_cast(findStr_.c_str()) ); + } + + SendMsgToItem( IDC_REPLACEBOX, WM_SETTEXT, 0, + reinterpret_cast(replStr_.c_str()) ); + + ::SetFocus( item(IDC_FINDBOX) ); + SendMsgToItem( IDC_FINDBOX, EM_SETSEL, 0, + ::GetWindowTextLength(item(IDC_FINDBOX)) ); +} + +void SearchManager::on_destroy() +{ + bChanged_ = false; +} + +bool SearchManager::on_command( UINT cmd, UINT id, HWND ctrl ) +{ + if( cmd==EN_CHANGE ) + { + // 文字列変更があったことを記憶 + bChanged_ = true; + } + else if( cmd==BN_CLICKED ) + { + switch( id ) + { + // チェックボックスの変更があったことを記憶 + case IDC_IGNORECASE: + case IDC_REGEXP: + bChanged_ = true; + break; + // ボタンが押された場合 + case ID_FINDNEXT: + on_findnext(); + break; + case ID_FINDPREV: + on_findprev(); + break; + case ID_REPLACENEXT: + on_replacenext(); + break; + case ID_REPLACEALL: + on_replaceall(); + break; + } + } + else + { + return false; + } + return true; +} + +void SearchManager::on_findnext() +{ + UpdateData(); + ConstructSearcher(); + if( isReady() ) + { + FindNextImpl(); +// End( IDOK ); + } +} + +void SearchManager::on_findprev() +{ + UpdateData(); + ConstructSearcher( false ); + if( isReady() ) + FindPrevImpl(); +} + +void SearchManager::on_replacenext() +{ + UpdateData(); + ConstructSearcher(); + if( isReady() ) + ReplaceImpl(); +} + +void SearchManager::on_replaceall() +{ + UpdateData(); + ConstructSearcher(); + if( isReady() ) + ReplaceAllImpl(); +} + +void SearchManager::UpdateData() +{ + // ダイアログから変更点を取り込み + bIgnoreCase_ = + (BST_CHECKED==SendMsgToItem( IDC_IGNORECASE, BM_GETCHECK )); + bRegExp_ = + (BST_CHECKED==SendMsgToItem( IDC_REGEXP, BM_GETCHECK )); + + TCHAR* str; + LRESULT n = SendMsgToItem( IDC_FINDBOX, WM_GETTEXTLENGTH ); + str = new TCHAR[n+1]; + SendMsgToItem( IDC_FINDBOX, WM_GETTEXT, + n+1, reinterpret_cast(str) ); + findStr_ = str; + delete [] str; + + n = SendMsgToItem( IDC_REPLACEBOX, WM_GETTEXTLENGTH ); + str = new TCHAR[n+1]; + SendMsgToItem( IDC_REPLACEBOX, WM_GETTEXT, + n+1, reinterpret_cast(str) ); + replStr_ = str; + delete [] str; +} + +void SearchManager::ConstructSearcher( bool down ) +{ + bChanged_ = (bChanged_ || (bDownSearch_ != down)); + if( (bChanged_ || !isReady()) && findStr_.len()!=0 ) + { + // 検索者作成 + bDownSearch_ = down; + const unicode *u = findStr_.ConvToWChar(); + + if( bRegExp_ ) + searcher_ = new RSearch( u, !bIgnoreCase_, bDownSearch_ ); + else + if( bDownSearch_ ) + if( bIgnoreCase_ ) + searcher_ = new NSearch(u); + else + searcher_ = new NSearch(u); + else + if( bIgnoreCase_ ) + searcher_ = new NSearchRev(u); + else + searcher_ = new NSearchRev(u); + + findStr_.FreeWCMem(u); + + // 変更終了フラグ + bChanged_ = false; + } +} + + + +//------------------------------------------------------------------------- + +void SearchManager::FindNext() +{ + if( !isReady() ) + { + ShowDlg(); + } + else + { + ConstructSearcher(); + if( isReady() ) + FindNextImpl(); + } +} + +void SearchManager::FindPrev() +{ + if( !isReady() ) + { + ShowDlg(); + } + else + { + ConstructSearcher( false ); + if( isReady() ) + FindPrevImpl(); + } +} + + + +//------------------------------------------------------------------------- +// 実際の処理の実装 +//------------------------------------------------------------------------- + +void SearchManager::FindNextImpl() +{ + // カーソル位置取得 + const VPos *stt, *end; + edit_.getCursor().getCurPos( &stt, &end ); + + // 選択範囲ありなら、選択範囲先頭の1文字先から検索 + // そうでなければカーソル位置から検索 + DPos s = *stt; + if( *stt != *end ) + if( stt->ad == edit_.getDoc().len(stt->tl) ) + s = DPos( stt->tl+1, 0 ); + else + s = DPos( stt->tl, stt->ad+1 ); + + // 検索 + DPos b, e; + if( FindNextFromImpl( s, &b, &e ) ) + { + // 見つかったら選択 + edit_.getCursor().MoveCur( b, false ); + edit_.getCursor().MoveCur( e, true ); + return; + } + + // 見つからなかった場合 + NotFound(); +} + +void SearchManager::NotFound() +{ + //MsgBox( String(IDS_NOTFOUND).c_str() ); + ::MessageBox( NULL, String(IDS_NOTFOUND).c_str(), NULL, MB_OK|MB_TASKMODAL ); +} + +void SearchManager::FindPrevImpl() +{ + // カーソル位置取得 + const VPos *stt, *end; + edit_.getCursor().getCurPos( &stt, &end ); + + if( stt->ad!=0 || stt->tl!=0 ) + { + // 選択範囲先頭の1文字前から検索 + DPos s; + if( stt->ad == 0 ) + s = DPos( stt->tl-1, edit_.getDoc().len(stt->tl-1) ); + else + s = DPos( stt->tl, stt->ad-1 ); + + // 検索 + DPos b, e; + if( FindPrevFromImpl( s, &b, &e ) ) + { + // 見つかったら選択 + edit_.getCursor().MoveCur( b, false ); + edit_.getCursor().MoveCur( e, true ); + return; + } + } + + // 見つからなかった場合 + NotFound(); +} + +bool SearchManager::FindNextFromImpl( DPos s, DPos* beg, DPos* end ) +{ + // 1行ずつサーチ + doc::Document& d = edit_.getDoc(); + for( ulong mbg,med,e=d.tln(); s.tlSearch( + d.tl(s.tl), d.len(s.tl), s.ad, &mbg, &med ) ) + { + beg->tl = end->tl = s.tl; + beg->ad = mbg; + end->ad = med; + return true; // 発見 + } + return false; +} + +bool SearchManager::FindPrevFromImpl( DPos s, DPos* beg, DPos* end ) +{ + // 1行ずつサーチ + doc::Document& d = edit_.getDoc(); + for( ulong mbg,med; ; s.ad=d.len(--s.tl) ) + { + if( searcher_->Search( + d.tl(s.tl), d.len(s.tl), s.ad, &mbg, &med ) ) + { + beg->tl = end->tl = s.tl; + beg->ad = mbg; + end->ad = med; + return true; // 発見 + } + if( s.tl==0 ) + break; + } + return false; +} + +void SearchManager::ReplaceImpl() +{ + // カーソル位置取得 + const VPos *stt, *end; + edit_.getCursor().getCurPos( &stt, &end ); + + // 選択範囲先頭から検索 + DPos b, e; + if( FindNextFromImpl( *stt, &b, &e ) ) + if( e == *end ) + { + const wchar_t* ustr = replStr_.ConvToWChar(); + const ulong ulen = my_lstrlenW( ustr ); + + // 置換 + edit_.getDoc().Execute( doc::Replace( + b, e, ustr, ulen + ) ); + + replStr_.FreeWCMem( ustr ); + + if( FindNextFromImpl( DPos(b.tl,b.ad+ulen), &b, &e ) ) + { + // 次を選択 + edit_.getCursor().MoveCur( b, false ); + edit_.getCursor().MoveCur( e, true ); + return; + } + } + else + { + // そうでなければとりあえず選択 + edit_.getCursor().MoveCur( b, false ); + edit_.getCursor().MoveCur( e, true ); + return; + } + + // 見つからなかった場合 + NotFound(); +} + +void SearchManager::ReplaceAllImpl() +{ + // まず、実行する置換を全てここに登録する + doc::MacroCommand mcr; + + // 置換後文字列 + const wchar_t* ustr = replStr_.ConvToWChar(); + const ulong ulen = my_lstrlenW( ustr ); + + // 文書の頭から検索 + int dif=0; + DPos s(0,0), b, e; + while( FindNextFromImpl( s, &b, &e ) ) + { + if( s.tl != b.tl ) dif = 0; + s = e; + + // 置換コマンドを登録 + b.ad += dif, e.ad += dif; + mcr.Add( new doc::Replace(b,e,ustr,ulen) ); + dif -= e.ad-b.ad-ulen; + } + + if( mcr.size() > 0 ) + { + // ここで連続置換 + edit_.getDoc().Execute( mcr ); + // カーソル移動 + e.ad = b.ad + ulen; + edit_.getCursor().MoveCur( e, false ); + // 閉じる? + End( IDOK ); + } + + TCHAR str[255]; + ::wsprintf( str, String(IDS_REPLACEALLDONE).c_str(), mcr.size() ); + MsgBox( str, String(IDS_APPNAME).c_str(), MB_ICONINFORMATION ); + + replStr_.FreeWCMem( ustr ); +} ADDED Search.h Index: Search.h ================================================================== --- Search.h +++ Search.h @@ -0,0 +1,130 @@ +#ifndef AFX_SEARCH_H__201E0D70_9C20_420A_8600_966D2BA23010__INCLUDED_ +#define AFX_SEARCH_H__201E0D70_9C20_420A_8600_966D2BA23010__INCLUDED_ +#include "editwing/editwing.h" +#include "kilib/window.h" +#include "kilib/memory.h" +#include "kilib/ktlaptr.h" +#include "kilib/string.h" + + + +//========================================================================= +//@{ @pkg Gp.Search //@} +//@{ +// 検索オブジェクト +//@} +//========================================================================= + +class Searchable : public ki::Object +{ +public: + //@{ + // 検索を行う + // @param str 対象文字列 + // @param len 対象文字列の長さ + // @param stt 検索開始index。0なら先頭から + // @param mbg マッチ結果の先頭index + // @param med マッチ結果の終端indexの1個後ろ + // @return マッチしたかどうか + // + // 下方向サーチオブジェクトの場合、stt <= *beg の範囲 + // 上方向サーチオブジェクトの場合、*beg <= stt の範囲を検索 + //@} + virtual bool Search( const unicode* str, ulong len, ulong stt, + ulong* mbg, ulong* med ) = 0; +}; + + + +//========================================================================= +//@{ +// 検索管理人 +// +// 前回検索したときのオプションや検索文字列を覚えておくのが +// このクラスの担当。検索・置換ダイアログの表示等もここで +// やるかもしれない。 +//@} +//========================================================================= + +class SearchManager : ki::DlgImpl +{ + typedef editwing::DPos DPos; + +public: + //@{ コンストラクタ。特記事項無し //@} + SearchManager( ki::Window& w, editwing::EwEdit& e ); + + //@{ デストラクタ。特記事項無し //@} + ~SearchManager(); + + //@{ 検索ダイアログ表示 //@} + void ShowDlg(); + + //@{ [次を検索]コマンド //@} + void FindNext(); + + //@{ [前を検索]コマンド //@} + void FindPrev(); + + //@{ 今すぐ検索可能か? //@} + bool isReady() const + { return searcher_.isValid(); } + + //@{ 設定Save //@} + void SaveToINI( ki::IniFile& ini ); + + //@{ 設定Load //@} + void LoadFromINI( ki::IniFile& ini ); + + //@{ 苦肉の策^^; //@} + bool TrapMsg(MSG* msg); + + //@{ 見つかりませんでしたダイアログ //@} + void NotFound(); + +private: + + //@{ [置換]コマンド //@} + void ReplaceImpl(); + + //@{ [全置換]コマンド //@} + void ReplaceAllImpl(); + +private: + + virtual void on_init(); + virtual void on_destroy(); + virtual bool on_command( UINT cmd, UINT id, HWND ctrl ); + void on_findnext(); + void on_findprev(); + void on_replacenext(); + void on_replaceall(); + void UpdateData(); + void ConstructSearcher( bool down=true ); + void FindNextImpl(); + void FindPrevImpl(); + bool FindNextFromImpl( DPos s, DPos* beg, DPos* end ); + bool FindPrevFromImpl( DPos s, DPos* beg, DPos* end ); + +private: + editwing::EwEdit& edit_; + ki::dptr searcher_; + ki::Window& mainWnd_; + + bool bIgnoreCase_; // 大文字小文字を同一視? + bool bRegExp_; // 正規表現? + bool bDownSearch_; // 検索方向 + bool bChanged_; // 前回のsearcher構築時から変更があったらtrue + + ki::String findStr_; + ki::String replStr_; + +private: + NOCOPY(SearchManager); +}; + + + +//========================================================================= + +#endif // AFX_SEARCH_H__201E0D70_9C20_420A_8600_966D2BA23010__INCLUDED_ ADDED editwing/editwing.h Index: editwing/editwing.h ================================================================== --- editwing/editwing.h +++ editwing/editwing.h @@ -0,0 +1,11 @@ +#ifndef _EDITWING_H_ +#define _EDITWING_H_ + + + +#include "ewCommon.h" +#include "ewCtrl1.h" + + + +#endif // _EDITWING_H_ ADDED editwing/ewCommon.h Index: editwing/ewCommon.h ================================================================== --- editwing/ewCommon.h +++ editwing/ewCommon.h @@ -0,0 +1,208 @@ +#ifndef _EDITWING_COMMON_H_ +#define _EDITWING_COMMON_H_ +#include "../kilib/kilib.h" +#ifndef __ccdoc__ +namespace editwing { +#endif + + +//========================================================================= +// Unicode関係 +//========================================================================= + +inline bool isHighSurrogate(unicode ch) +{ + return (0xD800 <= ch && ch <= 0xDBFF); +} + +inline bool isLowSurrogate(unicode ch) +{ + return (0xDC00 <= ch && ch <= 0xDFFF); +} + +//========================================================================= +//@{ @pkg editwing.Common //@} +//@{ +// テキスト中の位置情報 +//@} +//========================================================================= + +struct DPos : public ki::Object +{ + //@{ バッファ中のアドレス (0〜 ) //@} + ulong ad; + + //@{ 論理行番号 (0〜 ) //@} + ulong tl; + + bool operator == ( const DPos& r ) const + { return (tl==r.tl && ad==r.ad); } + bool operator != ( const DPos& r ) const + { return (tl!=r.tl || ad!=r.ad); } + bool operator < ( const DPos& r ) const + { return (tl ( const DPos& r ) const + { return (tl>r.tl || (tl==r.tl && ad>r.ad)); } + bool operator <= ( const DPos& r ) const + { return (tl= ( const DPos& r ) const + { return (tl>r.tl || (tl==r.tl && ad>=r.ad)); } + + DPos( ulong t, ulong a ) : tl(t), ad(a) {} + DPos() {} +}; + + + +//========================================================================= +//@{ +// 特殊文字を表す定数値 +//@} +//========================================================================= + +enum SpecialChars +{ + scEOF = 0, // EOF + scEOL = 1, // 改行 + scTAB = 2, // タブ + scHSP = 3, // 半角スペース + scZSP = 4 // 全角スペース +}; + + + +//========================================================================= +//@{ +// 単語の種類を表す定数値 +//@} +//========================================================================= + +enum TokenType +{ + TAB = 0x00, // Tab + WSP = 0x04, // 半角スペース + ALP = 0x08, // 普通の字 + CE = 0x0c, // コメント終了タグ + CB = 0x10, // コメント開始タグ + LB = 0x14, // 行コメント開始タグ + Q1 = 0x18, // 単一引用符 + Q2 = 0x1c // 二重引用符 +}; + + + +//========================================================================= +//@{ +// 色指定箇所を表す定数値 +//@} +//========================================================================= + +enum ColorType +{ + TXT = 0, // 文字色 + CMT = 1, // コメント文字色 + KWD = 2, // キーワード文字色 + // = 3, // ( コメントアウトされたキーワード文字色 ) + CTL = 4, // 特殊文字色 + BG = 5, // 背景色 + LN = 6 // 行番号 +}; + + + +//========================================================================= +//@{ +// 折り返し位置を示す定数値 +//@} +//========================================================================= + +enum WrapType +{ + NOWRAP = -1, // 折り返し無し + RIGHTEDGE = 0 // 右端 +}; + + + +//========================================================================= +//@{ +// 表示設定 +// +// フォント・色・タブ幅・特殊文字の表示、の情報を保持。 +// ただし、強調単語の指定は Document に対して行う。 +//@} +//========================================================================= + +struct VConfig : public ki::Object +{ + //@{ フォント //@} + LOGFONT font; + int fontsize; + + //@{ タブ幅文字数 //@} + int tabstep; + + //@{ 色 //@} + COLORREF color[7]; + + //@{ 特殊文字表示 //@} + bool sc[5]; + + //@{ 危険なデフォルトコンストラクタ //@} + VConfig() {} + + //@{ フォント関係初期化 //@} + VConfig( const TCHAR* fnam, int fsiz ) + { + SetFont( fnam,fsiz ); + tabstep = 4; + color[TXT] = + color[CMT] = + color[KWD] = + color[CTL] = RGB(0,0,0); + color[ BG] = RGB(255,255,255); + color[ LN] = RGB(0,0,0);//255,255,0); + sc[scEOF] = + sc[scEOL] = + sc[scTAB] = + sc[scHSP] = + sc[scZSP] = false; + } + + //@{ フォント関係設定 //@} + void SetFont( const TCHAR* fnam, int fsiz ) + { + fontsize = fsiz; + font.lfWidth = 0; + font.lfEscapement = 0; + font.lfOrientation = 0; + font.lfWeight = FW_DONTCARE; + font.lfItalic = FALSE; + font.lfUnderline = FALSE; + font.lfStrikeOut = FALSE; + font.lfOutPrecision = OUT_DEFAULT_PRECIS; + font.lfClipPrecision = CLIP_DEFAULT_PRECIS; + font.lfQuality = DEFAULT_QUALITY; + font.lfPitchAndFamily = VARIABLE_PITCH|FF_DONTCARE; + font.lfCharSet = DEFAULT_CHARSET; + + ::lstrcpy( font.lfFaceName, fnam ); + + HDC h = ::GetDC( NULL ); + font.lfHeight = -MulDiv(fsiz, ::GetDeviceCaps(h,LOGPIXELSY), 72); + ::ReleaseDC( NULL, h ); + } + + //@{ タブ幅設定 //@} + void SetTabStep( int tab ) + { + tabstep = Max( 1, tab ); + } +}; + + + +//========================================================================= + +} // namespace editwing +#endif // _EDITWING_COMMON_H_ ADDED editwing/ewCtrl1.h Index: editwing/ewCtrl1.h ================================================================== --- editwing/ewCtrl1.h +++ editwing/ewCtrl1.h @@ -0,0 +1,58 @@ +#ifndef _EDITWING_CTRL1_H_ +#define _EDITWING_CTRL1_H_ +#include "ewDoc.h" +#include "ewView.h" +#ifndef __ccdoc__ +namespace editwing { +#endif + + + +//========================================================================= +//@{ @pkg editwing.Ctrl //@} +//@{ +// 簡単なエディットコントロール +// +// とりあえず字が表示できて色が変えられてカーソルが動かせて… +// という、Doc/Viewの基本機能をそのまま使った形のもの。 +// ウインドウ分割対応版とかもそのうち作るかもしれない。 +//@} +//========================================================================= + +class EwEdit : public ki::WndImpl +{ +public: + + EwEdit(); + ~EwEdit(); + +public: + + //@{ 文書データ操作 //@} + doc::Document& getDoc() { return *doc_; } + + //@{ 表示機能操作 //@} + view::View& getView() { return *view_; } + + //@{ カーソル機能操作 //@} + view::Cursor& getCursor() { return view_->cur(); } + +private: + + ki::dptr doc_; + ki::dptr view_; + static ClsName className_; + +private: + + void on_create( CREATESTRUCT* cs ); + void on_destroy(); + LRESULT on_message( UINT msg, WPARAM wp, LPARAM lp ); +}; + + + +//========================================================================= + +} // namespace editwing +#endif // _EDITWING_CTRL1_H_ ADDED editwing/ewDoc.h Index: editwing/ewDoc.h ================================================================== --- editwing/ewDoc.h +++ editwing/ewDoc.h @@ -0,0 +1,303 @@ +#ifndef _EDITWING_DOC_H_ +#define _EDITWING_DOC_H_ +#include "ewCommon.h" +#ifndef __ccdoc__ +namespace editwing { +namespace doc { +#endif + + + +class DocImpl; +class DocEvHandler; +class Command; +class Insert; +class Delete; +class Replace; + + + +//========================================================================= +//@{ @pkg editwing.Doc //@} +//@{ +// 文書データ +// +// このクラスは単なるインターフェイスで、内部実装は +// class DocImpl で行う。ので、詳しくはそちらを参照のこと。 +//@} +//========================================================================= + +class Document : public ki::Object +{ +public: + + //@{ 何もしないコンストラクタ //@} + Document(); + ~Document(); + + //@{ ファイルを開く //@} + void OpenFile( ki::aptr tf ); + + //@{ ファイルを保存 //@} + void SaveFile( ki::TextFileW& tf ); + + //@{ 内容破棄 //@} + void ClearAll(); + + //@{ 操作コマンド実行 //@} + void Execute( const Command& cmd ); + + //@{ アンドゥ //@] + void Undo(); + + //@{ リドゥ //@] + void Redo(); + + //@{ アンドゥ回数制限 //@] + void SetUndoLimit( long lim ); + + //@{ 変更フラグをクリア //@} + void ClearModifyFlag(); + + //@{ イベントハンドラ登録 //@} + void AddHandler( DocEvHandler* eh ); + + //@{ イベントハンドラ解除 //@} + void DelHandler( DocEvHandler* eh ); + + //@{ キーワード定義切り替え //@} + void SetKeyword( const unicode* defbuf, ulong siz=0 ); + +public: + + //@{ 内部実装クラス //@} + DocImpl& impl() { return *impl_; } + + //@{ 行数 //@} + ulong tln() const; + + //@{ 行バッファ //@} + const unicode* tl( ulong i ) const; + + //@{ 行文字数 //@} + ulong len( ulong i ) const; + + //@{ 指定範囲のテキストの長さ //@} + ulong getRangeLength( const DPos& stt, const DPos& end ) const; + + //@{ 指定範囲のテキスト //@} + void getText( unicode* buf, const DPos& stt, const DPos& end ) const; + + //@{ アンドゥ可能? //@} + bool isUndoAble() const; + + //@{ リドゥ可能? //@} + bool isRedoAble() const; + + //@{ 変更済み? //@} + bool isModified() const; + + //@{ ビジーフラグ(マクロコマンド実行中のみ成立) //@} + void setBusyFlag( bool b=true ) { busy_ = b; } + bool isBusy() const { return busy_; } + +private: + + // 実装 + ki::dptr impl_; + bool busy_; + +private: + + NOCOPY(Document); +}; + + + +//========================================================================= +//@{ +// イベントハンドラインターフェイス +// +// ドキュメントから発生するイベント(挿入/削除などなど…)を +// 受け取りたい場合は、このインターフェイスを継承し、適宜ハンドラを +// 書くこと。Viewの再描画処理などもこれを通じて実行されている。 +//@} +//========================================================================= + +class DocEvHandler +{ +public: + //@{ + // テキスト内容が変更されたときに発生 + // @param s 変更範囲の先頭 + // @param e 変更範囲の終端(前) + // @param e2 変更範囲の終端(後) + // @param reparsed e2より後ろのコメントアウト状態が変化していたらtrue + // @param nmlcmd 挿入/削除/置換ならtrue、ファイル開き/全置換ならfalse + //@} + virtual void on_text_update( const DPos& s, + const DPos& e, const DPos& e2, bool reparsed, bool nmlcmd ) {} + + //@{ + // キーワードが変更されたときに発生 + //@} + virtual void on_keyword_change() {} + + //@{ + // ダーティフラグが変更されたときに発生 + //@} + virtual void on_dirtyflag_change( bool dirty ) {} +}; + + + +//========================================================================= +//@{ +// 操作コマンドインターフェイス +// +// ドキュメントは、Command から派生したクラスのインスタンスの +// operator() を呼び出すことで、色々な操作を実行する。とりあえず +// 具体的には Insert/Delete/Replace の3つだけ。あとでマクロコマンド用 +// クラスも作るつもりだけど、とりあえずは保留。 +//@} +//========================================================================= + +class Command : public ki::Object +{ +protected: + friend class UnReDoChain; + friend class MacroCommand; + virtual Command* operator()( Document& doc ) const = 0; +}; + + + +//========================================================================= +//@{ +// 挿入コマンド +//@} +//========================================================================= + +class Insert : public Command +{ +public: + + //@{ + // @param s 挿入位置 + // @param str 挿入文字列 + // @param len 文字列の長さ + // @param del コマンド終了時にdelete [] strしてよいか? + //@} + Insert( const DPos& s, const unicode* str, ulong len, bool del=false ); + ~Insert(); + +private: + + Command* operator()( Document& doc ) const; + DPos stt_; + const unicode* buf_; + ulong len_; + bool del_; +}; + + + +//========================================================================= +//@{ +// 削除コマンド +//@} +//========================================================================= + +class Delete : public Command +{ +public: + + //@{ + // @param s 開始位置 + // @param e 終端位置 + //@} + Delete( const DPos& s, const DPos& e ); + +private: + + Command* operator()( Document& doc ) const; + DPos stt_; + DPos end_; +}; + + + + +//========================================================================= +//@{ +// 置換コマンド +//@} +//========================================================================= + +class Replace : public Command +{ +public: + + //@{ + // @param s 開始位置 + // @param e 終端位置 + // @param str 挿入文字列 + // @param len 文字列の長さ + // @param del コマンド終了時にdelete [] strしてよいか? + //@} + Replace( const DPos& s, const DPos& e, + const unicode* str, ulong len, bool del=false ); + ~Replace(); + +private: + + Command* operator()( Document& doc ) const; + DPos stt_; + DPos end_; + const unicode* buf_; + ulong len_; + bool del_; +}; + + + +//========================================================================= +//@{ +// マクロコマンド +// +// 複数のコマンドを一つのコマンドとして連続実行する。 +// ただし、Insert/Delete/Replaceを一回行うたびに当然 +// 文字列の位置は変化するのだが、それに関する変換処理は +// 行わない。すなわち、Insert->Delete->Insert みたいな +// 連続処理を書くときは、行数や文字数の変化を考慮しながら +// 値を定めていくことが必要になる。ので、あんまり使えない(^^; +//@} +//========================================================================= + +class MacroCommand : public Command +{ +public: + //@{ コマンドの追加 //@} + void Add( Command* cmd ) { arr_.Add(cmd); } + + //@{ コマンド数 //@} + ulong size() const { return arr_.size(); } + + //@ デストラクタ //@} + ~MacroCommand() + { + for( ulong i=0,e=arr_.size(); i arr_; +}; + + + +//========================================================================= + +}} // namespace editwing::document +#endif // _EDITWING_DOC_H_ ADDED editwing/ewView.h Index: editwing/ewView.h ================================================================== --- editwing/ewView.h +++ editwing/ewView.h @@ -0,0 +1,229 @@ +#ifndef _EDITWING_VIEW_H_ +#define _EDITWING_VIEW_H_ +#include "ewCommon.h" +#include "ewDoc.h" +#ifndef __ccdoc__ +namespace editwing { +namespace view { +#endif + + + +class Canvas; +class ViewImpl; +class Cursor; +class Caret; + + + +//========================================================================= +//@{ @pkg editwing.View //@} +//@{ +// 描画処理など +// +// このクラスでは、メッセージの分配を行うだけで、実装は +// Canvas/ViewImpl 等で行う。ので、詳しくはそちらを参照のこと。 +//@} +//========================================================================= + +class View : public ki::WndImpl, public doc::DocEvHandler +{ +public: + + //@{ 何もしないコンストラクタ //@} + View( doc::Document& d, HWND wnd ); + ~View(); + + //@{ 折り返し方式切替 //@} + void SetWrapType( int wt ); + + //@{ 行番号表示/非表示切替 //@} + void ShowLineNo( bool show ); + + //@{ 表示色・フォント切替 //@} + void SetFont( const VConfig& vc ); + + //@{ 内部実装 //@} + ViewImpl& impl() { return *impl_; } + + //@{ カーソル //@} + Cursor& cur(); + +private: + + doc::DocImpl& doc_; + ki::dptr impl_; + static ClsName className_; + +private: + + void on_create( CREATESTRUCT* cs ); + void on_destroy(); + LRESULT on_message( UINT msg, WPARAM wp, LPARAM lp ); + void on_text_update( const DPos& s, const DPos& e, const DPos& e2, bool bAft, bool mCur ); + void on_keyword_change(); +}; + + + +//========================================================================= +//@{ +// イベントハンドラインターフェイス +// +// カーソルから発生するイベント色々を +//@} +//========================================================================= + +class CurEvHandler +{ + friend class Cursor; + virtual void on_move( const DPos& c, const DPos& s ) {} + virtual void on_char( Cursor& cur, unicode wch ); + virtual void on_key( Cursor& cur, int vk, bool sft, bool ctl ); + virtual void on_ime( Cursor& cur, unicode* str, ulong len ); +}; + + + +//========================================================================= +//@{ +// 表示位置情報まで含めたDPos +//@} +//========================================================================= + +struct VPos : public DPos +{ + ulong vl; // VLine-Index + ulong rl; // RLine-Index + int vx; // スクロールを考慮しない仮想スクリーン上のx座標(pixel) + int rx; // 文字の並びに左右されてないx座標(pixel) + // == 長い行のしっぽから短い行に [↑] で移動して + // == その後 [↓] で戻れるようなアレです。 + void operator=( const DPos& dp ) { tl=dp.tl, ad=dp.ad; } + + VPos(bool) : DPos(0,0),vl(0),rl(0),vx(0),rx(0) {} + VPos() {} +}; + + + +//========================================================================= +//@{ +// カーソル +//@} +//========================================================================= + +class Cursor : public ki::Object +{ +public: + + // 初期化とか + Cursor( HWND wnd, ViewImpl& vw, doc::DocImpl& dc ); + ~Cursor(); + void AddHandler( CurEvHandler* ev ); + void DelHandler( CurEvHandler* ev ); + + // カーソル移動 + void MoveCur( const DPos& dp, bool select ); + + // キーによるカーソル移動 + void Left( bool wide, bool select ); + void Right( bool wide, bool select ); + void Up( bool wide, bool select ); + void Down( bool wide, bool select ); + void Home( bool wide, bool select ); + void End( bool wide, bool select ); + void PageUp( bool select ); + void PageDown( bool select ); + + // テキスト書き換え + void Input( const unicode* str, ulong len ); + void Input( const char* str, ulong len ); + void InputChar( unicode ch ); + void Del(); + void DelBack(); + + // クリップボード + void Cut(); + void Copy(); + void Paste(); + + // 選択テキスト取得 + ki::aarr getSelectedStr() const; + + // モード切替 + void SetInsMode( bool bIns ); + void SetROMode( bool bRO ); + +public: + + bool isInsMode() const; + bool isROMode() const; + bool isSelected() const; + bool getCurPos( const VPos** start, const VPos** end ) const; + void ResetPos(); + void on_scroll_begin(); + void on_scroll_end(); + void on_text_update( const DPos& s, const DPos& e, const DPos& e2, bool mCur ); + void on_setfocus(); + void on_killfocus(); + void on_keydown( int vk, LPARAM flag ); + void on_char( TCHAR ch ); + void on_ime_composition( LPARAM lp ); + void on_lbutton_down( short x, short y, bool shift ); + void on_mouse_move( short x, short y ); + void on_lbutton_up( short x, short y ); + void on_lbutton_dbl( short x, short y ); + bool on_contextmenu( short x, short y ); + void on_timer(); + int on_ime_reconvertstring( RECONVERTSTRING* rs ); + bool on_ime_confirmreconvertstring( RECONVERTSTRING* rs ); + +private: + + doc::DocImpl& doc_; + ViewImpl& view_; + CurEvHandler* pEvHan_; + ki::dptr caret_; + + VPos cur_; // カーソル位置 + VPos sel_; // 選択時の軸足位置 + bool bIns_; // 挿入モード? + bool bRO_; // 読取専用? + + UINT_PTR timerID_;// マウスドラッグ制御用の + int keyRepTime_; // タイマー関係 + int dragX_; // 位置 + int dragY_; // 位置 + bool lineSelectMode_; // 行選択モード? + + CurEvHandler defaultHandler_; + +private: + + void MoveByMouse( int x, int y ); + void MoveTo( const VPos& vp, bool sel ); + void Ud( int dy, bool select ); + void UpdateCaretPos(); + void Redraw( const VPos& s, const VPos& e ); +}; + + + +//------------------------------------------------------------------------- + +inline bool Cursor::isSelected() const + { return cur_!=sel_; } + +inline bool Cursor::isInsMode() const + { return bIns_; } + +inline bool Cursor::isROMode() const + { return bRO_; } + + + +//========================================================================= + +}} // namespace editwing::view +#endif // _EDITWING_VIEW_H_ ADDED editwing/ip_ctrl1.cpp Index: editwing/ip_ctrl1.cpp ================================================================== --- editwing/ip_ctrl1.cpp +++ editwing/ip_ctrl1.cpp @@ -0,0 +1,124 @@ +#include "stdafx.h" +#include "ewCtrl1.h" +using namespace ki; +using namespace editwing; + + + +//------------------------------------------------------------------------- +// EwEditコントロール作成/破棄 +//------------------------------------------------------------------------- + +EwEdit::ClsName EwEdit::className_ = TEXT("EditWing Control-01"); + +EwEdit::EwEdit() + : WndImpl( className_, WS_CHILD|WS_VISIBLE, WS_EX_CLIENTEDGE ) +{ + static bool ClassRegistered = false; + if( !ClassRegistered ) + { + ClassRegistered = true; + + // 初回構築時のみ、クラス登録を行う + WNDCLASSEX wc = {0}; + wc.lpszClassName = className_; + WndImpl::Register( &wc ); + } +} + +EwEdit::~EwEdit() +{ + Destroy(); +} + +void EwEdit::on_create( CREATESTRUCT* cs ) +{ + doc_ = new doc::Document; + view_ = new view::View( *doc_, hwnd() ); +} + +void EwEdit::on_destroy() +{ + view_ = NULL; + doc_ = NULL; +} + + + +//------------------------------------------------------------------------- +// 簡単なメッセージ制御 +//------------------------------------------------------------------------- + +LRESULT EwEdit::on_message( UINT msg, WPARAM wp, LPARAM lp ) +{ + switch( msg ) + { + case WM_SETFOCUS: + { + view_->SetFocus(); + break; + } + case WM_SIZE: + { + RECT rc; + getClientRect( &rc ); + view_->MoveTo( 0, 0, rc.right, rc.bottom ); + break; + } + case EM_CANUNDO: return getDoc().isUndoAble(); + case EM_SETREADONLY: getCursor().SetROMode(wp!=FALSE); return TRUE; + case WM_COPY: getCursor().Copy(); return 0; + case WM_CUT: getCursor().Cut(); return 0; + case WM_PASTE: getCursor().Paste(); return 0; + case EM_UNDO: + case WM_UNDO: getDoc().Undo(); return TRUE; + default: + return WndImpl::on_message( msg, wp, lp ); + } + return 0; +/* +EM_CHARFROMPOS +EM_EMPTYUNDOBUFFER: +EM_FMTLINES +EM_GETCUEBANNER +EM_GETFIRSTVISIBLELINE +EM_GETHANDLE +EM_GETIMESTATUS +EM_GETLIMITTEXT +EM_GETLINE +EM_GETLINECOUNT +EM_GETMARGINS +EM_GETMODIFY +EM_GETPASSWORDCHAR +EM_GETRECT +EM_GETSEL +EM_GETTHUMB +EM_GETWORDBREAKPROC +EM_HIDEBALLOONTIP +EM_LIMITTEXT +EM_LINEFROMCHAR +EM_LINEINDEX +EM_LINELENGTH +EM_LINESCROLL +EM_POSFROMCHAR +EM_REPLACESEL +EM_SCROLL +EM_SCROLLCARET +EM_SETCUEBANNER +EM_SETHANDLE +EM_SETIMESTATUS +EM_SETLIMITTEXT +EM_SETMARGINS +EM_SETMODIFY +EM_SETPASSWORDCHAR +EM_SETRECT +EM_SETRECTNP +EM_SETSEL +EM_SETTABSTOPS +EM_SETWORDBREAKPROC +EM_SHOWBALLOONTIP +WM_COMMAND +WM_CTLCOLOREDIT +WM_CTLCOLORSTATIC +*/ +} ADDED editwing/ip_cursor.cpp Index: editwing/ip_cursor.cpp ================================================================== --- editwing/ip_cursor.cpp +++ editwing/ip_cursor.cpp @@ -0,0 +1,799 @@ +#include "stdafx.h" +#include "ip_view.h" +using namespace editwing; +using namespace editwing::view; +using doc::Insert; +using doc::Delete; +using doc::Replace; + + + +//========================================================================= +//---- ip_cursor.cpp カーソルコントロール +// +// カレットを表示したりIMEに適当に対応したり色々。 +// ところで疑問なのだが Caret って「カレット」と +// 読むのか「キャレット」と読むのか? +// +//---- ip_text.cpp 文字列操作・他 +//---- ip_parse.cpp キーワード解析 +//---- ip_wrap.cpp 折り返し +//---- ip_scroll.cpp スクロール +//---- ip_draw.cpp 描画・他 +//========================================================================= + + + +//------------------------------------------------------------------------- +// Caret制御用ラッパー +//------------------------------------------------------------------------- + +class editwing::view::Caret : public Object +{ +public: + + Caret( HWND wnd ) + : hwnd_( wnd ), created_( false ) {} + + ~Caret() + { Destroy(); } + + void Show() + { if( created_ ) ::ShowCaret( hwnd_ ); } + + void Hide() + { if( created_ ) ::HideCaret( hwnd_ ); } + + void Destroy() + { if( created_ ) ::DestroyCaret(), created_=false; } + + void SetPos( int x, int y ) + { if( created_ ) ::SetCaretPos(x,y), ime().SetPos(hwnd_,x,y); } + + void Create( int H, int W, const LOGFONT& lf ) + { + if( created_ ) + ::DestroyCaret(); + created_ = true; + ::CreateCaret( hwnd_, NULL, W, H ); + ime().SetFont( hwnd_, lf ); + Show(); + } + + bool isAlive() + { return created_; } + + HWND hwnd() + { return hwnd_; } + +private: + + const HWND hwnd_; + bool created_; +}; + + + +//------------------------------------------------------------------------- +// カーソル初期化 +//------------------------------------------------------------------------- + +Cursor::Cursor( HWND wnd, ViewImpl& vw, doc::DocImpl& dc ) + : view_ ( vw ) + , doc_ ( dc ) + , pEvHan_ ( &defaultHandler_ ) + , caret_ ( new Caret(wnd) ) + , bIns_ ( true ) + , bRO_ ( false ) + , timerID_( 0 ) + , lineSelectMode_( false ) +{ + // てきとーに情報初期化 + ::SystemParametersInfo( SPI_GETKEYBOARDSPEED, 0, &keyRepTime_, 0 ); + cur_.tl = cur_.ad = cur_.vl = cur_.rl = 0; + cur_.vx = cur_.rx = 0; sel_ = cur_; +} + +Cursor::~Cursor() +{ +} + +void Cursor::AddHandler( CurEvHandler* ev ) +{ + pEvHan_ = ev; +} + +void Cursor::DelHandler( CurEvHandler* ev ) +{ + if( ev == pEvHan_ ) + pEvHan_ = &defaultHandler_; +} + + + +//------------------------------------------------------------------------- +// ヘルパー関数群 +//------------------------------------------------------------------------- + +void Cursor::UpdateCaretPos() +{ + // メンバ変数の値を元に、実際にCaretを動かす処理 + int x, y; + view_.GetOrigin( &x, &y ); + x += cur_.vx; + y += cur_.vl * view_.fnt().H(); + + // 行番号ゾーンにCaretがあっても困るので左に追いやる + if( 0SetPos( x, y ); + pEvHan_->on_move( cur_, sel_ ); +} + +void Cursor::Redraw( const VPos& s, const VPos& e ) +{ + int x, y; // 原点 + view_.GetOrigin( &x, &y ); + + POINT sp = {x+s.vx, y+s.vl*view_.fnt().H()}; + POINT ep = {x+e.vx, y+e.vl*view_.fnt().H()}; + if( s > e ) // Swap + sp.x^=ep.x, ep.x^=sp.x, sp.x^=ep.x, + sp.y^=ep.y, ep.y^=sp.y, sp.y^=ep.y; + ep.x+=2; + + // 手抜き16bitチェック入り… + const long LFT = view_.left(); + const long RHT = view_.right(); + const long TOP = 0; + const int BTM = view_.bottom(); + + if( sp.y == ep.y ) + { + RECT rc = { Max(LFT,sp.x), sp.y, Min(RHT,ep.x), sp.y+view_.fnt().H() }; + ::InvalidateRect( caret_->hwnd(), &rc, FALSE ); + } + else + { + RECT rc = { Max(LFT,sp.x), Max(TOP,sp.y), RHT, Min(BTM,sp.y+view_.fnt().H()) }; + ::InvalidateRect( caret_->hwnd(), &rc, FALSE ); + RECT re = { LFT, Max(TOP,ep.y), Min(RHT,ep.x), Min(BTM,ep.y+view_.fnt().H()) }; + ::InvalidateRect( caret_->hwnd(), &re, FALSE ); + RECT rd = { LFT, Max(TOP,rc.bottom), RHT, Min((long)BTM,re.top) }; + ::InvalidateRect( caret_->hwnd(), &rd, FALSE ); + } +} + +bool Cursor::getCurPos( const VPos** start, const VPos** end ) const +{ + *start = *end = &cur_; + if( cur_==sel_ )//|| !caret_->isAlive() ) + return false; + if( cur_ < sel_ ) + *end = &sel_; + else + *start = &sel_; + return true; +} + + + + +//------------------------------------------------------------------------- +// Viewからの指令を処理 +//------------------------------------------------------------------------- + +void Cursor::on_setfocus() +{ + caret_->Create( view_.fnt().H(), + (bIns_ ? 2 : view_.fnt().W()), view_.fnt().LogFont() ); + UpdateCaretPos(); +} + +void Cursor::on_killfocus() +{ + caret_->Destroy(); + Redraw( cur_, sel_ ); +} + +void Cursor::on_scroll_begin() +{ + caret_->Hide(); +} + +void Cursor::on_scroll_end() +{ + UpdateCaretPos(); + caret_->Show(); +} + +void Cursor::ResetPos() +{ + // 設定変更などに対応 + view_.ConvDPosToVPos( cur_, &cur_ ); + view_.ConvDPosToVPos( sel_, &sel_ ); + UpdateCaretPos(); + if( caret_->isAlive() ) + view_.ScrollTo( cur_ ); +} + +void Cursor::on_text_update + ( const DPos& s, const DPos& e, const DPos& e2, bool mCur ) +{ + VPos* search_base = NULL; + + if( mCur && s==cur_ && e==sel_ ) + { + search_base = &cur_; + } + else if( mCur && s==sel_ && e==cur_ ) + { + search_base = &sel_; + } + else + { + Redraw( cur_, sel_ ); + if( mCur && caret_->isAlive() ) + { + if( cur_ <= s ) + search_base = &cur_; + } + else + { + if( s < cur_ ) + { + if( cur_ <= e ) + cur_ = e2; + else if( cur_.tl == e.tl ) + cur_.tl=e2.tl, cur_.ad=e2.ad+cur_.ad-e.ad; + else + cur_.tl=e2.tl-e.tl; + view_.ConvDPosToVPos( cur_, &cur_ ); + } + if( s < sel_ ) + sel_ = cur_; + } + } + + if( mCur ) + { + view_.ConvDPosToVPos( e2, &cur_, search_base ); + sel_ = cur_; + if( caret_->isAlive() ) + view_.ScrollTo( cur_ ); + } + UpdateCaretPos(); +} + + + +//------------------------------------------------------------------------- +// キー入力への対応 +//------------------------------------------------------------------------- + +void CurEvHandler::on_char( Cursor& cur, unicode wch ) +{ + cur.InputChar( wch ); +} + +void CurEvHandler::on_ime( Cursor& cur, unicode* str, ulong len ) +{ + cur.Input( str, len ); +} + +void CurEvHandler::on_key( Cursor& cur, int vk, bool sft, bool ctl ) +{ + switch( vk ) + { + case VK_HOME: cur.Home( ctl, sft ); break; + case VK_END: cur.End( ctl, sft ); break; + case VK_RIGHT: cur.Right( ctl, sft ); break; + case VK_LEFT: cur.Left( ctl, sft ); break; + case VK_UP: cur.Up( ctl, sft ); break; + case VK_DOWN: cur.Down( ctl, sft ); break; + case VK_PRIOR: cur.PageUp( sft ); break; + case VK_NEXT: cur.PageDown( sft ); break; + case VK_DELETE: cur.Del(); break; + case VK_BACK: cur.DelBack(); break; + case VK_INSERT: cur.SetInsMode(!cur.isInsMode()); break; + } +} + +void Cursor::on_char( TCHAR ch ) +{ + if( !bRO_ && ch!=0x7f + && ((unsigned)ch>=0x20 || ch==TEXT('\r') || ch==TEXT('\t')) ) + { + #ifdef _UNICODE + pEvHan_->on_char( *this, ch ); + #else + unicode wc = ch; + if( ch & 0x80 ) // 非ASCII文字にはトリビアルでない変換が必要 + ::MultiByteToWideChar( CP_ACP, MB_COMPOSITE, &ch, 1, &wc, 1 ); + pEvHan_->on_char( *this, wc ); + #endif + } +} + +void Cursor::on_ime_composition( LPARAM lp ) +{ + view_.ScrollTo( cur_ ); + if( !bRO_ && (lp&GCS_RESULTSTR) ) + { + unicode* str; + ulong len; + ime().GetString( caret_->hwnd(), &str, &len ); + if( str ) + { + pEvHan_->on_ime( *this, str, len ); + delete [] str; + } + } +} + +void Cursor::on_keydown( int vk, LPARAM flag ) +{ + bool sft = (::GetKeyState(VK_SHIFT)>>15)!=0; + bool ctl = (::GetKeyState(VK_CONTROL)>>15)!=0; + pEvHan_->on_key( *this, vk, sft, ctl ); +} + + + +//------------------------------------------------------------------------- +// モード切替 +//------------------------------------------------------------------------- + +void Cursor::SetInsMode( bool bIns ) +{ + bIns_ = bIns; + on_setfocus(); +} + +void Cursor::SetROMode( bool bRO ) +{ + bRO_ = bRO; +} + + + +//------------------------------------------------------------------------- +// 文字入力・削除 +//------------------------------------------------------------------------- + +void Cursor::InputChar( unicode ch ) +{ + // 「上書モード & 選択状態でない & 行末でない」なら右一文字選択 + if( !bIns_ && cur_==sel_ && doc_.len(cur_.tl)!=cur_.ad ) + Right( false, true ); + + // 入力 + Input( &ch, 1 ); +} + +void Cursor::Input( const unicode* str, ulong len ) +{ + if( cur_==sel_ ) + doc_.Execute( Insert( cur_, str, len ) ); + else + doc_.Execute( Replace( cur_, sel_, str, len ) ); +} + +void Cursor::Input( const char* str, ulong len ) +{ + unicode* ustr = new unicode[ len*4 ]; + len = ::MultiByteToWideChar( CP_ACP, 0, str, len, ustr, len*4 ); + Input( ustr, len ); + delete [] ustr; +} + +void Cursor::DelBack() +{ + // 選択状態なら BackSpace == Delete + // でなければ、 BackSpace == Left + Delete (手抜き + if( cur_ == sel_ ) + { + if( cur_.tl==0 && cur_.ad==0 ) + return; + Left( false, false ); + } + Del(); +} + +void Cursor::Del() +{ + // 選択状態なら cur_ 〜 sel_ を削除 + // でなければ、 cur_ 〜 rightOf(cur_) を削除 + DPos dp = (cur_==sel_ ? doc_.rightOf(cur_) : (DPos)sel_ ); + if( cur_ != dp ) + doc_.Execute( Delete( cur_, dp ) ); +} + + + +//------------------------------------------------------------------------- +// テキスト取得 +//------------------------------------------------------------------------- + +ki::aarr Cursor::getSelectedStr() const +{ + DPos dm=cur_, dM=sel_; + if( cur_ > sel_ ) + dm=sel_, dM=cur_; + + // テキスト取得 + int len = doc_.getRangeLength( dm, dM ); + ki::aarr ub( new unicode[len+1] ); + doc_.getText( ub.get(), dm, dM ); + return ub; +} + +//------------------------------------------------------------------------- +// クリップボード処理 +//------------------------------------------------------------------------- + +void Cursor::Cut() +{ + if( cur_ != sel_ ) + { + // コピーして削除 + Copy(); + Del(); + } +} + +void Cursor::Copy() +{ + Clipboard clp( caret_->hwnd(), false ); + if( cur_==sel_ || !clp.isOpened() ) + return; + + DPos dm=cur_, dM=sel_; + if( cur_ > sel_ ) + dm=sel_, dM=cur_; + + HGLOBAL h; + unicode* p; + int len = doc_.getRangeLength( dm, dM ); + + if( app().isNT() ) + { + // NT系ならそのままダイレクトに + h = ::GlobalAlloc( GMEM_MOVEABLE, (len+1)*2 ); + doc_.getText( static_cast(::GlobalLock(h)), dm, dM ); + ::GlobalUnlock( h ); + clp.SetData( CF_UNICODETEXT, h ); + } + else + { + // 9x系なら変換が必要 + h = ::GlobalAlloc( GMEM_MOVEABLE, (len+1)*3 ); + p = new unicode[len+1]; + doc_.getText( p, dm, dM ); + ::WideCharToMultiByte( CP_ACP, 0, p, -1, + static_cast(::GlobalLock(h)), (len+1)*3, NULL, NULL ); + ::GlobalUnlock( h ); + clp.SetData( CF_TEXT, h ); + delete [] p; + } +} + +void Cursor::Paste() +{ + Clipboard clp( caret_->hwnd(), true ); + if( clp.isOpened() ) + { + Clipboard::Text txt = clp.GetUnicodeText(); + if( txt.data() != NULL ) + doc_.Execute( + Replace( cur_, sel_, txt.data(), my_lstrlenW(txt.data()) ) + ); + } +} + + + +//------------------------------------------------------------------------- +// カーソル移動 +//------------------------------------------------------------------------- + +void Cursor::MoveCur( const DPos& dp, bool select ) +{ + VPos vp; + view_.ConvDPosToVPos( dp, &vp ); + MoveTo( vp, select ); +} + +void Cursor::MoveTo( const VPos& vp, bool sel ) +{ + if( sel ) + { + // 選択状態が変わる範囲を再描画 + Redraw( vp, cur_ ); + } + else + { + // 選択解除される範囲を再描画 + if( cur_ != sel_ ) + Redraw( cur_, sel_ ); + sel_ = vp; + } + cur_ = vp; + UpdateCaretPos(); + view_.ScrollTo( cur_ ); +} + +void Cursor::Home( bool wide, bool select ) +{ + VPos np; + np.ad = np.vx = np.rx = np.rl = 0; + if( wide ) // 文書の頭へ + np.tl = np.vl = 0; + else // 行の頭へ + { + // 1.07.4 --> 1.08 :: Virtual Home + // np.tl = cur_.tl, np.vl = cur_.vl-cur_.rl; + + if( cur_.rl == 0 ) + np.tl = cur_.tl, np.vl = cur_.vl-cur_.rl; + else + view_.ConvDPosToVPos( doc_.rightOf(DPos(cur_.tl, view_.rlend(cur_.tl,cur_.rl-1))), &np, &cur_ ); + } + MoveTo( np, select ); +} + +void Cursor::End( bool wide, bool select ) +{ + VPos np; + if( wide ) // 文書の末尾へ + { + np.tl = doc_.tln()-1; + np.vl = view_.vln()-1; + } + else // 行の末尾へ + { + // 1.07.4 --> 1.08 :: Virtual End + // np.tl = cur_.tl; + // np.vl = cur_.vl + view_.rln(np.tl) - 1 - cur_.rl; + + view_.ConvDPosToVPos( DPos(cur_.tl, view_.rlend(cur_.tl,cur_.rl)), &np, &cur_ ); + MoveTo( np, select ); + return; + } + np.ad = doc_.len(np.tl); + np.rl = view_.rln(np.tl)-1; + np.rx = np.vx = view_.GetLastWidth( np.tl ); + + MoveTo( np, select ); +} + +void Cursor::Ud( int dy, bool select ) +{ + // はみ出す場合は、先頭行/終端行で止まるように制限 + VPos np = cur_; + if( (signed)np.vl + dy < 0 ) + dy = -(signed)np.vl; + else if( np.vl + dy >= view_.vln() ) + dy = view_.vln()-np.vl-1; + + np.vl += dy; + np.rl += dy; + if( dy<0 ) // 上へ戻る場合 + { + // ジャンプ先論理行の行頭へDash! + while( (signed)np.rl < 0 ) + np.rl += view_.rln(--np.tl); + } + else if( dy>0 ) // 下へ進む場合 + { + // ジャンプ先論理行の行頭へDash! + while( (signed)np.rl > 0 ) + np.rl -= view_.rln(np.tl++); + if( (signed)np.rl < 0 ) + np.rl += view_.rln(--np.tl); //行き過ぎ修正〜 + } + + // x座標決定にかかる + const unicode* str = doc_.tl(np.tl); + + // 右寄せになってる。不自然? + np.ad = (np.rl==0 ? 0 : view_.rlend(np.tl,np.rl-1)+1); + np.vx = (np.rl==0 ? 0 : view_.fnt().W(&str[np.ad-1])); + while( np.vx < np.rx && np.ad < view_.rlend(np.tl,np.rl) ) + { + // 左寄せにしてみた。 + ulong newvx; + if( str[np.ad] == L'\t' ) + newvx = view_.fnt().nextTab(np.vx); + else + newvx = np.vx + view_.fnt().W(&str[np.ad]); + if( newvx > ulong(np.rx) ) + break; + np.vx = newvx; + ++np.ad; + } + + MoveTo( np, select ); +} + +void Cursor::Up( bool wide, bool select ) +{ + Ud( wide?-3:-1, select ); +} + +void Cursor::Down( bool wide, bool select ) +{ + Ud( wide?3:1, select ); +} + +void Cursor::PageUp( bool select ) +{ + Ud( -view_.cy()/view_.fnt().H(), select ); +} + +void Cursor::PageDown( bool select ) +{ + Ud( view_.cy()/view_.fnt().H(), select ); +} + +void Cursor::Left( bool wide, bool select ) +{ + VPos np; + if( cur_!=sel_ && !select ) + np = Min( cur_, sel_ ), np.rx = np.vx; + else + view_.ConvDPosToVPos( doc_.leftOf(cur_,wide), &np, &cur_ ); + MoveTo( np, select ); +} + +void Cursor::Right( bool wide, bool select ) +{ + VPos np; + if( cur_!=sel_ && !select ) + np = Max( cur_, sel_ ), np.rx = np.vx; + else + view_.ConvDPosToVPos( doc_.rightOf(cur_,wide), &np, &cur_ ); + MoveTo( np, select ); +} + + + +//------------------------------------------------------------------------- +// マウス入力への対応 +//------------------------------------------------------------------------- + +void Cursor::on_lbutton_dbl( short x, short y ) +{ + // 行番号ゾーンの場合は特に何もしない + if( view_.lna()-view_.fnt().F() < x ) + // 行末の場合も特に何もしない + if( cur_.ad != doc_.len(cur_.tl) ) + { + VPos np; + view_.ConvDPosToVPos( doc_.wordStartOf(cur_), &np, &cur_ ); + MoveTo( np, false ); + Right( true, true ); + } +} + +bool Cursor::on_contextmenu( short x, short y ) +{ + // Not Tracked + return false; +} + +void Cursor::on_lbutton_down( short x, short y, bool shift ) +{ + if( !shift ) + { + // これまでの選択範囲をクリア + Redraw( cur_, sel_ ); + + // 行番号ゾーンのクリックだったら、行選択モードに + lineSelectMode_ = ( x < view_.lna()-view_.fnt().F() ); + + // 選択開始位置を調整 + view_.GetVPos( x, y, &sel_ ); + if( lineSelectMode_ ) + view_.ConvDPosToVPos( DPos(sel_.tl,0), &sel_, &sel_ ); + cur_ = sel_; + } + + // 移動! + MoveByMouse( dragX_=x, dragY_=y ); + + // マウス位置の追跡開始 + timerID_ = ::SetTimer( caret_->hwnd(), 178116, keyRepTime_, NULL ); + ::SetCapture( caret_->hwnd() ); +} + +void Cursor::on_lbutton_up( short x, short y ) +{ + // 追跡解除 + if( timerID_ != 0 ) + { + ::ReleaseCapture(); + ::KillTimer( caret_->hwnd(), timerID_ ); + timerID_ = 0; + } +} + +void Cursor::on_mouse_move( short x, short y ) +{ + if( timerID_ != 0 ) + { + // View内部ならMouseMoveに反応 + POINT pt = { dragX_=x, dragY_=y }; + if( PtInRect( &view_.zone(), pt ) ) + MoveByMouse( dragX_, dragY_ ); + } +} + +void Cursor::on_timer() +{ + // View外部ならTimerに反応 + POINT pt = { dragX_, dragY_ }; + if( !PtInRect( &view_.zone(), pt ) ) + MoveByMouse( dragX_, dragY_ ); +} + +void Cursor::MoveByMouse( int x, int y ) +{ + VPos vp; + view_.GetVPos( x, y, &vp, lineSelectMode_ ); + MoveTo( vp, true ); +} + +//------------------------------------------------------------------------- +// 再変換 +//------------------------------------------------------------------------- + +int Cursor::on_ime_reconvertstring( RECONVERTSTRING* rs ) +{ + if( ! isSelected() || cur_.tl != sel_.tl ) + return 0; + +#ifdef _UNICODE + aarr str = getSelectedStr(); +#else + aarr str; + { + aarr ub = getSelectedStr(); + ulong len; + for(len=0; ub[len]; ++len); + ki::aarr nw( new TCHAR[(len+1)*3] ); + str = nw; + ::WideCharToMultiByte( CP_ACP, 0, ub.get(), -1, + str.get(), (len+1)*3, NULL, NULL ); + } +#endif + const ulong len = ::lstrlen(str.get()); + if( rs != NULL ) + { + rs->dwSize = sizeof(RECONVERTSTRING) + (len+1)*sizeof(TCHAR); + rs->dwVersion = 0; + rs->dwStrOffset = sizeof(RECONVERTSTRING); + rs->dwStrLen = len; + rs->dwCompStrOffset = 0; + rs->dwCompStrLen = len; + rs->dwTargetStrOffset = 0; + rs->dwTargetStrLen = len; + memmove( ((char*)rs)+rs->dwStrOffset, str.get(), (len+1)*sizeof(TCHAR) ); + + if( sel_ < cur_ ) + { + DPos psel_ = sel_; + MoveCur( cur_, false ); + MoveCur( psel_, true ); + } + } + return sizeof(RECONVERTSTRING) + (len+1)*sizeof(TCHAR); +} + +bool Cursor::on_ime_confirmreconvertstring( RECONVERTSTRING* rs ) +{ + return false; +} + ADDED editwing/ip_doc.h Index: editwing/ip_doc.h ================================================================== --- editwing/ip_doc.h +++ editwing/ip_doc.h @@ -0,0 +1,463 @@ +#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( mem().Alloc((alen_+1)*2+alen_) ) ) + , flg_ ( reinterpret_cast(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( mem().Alloc((alen_+1)*2+alen_) ); + uchar* tmpF = + reinterpret_cast(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( ; pnext_ != &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 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_; // 文字列解析役 + gapbufobj text_; // テキストデータ + mutable storage pEvHan_; // イベント通知先 + UnReDoChain urdo_; // アンドゥリドゥ + + aptr 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_ ADDED editwing/ip_draw.cpp Index: editwing/ip_draw.cpp ================================================================== --- editwing/ip_draw.cpp +++ editwing/ip_draw.cpp @@ -0,0 +1,619 @@ +#include "stdafx.h" +#include "ip_view.h" +using namespace editwing; +using namespace editwing::view; + + + +//========================================================================= +//---- ip_draw.cpp 描画・他 +// +// 折り返しとか色とかを考慮しつつ、実際に描画処理を +// 行うのがここ。あとメッセージディスパッチャなども +// ついでにこのファイルに。^^; +// +//---- ip_text.cpp 文字列操作・他 +//---- ip_parse.cpp キーワード解析 +//---- ip_wrap.cpp 折り返し +//---- ip_scroll.cpp スクロール +//---- ip_cursor.cpp カーソルコントロール +//========================================================================= + + + +//------------------------------------------------------------------------- +// Viewの初期化・解放 +//------------------------------------------------------------------------- + +View::ClsName + View::className_ = TEXT("EditWing View"); + +View::View( doc::Document& d, HWND wnd ) + : WndImpl( className_, WS_CHILD|WS_VISIBLE|WS_VSCROLL|WS_HSCROLL ) + , doc_ ( d.impl() ) +{ + static bool ClassRegistered = false; + if( !ClassRegistered ) + { + // 初回構築時のみ、クラス登録を行う + ClassRegistered = true; + WNDCLASSEX wc = {0}; + wc.lpszClassName = className_; + wc.style = CS_DBLCLKS | CS_OWNDC; + wc.hCursor = app().LoadOemCursor( IDC_IBEAM ); + + // GlobalIMEを有効にする + ATOM a = WndImpl::Register( &wc ); + ime().FilterWindows( &a, 1 ); + } + + // 窓作成 + Create( NULL, wnd ); +} + +View::~View() +{ + // 窓破棄 + Destroy(); +} + +void View::on_create( CREATESTRUCT* cs ) +{ + impl_ = new ViewImpl( *this, doc_ ); + doc_.AddHandler( this ); +} + +void View::on_destroy() +{ + doc_.DelHandler( this ); + impl_ = NULL; +} + + + +//------------------------------------------------------------------------- +// サブオブジェクトにそのまま回す +//------------------------------------------------------------------------- + +void View::SetWrapType( int wt ) + { impl_->SetWrapType( wt ); } + +void View::ShowLineNo( bool show ) + { impl_->ShowLineNo( show ); } + +void View::SetFont( const VConfig& vc ) + { impl_->SetFont( vc ); } + +void View::on_keyword_change() + { ::InvalidateRect( hwnd(), NULL, FALSE ); } + +void View::on_text_update + ( const DPos& s, const DPos& e, const DPos& e2, bool bAft, bool mCur ) + { impl_->on_text_update( s, e, e2, bAft, mCur ); } + +Cursor& View::cur() + { return impl_->cur(); } + +LRESULT View::on_message( UINT msg, WPARAM wp, LPARAM lp ) +{ + switch( msg ) + { + case WM_PAINT:{ + PAINTSTRUCT ps; + ::BeginPaint( hwnd(), &ps ); + impl_->on_paint( ps ); + ::EndPaint( hwnd(), &ps ); + }break; + + case WM_SIZE: + impl_->on_view_resize( LOWORD(lp), HIWORD(lp) ); + break; + + case WM_HSCROLL: + impl_->on_hscroll( LOWORD(wp) ); + break; + + case WM_VSCROLL: + impl_->on_vscroll( LOWORD(wp) ); + break; + + case WM_MOUSEWHEEL: + impl_->on_wheel( HIWORD(wp) ); + break; + + case WM_SETFOCUS: + cur().on_setfocus(); + break; + + case WM_KILLFOCUS: + cur().on_killfocus(); + break; + + case WM_TIMER: + cur().on_timer(); + break; + + case WM_KEYDOWN: + cur().on_keydown( (int)wp, lp ); + break; + + case WM_CHAR: + cur().on_char( (TCHAR)wp ); + break; + + case WM_LBUTTONDOWN: + cur().on_lbutton_down( LOWORD(lp), HIWORD(lp), (wp&MK_SHIFT)!=0 ); + break; + + case WM_LBUTTONUP: + cur().on_lbutton_up( LOWORD(lp), HIWORD(lp) ); + break; + + case WM_LBUTTONDBLCLK: + cur().on_lbutton_dbl( LOWORD(lp), HIWORD(lp) ); + break; + + case WM_MOUSEMOVE: + cur().on_mouse_move( LOWORD(lp), HIWORD(lp) ); + break; + + case WM_CONTEXTMENU: + if( !cur().on_contextmenu( LOWORD(lp), HIWORD(lp) ) ) + return WndImpl::on_message( msg, wp, lp ); + break; + + case WM_IME_REQUEST: + switch( wp ) + { + case IMR_RECONVERTSTRING: + return cur().on_ime_reconvertstring( + reinterpret_cast(lp) ); + case IMR_CONFIRMRECONVERTSTRING: + return cur().on_ime_confirmreconvertstring( + reinterpret_cast(lp) ); + } + break; + + case WM_IME_STARTCOMPOSITION: + cur().on_ime_composition( 0 ); + return WndImpl::on_message( msg, wp, lp ); + + case WM_IME_COMPOSITION: + cur().on_ime_composition( lp ); + if( lp&GCS_RESULTSTR ) + break; + // fall through... + + default: + return WndImpl::on_message( msg, wp, lp ); + } + return 0; +} + + + +//------------------------------------------------------------------------- +// 線を引くとか四角く塗るとか、そーいう基本的な処理 +//------------------------------------------------------------------------- + +Painter::Painter( HDC hdc, const VConfig& vc ) + : dc_ ( hdc ) + , font_ ( ::CreateFontIndirect( &vc.font ) ) + , pen_ ( ::CreatePen( PS_SOLID, 0, vc.color[CTL] ) ) + , brush_ ( ::CreateSolidBrush( vc.color[BG] ) ) + , widthTable_( new int[65536] ) +{ + // 制御文字を描画するか否か?のフラグを記憶 + for( int i=0; i 0 ) + { + RECT rc = { 0, 0, lna(), bottom() }; + ::InvalidateRect( hwnd_, &rc, FALSE ); + } + break; + + case LINE: // 指定した行の後半 + case AFTER: // 指定した行以下全部 + + { + DPos st = ( s->ad==0 ? *s : doc_.leftOf(*s,true) ); + InvalidateView( st, r==AFTER ); + } + } +} + + + +//------------------------------------------------------------------------- +// WM_PAINTハンドラ +//------------------------------------------------------------------------- + +void ViewImpl::on_paint( const PAINTSTRUCT& ps ) +{ + // 描画範囲の情報を詳しく取得 + Painter& p = cvs_.getPainter(); + VDrawInfo v( ps.rcPaint ); + GetDrawPosInfo( v ); + + if( ps.rcPaint.right <= lna() ) + { + // case A: 行番号表示域のみ更新 + DrawLNA( v, p ); + } + else if( lna() <= ps.rcPaint.left ) + { + // case B: テキスト表示域のみ更新 + DrawTXT( v, p ); + } + else + { + // case C: 両方更新 + DrawLNA( v, p ); + p.SetClip( cvs_.zone() ); + DrawTXT( v, p ); + p.ClearClip(); + } +} + + + +//------------------------------------------------------------------------- +// 行番号ゾーン描画 +//------------------------------------------------------------------------- + +void ViewImpl::DrawLNA( const VDrawInfo& v, Painter& p ) +{ + // + // 文字列のまま足し算を行うルーチン + // + struct strint { + strint( ulong num ) { + int i=11; + while( num ) digit[--i] = (unicode)(L'0'+(num%10)), num/=10; + while( i ) digit[--i] = L' '; + } + void operator++() { + int i=10; + do + if( digit[i] == L'9' ) + digit[i] = L'0'; + else + { ++digit[i]; return; } + while( digit[--i] != L' ' ); + digit[i] = L'1'; + } + void Output( Painter& f, int x, int y ) { + for( unicode* p=digit+10; *p!=L' '; --p,x-=f.F() ) + f.CharOut( *p, x, y ); + } + unicode digit[11]; + }; + + // 背面消去 + RECT rc = { v.rc.left, v.rc.top, lna(), v.rc.bottom }; + p.Fill( rc ); + + if( v.rc.top < v.YMAX ) + { + // 境界線表示 + int line = lna() - p.F()/2; + p.DrawLine( line, v.rc.top, line, v.YMAX ); + p.SetColor( LN ); + + // 行番号表示 + strint n = v.TLMIN+1; + int y = v.YMIN; + int edge = lna() - p.F()*2; + for( ulong i=v.TLMIN; y( v.YMAX, a.top+rln(tl)*H ); + + // 作業用変数2 + ulong stt=0, end, t, n; + + // 表示行単位のLoop + for( ulong rl=0; a.top>5); + n = i + t; + if( n >= end ) + n = end; + else if( t==7 || t==0 ) + while( n>5)==0 ) + ++n; + + // x2, i2 := このTokenの右端 + i2 ++; + x2 = (str[i]==L'\t' ? p.nextTab(x2) : x2+p.W(&str[i])); + // if( x2 <= v.XMIN ) + // x=x2, i=i2; + while( i2', x+v.XBASE, a.top ); + } + break; + case L' ': + if( p.sc(scHSP) ) + p.DrawHSP( x+v.XBASE, a.top, i2-i ); + break; + case 0x3000://L' ': + if( p.sc(scZSP) ) + p.DrawZSP( x+v.XBASE, a.top, i2-i ); + break; + default: + if( clr != (flg[i]&3) ) + p.SetColor( clr=(flg[i]&3) ); + p.StringOut( str+i, i2-i, x+v.XBASE, a.top ); + //p.StringOut( str+i, i2-i, x+v.XBASE, a.top ); + // 何故だか2度描きしないとうまくいかん… + break; + } + } + + // 選択範囲だったら反転 + if( v.SYB<=a.top && a.top<=v.SYE ) + Inv( a.top, a.top==v.SYB?v.SXB:(v.XBASE), + a.top==v.SYE?v.SXE:(v.XBASE+x), p ); + + // 行末より後ろの余白を背景色塗 + if( x> prev.head)&1; +// で順次計算していくことが出来る。 +// この計算の際に内部バッファの状態まで書き換えるのは +// コストがでかすぎるので、次に示すフラグを見ながら +// 描画寸前に適宜調整する。 +// +// ----------------------------------------------- +// +// Line::commentBitReady_ +// コメントビットが調整済みかどうか +// +// ----------------------------------------------- +// +// Line::str_[] +// UCS-2ベタで、文字列データがそのまま格納される。 +// ただし、パーサの高速化のために最終文字の後ろに +// 0x007fが付加される。 +// +// ----------------------------------------------- +// +// Line::flg_ +// 一文字毎に、下のような8bitのフラグを割り当てる +// | aaabbbcd | +// +// ----------------------------------------------- +// +// aaa == "PosInToken" +// 0: トークンの途中 +// 1-6: トークンの頭。次の頭は1-6文字先。 +// 7: トークンの頭。次の頭は7文字以上先。 +// +// ----------------------------------------------- +// +// bbb == "TokenType" +// 0: TAB: タブ文字 +// 1: WSP: ホワイトスペース +// 2: TXT: 普通の文字 +// 3: CE: コメント開始タグ +// 4: CB: コメント終了タグ +// 5: LB: 行コメント開始タグ +// 6: Q1: '' 引用符1 +// 7: Q2: "" 引用符2 +// +// ----------------------------------------------- +// +// c == "isKeyword?" +// 0: キーワードではない +// 1: キーワード +// +// ----------------------------------------------- +// +// d == "inComment?" +// 0: コメントの中ではない +// 1: コメントの中 +// +// ----------------------------------------------- + + + +namespace { +//------------------------------------------------------------------------- +// コメントの中なのか外なのか等を判定するためのオートマトン +// +// /* が出たらその後ろはコメントで */ が出たらその後ろはノーマルゾーン +// …という単純な規則では上手く行かない。例えば str"/*"str なんてものが +// 出現した場合に困ってしまう。そこで、 +// ・普通のテキスト +// ・ブロックコメントの中 +// ・行コメントの中 +// ・一重引用符の中 +// ・二重引用符の中 +// の5種類の状態に分けて、それぞれの場合について、どの記号が出たら +// 次にどの状態に移るのか…を処理する必要がある。その状態変化の規則を +// 5x5の2次元配列で与えて管理する。 +//------------------------------------------------------------------------- + +enum CommentDFASymbol{ sCB, sCE, sLB, sQ1, sQ2, sXXX }; +struct CommentDFA +{ + // <状態> + // 最下位bitが、現在コメント内かどうかのフラグになります。 + // ブロックコメント中かどうかは (state>>1)&(state) で。 + // 000: normal text 011: in BlockComment + // 001: in LineComment 100: in Quote2 + // 010: in Quote1 + // + // <シンボル> + // C++で言うと下の通り + // 値はTokenTypeフラグとシンクロするようになってます。 + // 000: CE */ 011: Q1 ' + // 001: CB /* 100: Q2 " + // 010: LB // + + // 初期状態を指定。コメント内かコメント外か + CommentDFA( bool inComment ) + : state( inComment ? 3 : 0 ) {} + + // 入力符号を与えて状態遷移 + void transit( int sym ) + { state = tr_table[state][sym]; } + + // 現在の状態 + int state; + + // 状態遷移テーブル + static const int tr_table[5][5]; +}; + +const int CommentDFA::tr_table[5][5] = { + {0,3,1,2,4}, + {1,1,1,1,1}, + {2,2,2,0,2}, + {0,3,3,3,3}, + {4,4,4,4,0}, +}; + + + +//------------------------------------------------------------------------- +// 単純な、キーワード格納構造体。 +// ChainHashの要素にするためnextポインタがつけてあります。 +//------------------------------------------------------------------------- + +struct Keyword : public Object +{ + unicode* str; + const ulong len; + Keyword* next; + + Keyword( const unicode* s, ulong l ) + : str( new unicode[l+1] ) + , len( l ) + , next( NULL ) + { memmove( str, s, l*sizeof(unicode) ); } + + ~Keyword() + { delete [] str; } +}; + + + +//------------------------------------------------------------------------- +// サポート関数。Unicodeテキスト同士の比較 +//------------------------------------------------------------------------- + +static bool compare_s(const unicode* a,const unicode* b,ulong l) +{ + // 大文字小文字を区別 + while( l-- ) + if( *a++ != *b++ ) + return false; + return true; +} + +static bool compare_i(const unicode* a,const unicode* b,ulong l) +{ + // 大文字小文字を区別しない(雑) + while( l-- ) + if( ((*a++) ^ (*b++)) & 0xdf ) + return false; + return true; +} + + + +//------------------------------------------------------------------------- +// 与えられた記号文字列から、コメント開始等の意味のあるトークンを +// 切り出してくるための構造。 +//------------------------------------------------------------------------- + +class TagMap +{ + Keyword* tag_[3]; // 0:CE 1:CB 2:LB + bool esc_, q1_, q2_, map_[128]; + +public: + + TagMap( const unicode* cb, ulong cblen, + const unicode* ce, ulong celen, + const unicode* lb, ulong lblen, + bool q1, bool q2, bool esc ) + : q1_ ( q1 ) + , q2_ ( q2 ) + , esc_( esc ) + { + // '/' で始まる記号は使われているか…? + // みたいな、1文字目のみのチェックに使う表を作成 + tag_[0] = tag_[1] = tag_[2] = NULL; + mem00( map_, sizeof(map_) ); + map_[L'\''] = q1; + map_[L'\"'] = q2; + map_[L'\\'] = esc; + if( celen!=0 ){ map_[*ce]=true; tag_[0]=new Keyword(ce,celen); } + if( cblen!=0 ){ map_[*cb]=true; tag_[1]=new Keyword(cb,cblen); } + if( lblen!=0 ){ map_[*lb]=true; tag_[2]=new Keyword(lb,lblen); } + } + + ~TagMap() + { + // キーワード解放 + delete tag_[0]; + delete tag_[1]; + delete tag_[2]; + } + + bool does_esc() + { + // \ によるエスケープをするかどうか + return esc_; + } + + ulong SymbolLoop( + const unicode* str, ulong len, ulong& mlen, int& sym ) + { + // 有意味な記号にマッチするまでループ + // 返値に、マッチするまでに飛ばした文字数、 + // mlen,symに、マッチした記号の情報を返す + + int i; + ulong ans=0; + for( sym=sXXX, mlen=1; ans=0; --i ) + if( tag_[i]!=NULL + && tag_[i]->len <= len-ans + && compare_s( + tag_[i]->str, str+ans, tag_[i]->len ) ) + { + sym = i; + mlen = tag_[i]->len; + goto symbolfound; + } + if( str[ans] == L'\'' ) // 一重引用符 + { + if( q1_ ) + { + sym = sQ1; + goto symbolfound; + } + } + else if( str[ans] == L'\"' ) // 二重引用符 + { + if( q2_ ) + { + sym = sQ2; + goto symbolfound; + } + } + else if( str[ans] == L'\\' ) // \ の後の文字はSkip + { + if( esc_ && ans+1 dustbox_; + bool (*compare_)(const unicode*,const unicode*,ulong); + +public: + + KeywordMap( bool bCaseSensitive ) + : compare_( bCaseSensitive ? compare_s : compare_i ) + { + // ハッシュ表初期化 + mem00( backet_, sizeof(backet_) ); + } + + ~KeywordMap() + { + // 解放 + for( ulong i=0; inext; + while( p!=NULL ) + q=p, p=p->next; + q->next = x; + } + + // データクリア用のリストにも入れておく + dustbox_.Add(x); + } + + ulong isKeyword( const unicode* str, ulong len ) + { + // 登録されているキーワードと一致するか? + for( Keyword* p=backet_[hash(str,len)]; p!=NULL; p=p->next ) + if( p->len==len && compare_( p->str, str, len ) ) + return 2; + return 0; + } + +private: + + static int hash( const unicode* a, ulong al ) + { + // 12bitに潰すめっちゃ雑なハッシュ関数 + // ルーチン分けるの面倒なので、大文字小文字は常に区別されない。(^^; + int h=0,i=0; + while( al-- ) + { + h ^= ((*(a++)&0xdf)<7 ? 7<<5 : _d<<5 ) + #define tkenc(_d) ( (_d)>7 ? 0xe0 : (_d)<<5 ) + + // コメント状態遷移追跡用オートマトン + CommentDFA dfa[2] = {CommentDFA(false), CommentDFA(true)}; + int& cmtState = dfa[line.isLineHeadCmt()].state; + int commentbit = cmtState&1; + + // 作業領域 + int sym; + ulong j, k, um, m; + uchar t, f; + + // ループ〜 + const unicode* str = line.str(); + uchar* flg = line.flg(); + ulong ie = line.size(); + for( ulong i=0; i= 0x007f ) + { + f = (ALP | commentbit); + if( str[i] == 0x3000 )//L' ' ) + while( str[++j] == 0x3000 ) + flg[j] = f; + else + while( str[++j] >= 0x80 && str[j]!=0x3000 ) + flg[j] = f; + flg[i] = static_cast(tkenc(j-i) | f); + } + // ASCII文字の場合?? + else + { + t = letter_type[str[i]]; + if( t==S && tag_.does_esc() ) + do + if( j+1= j ) + break; + + // マッチした部分 + f = (CE | commentbit); + dfa[0].transit( sym ); + dfa[1].transit( sym ); + commentbit = cmtState&1; + if( sym != 0 ) // 0:comment end + f = (((sym+3)<<2) | commentbit); + flg[k++] = (uchar)(tkenc(m)|f); + while( --m ) + flg[k++] = f; + } + break; + } + } + } + + // transitフラグ更新 + line.SetTransitFlag( + (dfa[1].state & (dfa[1].state<<1)) | + ((dfa[0].state>>1) & dfa[0].state) + ); + line.CommentBitUpdated(); + return line.TransitCmt( cmst ); + } + + // コメントビットを正しく調整 + void SetCommentBit( Line& line ) + { + CommentDFA dfa( line.isLineHeadCmt()==1 ); + ulong commentbit = dfa.state&1; + + // ループ〜 + const unicode* str = line.str(); + uchar* flg = line.flg(); + ulong j,k,ie = line.size(); + for( ulong i=0; i>5); + j = i + k; + if( j >= ie ) + j = ie; + else if( k==7 ) // || k==0 ) + while( (flg[j]>>5)==0 && j= CE ) + { + dfa.transit( (k>>2)-3 ); + commentbit = dfa.state&1; + if( k != CE ) + for( ; i np( new Parser( + tags[0], taglen[0], tags[1], taglen[1], tags[2], taglen[2], + flags[1], flags[2], flags[3], flags[0] ) ); + parser_ = np; + + // 5行目以降:キーワードリスト + while( !r.isEmpty() ) + { + r.getLine(); + if( len != 0 ) + parser_->AddKeyword( str, len ); + } + + // 全行解析し直し + ReParse( 0, tln()-1 ); + + // 変更通知 + Fire_KEYWORDCHANGE(); +} + +bool DocImpl::ReParse( ulong s, ulong e ) +{ + ulong i; + uchar cmt = text_[s].isLineHeadCmt(); + + // まずは変更範囲を再解析 + for( i=s; i<=e; ++i ) + cmt = parser_->Parse( text_[i], cmt ); + + // コメントアウト状態に変化がなかったらここでお終い。 + if( i==tln() || text_[i].isLineHeadCmt()==cmt ) + return false; + + // 例えば、/* が入力された場合などは、下の方の行まで + // コメントアウト状態の変化を伝達する必要がある。 + do + cmt = text_[i++].TransitCmt( cmt ); + while( iSetCommentBit( const_cast(x) ); +} ADDED editwing/ip_scroll.cpp Index: editwing/ip_scroll.cpp ================================================================== --- editwing/ip_scroll.cpp +++ editwing/ip_scroll.cpp @@ -0,0 +1,553 @@ +#include "stdafx.h" +#include "ip_view.h" +using namespace editwing; +using namespace editwing::view; + + + +//========================================================================= +//---- ip_scroll.cpp スクロール +// +// ウインドウサイズはスクロールバーの位置によって +// 描画位置を適当に更新していく処理がここ。 +// +//---- ip_text.cpp 文字列操作・他 +//---- ip_parse.cpp キーワード解析 +//---- ip_wrap.cpp 折り返し +//---- ip_draw.cpp 描画・他 +//---- ip_cursor.cpp カーソルコントロール +//========================================================================= + + + +//------------------------------------------------------------------------- +// 描画領域サイズ管理 +//------------------------------------------------------------------------- + +namespace +{ + static int Log10( ulong n ) + { + const static ulong power_of_ten[] = + { 1, 10, 100, 1000, 10000, 100000, 1000000, + 10000000, 100000000, 1000000000 }; // 10^0 〜 10^9 + int c=3; + if( power_of_ten[9] <= n ) + c=10; + else + while( power_of_ten[c] <= n ) + c++; + return c; // 3<=c<=10 s.t. 10^(c-1) <= n < 10^c + } +} + +bool Canvas::CalcLNAreaWidth() +{ + const int prev = txtZone_.left; + if( showLN_ ) + { + txtZone_.left = (1 + figNum_) * font_->F(); + if( txtZone_.left+font_->W() >= txtZone_.right ) + txtZone_.left = 0; // 行番号ゾーンがデカすぎるときは表示しない + } + else + { + txtZone_.left = 0; + } + + return (prev != txtZone_.left); +} + +void Canvas::CalcWrapWidth() +{ + switch( wrapType_ ) + { + case NOWRAP: + wrapWidth_ = 0xffffffff; + break; + case RIGHTEDGE: + wrapWidth_ = txtZone_.right - txtZone_.left - 3; + break; //Caretの分-3補正 + default: + wrapWidth_ = wrapType_ * font_->W(); + break; + } +} + +Canvas::Canvas( const View& vw ) + : wrapType_ ( -1 ) + , showLN_ ( false ) + , wrapWidth_( 0xffffffff ) + , figNum_ ( 3 ) + , font_ ( new Painter( ::GetDC(vw.hwnd()), + VConfig(TEXT("FixedSys"),14) ) ) +{ + vw.getClientRect( &txtZone_ ); +} + +bool Canvas::on_view_resize( int cx, int cy ) +{ + txtZone_.right = cx; + txtZone_.bottom = cy; + + CalcLNAreaWidth(); + if( wrapType_ == RIGHTEDGE ) + { + CalcWrapWidth(); + return true; + } + return false; +} + +void Canvas::on_font_change( const VConfig& vc ) +{ + HDC dc = font_->getDC(); + font_ = NULL; // 先にデストラクタを呼ばねばならない… + // ってうわー格好悪ぃーーー(T_T) + font_ = new Painter( dc, vc ); + + CalcLNAreaWidth(); + CalcWrapWidth(); +} + +void Canvas::on_config_change( int wrap, bool showln ) +{ + showLN_ = showln; + wrapType_ = wrap; + + CalcLNAreaWidth(); + CalcWrapWidth(); +} + +bool Canvas::on_tln_change( ulong tln ) +{ + figNum_ = Log10( tln ); // 桁数計算 + + if( CalcLNAreaWidth() ) + { + if( wrapType_ == RIGHTEDGE ) + CalcWrapWidth(); + return true; + } + return false; +} + + + +//------------------------------------------------------------------------- +// スクロールバー計算ルーチン +//------------------------------------------------------------------------- +// rl (横スクロール情報) +// max: view.txt.txtwidth() +// page: view.cx() +// pos: 0〜max-page + +// ud (縦スクロール情報) +// max: view.txt.vln() + page - 1 +// page: view.cy() / view.fnt.H() +// delta: 0〜view.fnt.H() +// pos: 0〜max-page (topの行番号) + +bool ViewImpl::ReSetScrollInfo() +{ + const int prevRlPos = rlScr_.nPos; + const ulong cx = cvs_.zone().right - cvs_.zone().left; + const ulong cy = cvs_.zone().bottom; + + // 横は変な値にならないよう補正するだけでよい +// rlScr_.nPage = cx + 1; +// rlScr_.nMax = Max( textCx_, cx ); +// rlScr_.nPos = Min( rlScr_.nPos, rlScr_.nMax-rlScr_.nPage+1 ); + rlScr_.nPage = cx + 1; + rlScr_.nMax = Max( textCx_+3, cx ); + rlScr_.nPos = Min( rlScr_.nPos, rlScr_.nMax-rlScr_.nPage+1 ); + + // 縦はnPageとnMaxはとりあえず補正 + // nPosは場合によって直し方が異なるので別ルーチンにて + udScr_.nPage = cy / cvs_.getPainter().H() + 1; + udScr_.nMax = vln() + udScr_.nPage - 2; + + // 横スクロールが起きたらtrue + return (prevRlPos != rlScr_.nPos); +} + +void ViewImpl::ForceScrollTo( ulong tl ) +{ + udScr_.nPos = tl2vl(tl); + udScr_tl_ = tl; + udScr_vrl_ = 0; +} + +ulong ViewImpl::tl2vl( ulong tl ) const +{ + if( vln() == doc_.tln() ) + return tl; + + ulong vl=0; + for( ulong i=0; i(udScr_.nPos) >= vln() ) + { + // パターン2-1:しかしそこはすでにEOFよりも下だ! + // しゃーないので一番下の行を表示 + udScr_.nPos = vln()-1; + udScr_tl_ = doc_.tln()-1; + udScr_vrl_ = rln(udScr_tl_)-1; + ans = ALL; + } + else + { + // パターン2-2: + // スクロール無し + while( udScr_vrl_ >= rln(udScr_tl_) ) + { + udScr_vrl_ -= rln(udScr_tl_); + udScr_tl_++; + } + } + } + else + { + // パターン3:現在の画面上端より上で更新された場合 + // 表示内容を変えないように頑張る + + if( e.tl < udScr_tl_ ) + { + // パターン3-1:変更範囲の終端も、現在行より上の場合 + // 行番号は変わるが表示内容は変わらないで済む + udScr_.nPos += vl_dif; + udScr_tl_ += (e2.tl - e.tl); + ans = LNAREA; + } + else + { + // パターン3-2: + // どうしよーもないので適当な位置にスクロール + ForceScrollTo( e2.tl ); + ans = ALL; + } + } + + // どんな再描画をすればよいか返す + return (rlScrolled ? ALL : ans); +} + +void ViewImpl::ScrollTo( const VPos& vp ) +{ + // 横フォーカス + int dx=0; + if( vp.vx < (signed)rlScr_.nPos ) + { + dx = vp.vx - rlScr_.nPos; + } + else + { + const int W = cvs_.getPainter().W(); + if( rlScr_.nPos + (signed)(rlScr_.nPage-W) <= vp.vx ) + dx = vp.vx - (rlScr_.nPos + rlScr_.nPage) + W; + } + + // 縦フォーカス + int dy=0; + if( vp.vl < (unsigned)udScr_.nPos ) + dy = vp.vl - udScr_.nPos; + else if( udScr_.nPos + (udScr_.nPage-1) <= vp.vl ) + dy = vp.vl - (udScr_.nPos + udScr_.nPage) + 2; + + // スクロール + if( dy!=0 ) UpDown( dy, dx==0 ); + if( dx!=0 ) ScrollView( dx, 0, true ); +} + +void ViewImpl::GetDrawPosInfo( VDrawInfo& v ) const +{ + const int H = cvs_.getPainter().H(); + + long most_under = (vln()-udScr_.nPos)*H; + if( most_under <= v.rc.top ) + { + v.YMIN = v.rc.top; + v.YMAX = most_under; + } + else + { + int y = -(signed)udScr_vrl_; + ulong tl = udScr_tl_; + int top = v.rc.top / H; + while( y + (signed)rln(tl) <= top ) + y += rln( tl++ ); + + // 縦座標 + v.YMIN = y * H; + v.YMAX = Min( v.rc.bottom, most_under ); + v.TLMIN = tl; + + // 横座標 + v.XBASE = left() - rlScr_.nPos; + v.XMIN = v.rc.left - v.XBASE; + v.XMAX = v.rc.right - v.XBASE; + + // 選択範囲 + v.SXB = v.SXE = v.SYB = v.SYE = 0x7fffffff; + + const VPos *bg, *ed; + if( cur_.getCurPos( &bg, &ed ) ) + { + v.SXB = bg->vx - rlScr_.nPos + left(); + v.SXE = ed->vx - rlScr_.nPos + left(); + v.SYB = (bg->vl - udScr_.nPos) * H; + v.SYE = (ed->vl - udScr_.nPos) * H; + } + } +} + +void ViewImpl::ScrollView( int dx, int dy, bool update ) +{ + // スクロール開始通知 + cur_.on_scroll_begin(); + + const RECT* clip = (dy==0 ? &cvs_.zone() : NULL); + const int H = cvs_.getPainter().H(); + + // スクロールバー更新 + if( dx != 0 ) + { + // 範囲チェック + if( rlScr_.nPos+dx < 0 ) + dx = -rlScr_.nPos; + else if( rlScr_.nMax-(signed)rlScr_.nPage < rlScr_.nPos+dx ) + dx = rlScr_.nMax-rlScr_.nPage-rlScr_.nPos+1; + + rlScr_.nPos += dx; + ::SetScrollInfo( hwnd_, SB_HORZ, &rlScr_, TRUE ); + dx = -dx; + } + if( dy != 0 ) + { + // 範囲チェック…は前処理で終わってる。 + + udScr_.nPos += dy; + ::SetScrollInfo( hwnd_, SB_VERT, &udScr_, TRUE ); + dy *= -H; + } + if( dx!=0 || dy!=0 ) + { + if( -dx>=right() || dx>=right() + || -dy>=bottom() || dy>=bottom() ) + { + // 全画面再描画 + // ちょうど65536の倍数くらいスクロールしたときに、 + // ScrollWindowEx on Win9x だと再描画が変なのを回避。 + ::InvalidateRect( hwnd_, NULL, FALSE ); + } + else + { + // 再描画の不要な領域をスクロール + ::ScrollWindowEx( hwnd_, dx, dy, NULL, + clip, NULL, NULL, SW_INVALIDATE ); + + // 即時再描画? + if( update ) + { + // 縦スクロールは高速化したいので一工夫 + if( dy != 0 ) + { + // 再描画の必要な領域を自分で計算 + RECT rc = {0,0,right(),bottom()}; + if( dy < 0 ) rc.top = rc.bottom + dy; + else rc.bottom = dy; + + // インテリマウスの中ボタンクリックによる + // オートスクロール用カーソルの下の部分を先に描く + // 2回に分けることで、小さな矩形部分二つで済むので高速 + ::ValidateRect( hwnd_, &rc ); + ::UpdateWindow( hwnd_ ); + ::InvalidateRect( hwnd_, &rc, FALSE ); + } + ::UpdateWindow( hwnd_ ); + } + } + } + + // スクロール終了通知 + cur_.on_scroll_end(); +} + +void ViewImpl::on_hscroll( int code ) +{ + // 変化量を計算 + int dx; + switch( code ) + { + default: return; + case SB_LINELEFT: dx= -cvs_.getPainter().W(); break; + case SB_LINERIGHT: dx= +cvs_.getPainter().W(); break; + case SB_PAGELEFT: dx= -(cx()>>1); break; + case SB_PAGERIGHT: dx= +(cx()>>1); break; + case SB_THUMBTRACK: + { + SCROLLINFO si = { sizeof(SCROLLINFO), SIF_TRACKPOS }; + ::GetScrollInfo( hwnd_, SB_HORZ, &si ); + dx = si.nTrackPos - rlScr_.nPos; + break; + } + case SB_LEFT: dx = -rlScr_.nPos; break; + case SB_RIGHT: dx = rlScr_.nMax+1-(signed)rlScr_.nPage-rlScr_.nPos; break; + } + + // スクロール + ScrollView( dx, 0, code!=SB_THUMBTRACK ); +} + +void ViewImpl::on_vscroll( int code ) +{ + // 変化量を計算 + int dy; + switch( code ) + { + default: return; + case SB_LINEUP: dy= -1; break; + case SB_LINEDOWN: dy= +1; break; + case SB_PAGEUP: dy= -(cy() / cvs_.getPainter().H()); break; + case SB_PAGEDOWN: dy= +(cy() / cvs_.getPainter().H()); break; + case SB_THUMBTRACK: + { + SCROLLINFO si = { sizeof(SCROLLINFO), SIF_TRACKPOS }; + ::GetScrollInfo( hwnd_, SB_VERT, &si ); + dy = si.nTrackPos - udScr_.nPos; + break; + } + case SB_TOP: dy = -udScr_.nPos; break; + case SB_BOTTOM: dy = udScr_.nMax+1-(signed)udScr_.nPage-udScr_.nPos; break; + } + + // スクロール + UpDown( dy, code==SB_THUMBTRACK ); +} + +void ViewImpl::on_wheel( short delta ) +{ + // スクロール + UpDown( -delta / WHEEL_DELTA * 3, false ); +} + +void ViewImpl::UpDown( int dy, bool thumb ) +{ + // 1.udScr_.nPos + dy が正常範囲に収まるように補正 + if( udScr_.nPos+dy < 0 ) + dy = -udScr_.nPos; + else if( udScr_.nMax+1-(signed)udScr_.nPage < udScr_.nPos+dy ) + dy = udScr_.nMax+1-udScr_.nPage-udScr_.nPos; + if( dy==0 ) + return; + + // 2−1.折り返し無しの場合は一気にジャンプ出来る + if( !wrapexists() ) + { + udScr_tl_ = udScr_.nPos + dy; + } + + // 2−2.でなけりゃ、現在位置からの相対サーチ + // ScrollBarを連続的にドラッグせず一度に一気に飛んだ場合は + // 1行目や最終行からの相対サーチの方が有効な可能性があるが、 + // その場合は多少速度が遅くなっても描画が引っかかることはないのでOK + else + { + int rl = dy + udScr_vrl_; + ulong tl = udScr_tl_; + + if( dy<0 ) // 上へ戻る場合 + { + // ジャンプ先論理行の行頭へDash! + while( rl < 0 ) + rl += rln(--tl); + } + else if( dy>0 ) // 下へ進む場合 + { + // ジャンプ先論理行の行頭へDash! + while( rl > 0 ) + rl -= rln(tl++); + if( rl < 0 ) + rl += rln(--tl); //行き過ぎ修正 + } + udScr_tl_ = tl; + udScr_vrl_= static_cast(rl); + } + + // 4.画面をスクロール + ScrollView( 0, dy, !thumb ); +} + +void ViewImpl::InvalidateView( const DPos& dp, bool afterall ) const +{ + const int H = cvs_.getPainter().H(); + + // 表示域より上での更新 + if( dp.tl < udScr_tl_ ) + { + if( afterall ) + ::InvalidateRect( hwnd_, NULL, FALSE ); + return; + } + + // 開始y座標計算 + int r=0, yb=-(signed)udScr_vrl_; + for( int t=udScr_tl_, ybe=cy()/H; (unsigned)t= ybe ) + return; + for( ; dp.ad>rlend(dp.tl,r); ++r,++yb ); + yb = H * Max( yb, -100 ); // 上にはみ出し過ぎないよう調整 + if( yb >= cy() ) + return; + + // 1行目を再描画 + int rb = (r==0 ? 0 : rlend(dp.tl,r-1)); + int xb = left() + Max( 0UL, + CalcLineWidth(doc_.tl(dp.tl)+rb,dp.ad-rb) -rlScr_.nPos ); + if( xb < right() ) + { + RECT rc={xb,yb,right(),yb+H}; + ::InvalidateRect( hwnd_, &rc, FALSE ); + } + + // 残り + int ye; + yb += H; + if( afterall ) + xb=0, ye=cy(); + else + xb=left(), ye=Min(cy(),yb+(int)(H*(rln(dp.tl)-r-1))); + if( yb < ye ) + { + RECT rc={xb,yb,right(),ye}; + ::InvalidateRect( hwnd_, &rc, FALSE ); + } +} ADDED editwing/ip_text.cpp Index: editwing/ip_text.cpp ================================================================== --- editwing/ip_text.cpp +++ editwing/ip_text.cpp @@ -0,0 +1,755 @@ +#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 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(); ion_text_update( s, e, e2, reparsed, nmlcmd ); +} + +void DocImpl::Fire_KEYWORDCHANGE() +{ + AutoLock lk(this); + + // 全部にイベント通知 + for( ulong i=0, ie=pEvHan_.size(); ion_keyword_change(); +} + +void DocImpl::Fire_MODIFYFLAGCHANGE() +{ + AutoLock lk(this); + + // 全部にイベント通知 + bool b = urdo_.isModified(); + for( ulong i=0, ie=pEvHan_.size(); ion_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(-1) ) +{ +} + +UnReDoChain::~UnReDoChain() +{ + Clear(); +} + +ulong UnReDoChain::Node::ChainDelete(Node*& savedPos_ref) +{ + if( cmd_ == NULL ) + return 0; + if( savedPos_ref == this ) + savedPos_ref = NULL; + dptr 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>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 ) + { + 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(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(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(); iarr_[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 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 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 ); + } +*/ +} + ADDED editwing/ip_view.h Index: editwing/ip_view.h ================================================================== --- editwing/ip_view.h +++ editwing/ip_view.h @@ -0,0 +1,422 @@ +#ifndef _EDITWING_IP_VIEW_H_ +#define _EDITWING_IP_VIEW_H_ +#include "ewView.h" +#include "ip_doc.h" +using namespace ki; +#ifndef __ccdoc__ +namespace editwing { +namespace view { +#endif + + + +using doc::DocImpl; + + + +//========================================================================= +//@{ @pkg editwing.View.Impl //@} +//@{ +// 描画基本ルーチン +// +// 利用するには、Canvasオブジェクトから getPainter して使います。 +// 画面用デバイスコンテキストのレイヤです。きちんと書いておけば印刷機能を +// 追加するときに楽なのかもしれませんが、そんなことを考える計画性が +// あるはずもなく極めて適当に…。 +//@} +//========================================================================= + +class Painter : public Object +{ +public: + + ~Painter(); + + //@{ 指定位置に一文字出力 //@} + void CharOut( unicode ch, int x, int y ); + + //@{ 指定位置に文字列を出力 //@} + void StringOut( const unicode* str, int len, int x, int y ); + + //@{ 文字色切り替え //@} + void SetColor( int i ); + + //@{ 背景色で塗りつぶし //@} + void Fill( const RECT& rc ); + + //@{ 反転 //@} + void Invert( const RECT& rc ); + + //@{ 線を引く //@} + void DrawLine( int x1, int y1, int x2, int y2 ); + + //@{ クリップ領域設定 //@} + void SetClip( const RECT& rc ); + + //@{ クリップ領域解除 //@} + void ClearClip(); + + //@{ 半角スペース用記号描画 //@} + void DrawHSP( int x, int y, int times ); + + //@{ 全角スペース用記号描画 //@} + void DrawZSP( int x, int y, int times ); + +public: + + //@{ 高さ(pixel) //@} + int H() const { return height_; } + + //@{ 数字幅(pixel) //@} + int F() const { return figWidth_; } + + //@{ 文字幅(pixel) //@} + int Wc( unicode ch ) const + { + if( widthTable_[ ch ] == -1 ) + ::GetCharWidthW( dc_, ch, ch, widthTable_+ch ); + return widthTable_[ ch ]; + } + int W( const unicode* pch ) const // 1.08 サロゲートペア回避 + { + unicode ch = *pch; + if( widthTable_[ ch ] == -1 ) + { + if( isHighSurrogate(ch) ) + { + SIZE sz; + if( ::GetTextExtentPoint32W( dc_, pch, 2, &sz ) ) + return sz.cx; + int w = 0; + ::GetCharWidthW( dc_, ch, ch, &w ); + return w; + } + ::GetCharWidthW( dc_, ch, ch, widthTable_+ch ); + } + return widthTable_[ ch ]; + } + + //@{ 標準文字幅(pixel) //@} + int W() const { return widthTable_[ L'x' ]; } + + //@{ 次のタブ揃え位置を計算 //@} + //int nextTab(int x) const { int t=T(); return (x/t+1)*t; } + int nextTab(int x) const { int t=T(); return ((x+4)/t+1)*t; } + private: int T() const { return widthTable_[ L'\t' ]; } public: + + //@{ 現在のフォント情報 //@} + const LOGFONT& LogFont() const { return logfont_; } + + //@{ 特別文字を描画するか否か //@} + bool sc( int i ) const { return scDraw_[i]; } + +private: + + const HDC dc_; + const HFONT font_; + const HPEN pen_; + const HBRUSH brush_; + int height_; + int* widthTable_; + int figWidth_; + LOGFONT logfont_; + COLORREF colorTable_[7]; + bool scDraw_[5]; + +private: + + Painter( HDC hdc, const VConfig& vc ); + HDC getDC() { return dc_; } + friend class Canvas; + NOCOPY(Painter); +}; + + + +//========================================================================= +//@{ +// 描画可能領域 +// +// ウインドウサイズの変更や折り返しの有無やフォントの設定などに +// 対応して、描画領域のサイズを適当に管理します。やることは +// とりあえずそれだけ。 +//@} +//========================================================================= + +class Canvas : public Object +{ +public: + + Canvas( const View& vw ); + + //@{ Viewの大きさ変更イベント処理 + // @return 折り返し幅が変わったらtrue //@} + bool on_view_resize( int cx, int cy ); + + //@{ 行数変更イベント処理 + // @return テキスト領域の幅が変わったらtrue //@} + bool on_tln_change( ulong tln ); + + //@{ フォント変更イベント処理 //@} + void on_font_change( const VConfig& vc ); + + //@{ 設定変更イベント処理 //@} + void on_config_change( int wrap, bool showln ); + +public: + + //@{ [行番号を表示するか否か] //@} + bool showLN() const { return showLN_; } + + //@{ [-1:折り返し無し 0:窓右端 else:指定文字数] //@} + int wrapType() const { return wrapType_; } + + //@{ 折り返し幅(pixel) //@} + ulong wrapWidth() const { return wrapWidth_; } + + //@{ 表示領域の位置(pixel) //@} + const RECT& zone() const { return txtZone_; } + + //@{ 描画用オブジェクト //@} + Painter& getPainter() const { return *font_; } + +private: + + int wrapType_; // [ -1:折り返し無し 0:窓右端 else:指定文字数 ] + bool showLN_; // [ 行番号を表示するか否か ] + + dptr font_; // 描画用オブジェクト + ulong wrapWidth_; // 折り返し幅(pixel) + RECT txtZone_; // テキスト表示域の位置(pixel) + int figNum_; // 行番号の桁数 + +private: + + bool CalcLNAreaWidth(); + void CalcWrapWidth(); + +private: + + NOCOPY(Canvas); +}; + + + +//========================================================================= +//@{ +// 行毎の折り返し情報 +//@} +//========================================================================= + +struct WLine : public storage +{ + // [0] : その行の折り返し無しでの横幅を格納 + // [1-n] : n行目の終端のindexを格納。 + // + // 例えば "aaabbb" という論理行を "aaab" "bb" と折るなら + // {48, 4, 6} などという長さ3の配列となる。 + + WLine() : storage(2) {} + ulong& width() { return (*this)[0]; } + ulong width() const { return (*this)[0]; } + ulong rln() const { return size()-1; } +}; + + + +//========================================================================= +//@{ +// 再描画範囲を指定するためのフラグ +//@} +//========================================================================= + +enum ReDrawType +{ + LNAREA, // 行番号ゾーンのみ + LINE, // 変更のあった一行のみ + AFTER, // 変更のあった行以下全部 + ALL // 全画面 +}; + + + +//========================================================================= +//@{ +// 描画処理を細かく指定する構造体 +//@} +//========================================================================= + +struct VDrawInfo +{ + const RECT rc; // 再描画範囲 + int XBASE; // 一番左の文字のx座標 + int XMIN; // テキスト再描画範囲左端 + int XMAX; // テキスト再描画範囲右端 + int YMIN; // テキスト再描画範囲上端 + int YMAX; // テキスト再描画範囲下端 + ulong TLMIN; // テキスト再描画範囲上端論理行番号 + int SXB, SXE; // 選択範囲のx座標 + int SYB, SYE; // 選択範囲のy座標 + + explicit VDrawInfo( const RECT& r ) : rc(r) {} +}; + + + +//========================================================================= +//@{ +// 折り返しedテキストの管理・表示等 +// +// Canvasクラスによって計算された領域サイズを参考に、テキストの +// 折り返し処理を実行する。ここで、スクロール制御、描画処理など +// 主要な処理は全て実行することになる。 +//@} +//========================================================================= + +class ViewImpl : public Object +{ +public: + + ViewImpl( View& vw, DocImpl& dc ); + + //@{ 折り返し方式切替 //@} + void SetWrapType( int wt ); + + //@{ 行番号表示/非表示切替 //@} + void ShowLineNo( bool show ); + + //@{ 表示色・フォント切替 //@} + void SetFont( const VConfig& vc ); + + //@{ テキスト領域のサイズ変更イベント //@} + void on_view_resize( int cx, int cy ); + + void DoResize( bool wrapWidthChanged ); + void DoConfigChange(); + + //@{ テキストデータの更新イベント //@} + void on_text_update( const DPos& s, + const DPos& e, const DPos& e2, bool bAft, bool mCur ); + + //@{ 描画処理 //@} + void on_paint( const PAINTSTRUCT& ps ); + +public: + + //@{ 全表示行数 //@} + ulong vln() const { return vlNum_; } + + //@{ 一行の表示行数 //@} + ulong rln( ulong tl ) const { return wrap_[tl].rln(); } + + //@{ 折り返し位置 //@} + ulong rlend( ulong tl, ulong rl ) const { return wrap_[tl][rl+1]; } + + //@{ 一個でも折り返しが存在するか否か //@} + bool wrapexists() const { return doc_.tln() != vln(); } + + //@{ カーソル //@} + Cursor& cur() { return cur_; } + + //@{ フォント //@} + const Painter& fnt() const { return cvs_.getPainter(); } + + + void on_hscroll( int code ); + void on_vscroll( int code ); + void on_wheel( short delta ); + + void GetVPos( int x, int y, VPos* vp, bool linemode=false ) const; + void GetOrigin( int* x, int* y ) const; + void ConvDPosToVPos( DPos dp, VPos* vp, const VPos* base=NULL ) const; + void ScrollTo( const VPos& vp ); + int GetLastWidth( ulong tl ) const; + +public: + + const RECT& zone() const { return cvs_.zone(); } + int left() const { return cvs_.zone().left; } + int right() const { return cvs_.zone().right; } + int bottom()const { return cvs_.zone().bottom; } + int lna() const { return cvs_.zone().left; } + int cx() const { return cvs_.zone().right - cvs_.zone().left; } + int cxAll() const { return cvs_.zone().right; } + int cy() const { return cvs_.zone().bottom; } + +private: + + const DocImpl& doc_; + Canvas cvs_; + Cursor cur_; + gapbufobj wrap_; + ulong vlNum_; + ulong textCx_; + +private: + + void DrawLNA( const VDrawInfo& v, Painter& p ); + void DrawTXT( const VDrawInfo v, Painter& p ); + void Inv( int y, int xb, int xe, Painter& p ); + + void CalcEveryLineWidth(); + ulong CalcLineWidth( const unicode* txt, ulong len ) const; + void ModifyWrapInfo( const unicode* txt, ulong len, WLine& wl, ulong stt ); + void ReWrapAll(); + int ReWrapSingle( const DPos& s ); + int InsertMulti( ulong ti_s, ulong ti_e ); + int DeleteMulti( ulong ti_s, ulong ti_e ); + void UpdateTextCx(); + void ReDraw( ReDrawType r, const DPos* s=NULL ); + +private: + + HWND hwnd_; + SCROLLINFO rlScr_; // 横スクロール情報(pixel単位) + SCROLLINFO udScr_; // 縦スクロール情報(行単位) + ulong udScr_tl_; // 一番上に表示される論理行のTLine_Index + ulong udScr_vrl_; // 一番上に表示される表示行のVRLine_Index + +private: + + bool ReSetScrollInfo(); + void ForceScrollTo( ulong tl ); + void UpdateScrollBar(); + ReDrawType TextUpdate_ScrollBar( const DPos& s, const DPos& e, const DPos& e2 ); + + ulong tl2vl( ulong tl ) const; + void GetDrawPosInfo( VDrawInfo& v ) const; + void InvalidateView( const DPos& dp, bool afterall ) const; + void ScrollView( int dx, int dy, bool update ); + void UpDown( int dy, bool thumb ); +}; + + + +//------------------------------------------------------------------------- + +inline void ViewImpl::on_view_resize( int cx, int cy ) + { DoResize( cvs_.on_view_resize( cx, cy ) ); } + +inline void ViewImpl::SetWrapType( int wt ) + { cvs_.on_config_change( wt, cvs_.showLN() ); + DoConfigChange(); } + +inline void ViewImpl::ShowLineNo( bool show ) + { cvs_.on_config_change( cvs_.wrapType(), show ); + DoConfigChange(); } + +inline void ViewImpl::SetFont( const VConfig& vc ) + { cvs_.on_font_change( vc ); + cur_.on_setfocus(); + CalcEveryLineWidth(); // 行幅再計算 + DoConfigChange(); } + +inline void ViewImpl::GetOrigin( int* x, int* y ) const + { *x = left()-rlScr_.nPos, *y = -udScr_.nPos*cvs_.getPainter().H(); } + + + +//========================================================================= + +}} // namespace editwing::view +#endif // _EDITWING_IP_VIEW_H_ ADDED editwing/ip_wrap.cpp Index: editwing/ip_wrap.cpp ================================================================== --- editwing/ip_wrap.cpp +++ editwing/ip_wrap.cpp @@ -0,0 +1,536 @@ +#include "stdafx.h" +#include "ip_view.h" +using namespace editwing; +using namespace editwing::view; + + + +//========================================================================= +//---- ip_wrap.cpp 折り返し +// +// Documentで文字列データが更新されるのを受けて +// Viewでは折り返し位置情報を更新する。その処理がココ。 +// +//---- ip_text.cpp 文字列操作・他 +//---- ip_parse.cpp キーワード解析 +//---- ip_scroll.cpp スクロール +//---- ip_draw.cpp 描画・他 +//---- ip_cursor.cpp カーソルコントロール +//========================================================================= + + + +//------------------------------------------------------------------------- +// 初期化 +//------------------------------------------------------------------------- + +ViewImpl::ViewImpl( View& vw, DocImpl& dc ) + : doc_ ( dc ) + , cvs_ ( vw ) + , cur_ ( vw.hwnd(), *this, dc ) + , hwnd_ ( vw.hwnd() ) + , vlNum_ ( 0 ) + , textCx_( 0 ) +{ + // 適当に折り返し情報初期化 + InsertMulti( 0, doc_.tln()-1 ); + + // 適当にスクロール情報初期化 + udScr_.cbSize = rlScr_.cbSize = sizeof(udScr_); + udScr_.fMask = rlScr_.fMask = SIF_PAGE | SIF_POS | SIF_RANGE; + udScr_.nMin = rlScr_.nMin = 0; + udScr_.nPos = rlScr_.nPos = 0; + udScr_.fMask |= SIF_DISABLENOSCROLL; + udScr_tl_ = udScr_vrl_ = 0; + ReSetScrollInfo(); +} + + + +//------------------------------------------------------------------------- +// 状態変更への対応 +//------------------------------------------------------------------------- + +void ViewImpl::DoResize( bool wrapWidthChanged ) +{ + // 折り返し位置再計算 + if( wrapWidthChanged ) + { + ReWrapAll(); + UpdateTextCx(); + } + + // スクロール情報変更 + ReSetScrollInfo(); + if( wrapWidthChanged ) + ForceScrollTo( udScr_tl_ ); + + // 再描画 + ReDraw( ALL ); + cur_.ResetPos(); +} + +void ViewImpl::DoConfigChange() +{ + // 折り返し位置再計算 + ReWrapAll(); + UpdateTextCx(); + + // スクロール情報変更 + ReSetScrollInfo(); + ForceScrollTo( udScr_tl_ ); + + // 再描画 + ReDraw( ALL ); + cur_.ResetPos(); +} + +void ViewImpl::on_text_update + ( const DPos& s, const DPos& e, const DPos& e2, bool bAft, bool mCur ) +{ + // まず、折り返し位置再計算 + + // 置換範囲の先頭行を調整 + int r3 = 0, r2 = 1, r1 = ReWrapSingle( s ); + + // 残りを調整 + if( s.tl != e.tl ) + r2 = DeleteMulti( s.tl+1, e.tl ); + if( s.tl != e2.tl ) + r3 = InsertMulti( s.tl+1, e2.tl ); + + // この変更で横幅が… + // if( "長くなったなてはいない" AND "短くなっちゃった可能性あり" ) + // 横幅再計算(); + if( !(r1==2 || r3==1) && (r1==0 || r2==0) ) + UpdateTextCx(); + + // スクロールバー修正 + ReDrawType t = TextUpdate_ScrollBar( s, e, e2 ); + bool doResize = false; + + // 行数に変化があって、行番号表示域の幅を変えなきゃならん時 + if( e.tl!=e2.tl && cvs_.on_tln_change( doc_.tln() ) ) + { + doResize = true; + } + else if( bAft && t!=ALL ) + { + t = AFTER; + } + + // カーソル移動 + cur_.on_text_update( s, e, e2, mCur ); + + // 再描画 + if( doResize ) + DoResize( true ); + else + { + if( e.tl != e2.tl ) // 行番号領域再描画の必要があるとき + ReDraw( LNAREA, 0 ); + ReDraw( t, &s ); + } +} + + + +//------------------------------------------------------------------------- +// 折り返し位置計算補助ルーチン +//------------------------------------------------------------------------- + +void ViewImpl::UpdateTextCx() +{ + if( cvs_.wrapType() == NOWRAP ) + { + // 折り返しなしなら、数えてみないと横幅はわからない + ulong cx=0; + for( ulong i=0, ie=doc_.tln(); iww ) + break; // 幅が設定値を超えた所でおしまい + } + wl.Add( stt = (i==stt?i+1:i) ); + } +} + +int ViewImpl::GetLastWidth( ulong tl ) const +{ + if( rln(tl)==1 ) + return wrap_[tl][0]; + + ulong beg = rlend(tl,rln(tl)-2); + return CalcLineWidth( doc_.tl(tl)+beg, doc_.len(tl)-beg ); +} + +void ViewImpl::ReWrapAll() +{ + // 折り返し幅に変更があった場合に、全ての行の + // 折り返し位置情報を変更する。 + const ulong ww = cvs_.wrapWidth(); + + ulong vln=0; + for( ulong i=0, ie=doc_.tln(); iAdd( CalcLineWidth( doc_.tl(i), doc_.len(i) ) ); + + if( pwl->width() < cvs_.wrapWidth() ) + { + // 設定した折り返し幅より短い場合は一行で済む。 + pwl->Add( doc_.len(i) ); + dy++; + if( cx < pwl->width() ) + cx = pwl->width(); + } + else + { + // 複数行になる場合 + ModifyWrapInfo( doc_.tl(i), doc_.len(i), *pwl, 0 ); + dy += pwl->rln(); + } + + wrap_.InsertAt( i, pwl ); + } + + // 表示行の総数を修正 + vlNum_ += dy; + + // 折り返しなしだと総横幅の更新が必要 + if( cvs_.wrapType() == NOWRAP ) + { + if( textCx_ <= cx ) + { + textCx_ = cx; + return 1; + } + return 0; + } + return 1; +} + +int ViewImpl::DeleteMulti( ulong ti_s, ulong ti_e ) +{ + // 指定した範囲の行情報を削除 + // + // 返値は + // 1: "折り返しあり" or "この行以外のどこかが最長" + // 0: "さっきまでこの行は最長だったが短くなっちゃった" + // 詳しくは ReWrapSingle() を見よ。 + + bool widthChanged = false; + ulong dy = 0; + + // 情報収集しながら削除 + for( ulong cx=textCx_, i=ti_s; i<=ti_e; ++i ) + { + WLine& wl = wrap_[i]; + dy += wl.rln(); + if( cx == wl.width() ) + widthChanged = true; + } + wrap_.RemoveAt( ti_s, (ti_e-ti_s+1) ); + + // 表示行の総数を修正 + vlNum_ -= dy; + + // 折り返しなしだと総横幅の更新が必要 + return ( cvs_.wrapType()==NOWRAP && widthChanged ) ? 0 : 1; +} + + + +//------------------------------------------------------------------------- +// 座標値変換 +//------------------------------------------------------------------------- + +void ViewImpl::ConvDPosToVPos( DPos dp, VPos* vp, const VPos* base ) const +{ + // 補正 + dp.tl = Min( dp.tl, doc_.tln()-1 ); + dp.ad = Min( dp.ad, doc_.len(dp.tl) ); + + // 変換の基準点が指定されていなければ、原点を基準とする + VPos topPos(false); // 0クリア + if( base == NULL ) + base = &topPos; + + // とりあえずbase行頭の値を入れておく + ulong vl = base->vl - base->rl; + ulong rl = 0; + int vx; + + // 同じ行内だった場合 + //if( dp.tl == base->tl ) + //{ + // 例えば [→] を押したときなど、右隣の文字の横幅を + // 足すだけで次の位置は算出できる。これを使って普通の + // カーソル移動はずっと高速化できるはずであるが、 + // とりあえず面倒くさいので、今のところ略。 + //} + + // 違う行だった場合 + //else + { + // vlを合わせる + ulong tl = base->tl; + if( tl > dp.tl ) // 目的地が基準より上にある場合 + do + vl -= rln(--tl); + while( tl > dp.tl ); + else if( tl < dp.tl ) // 目的地が基準より下にある場合 + do + vl += rln(tl++); + while( tl < dp.tl ); + + // rlを合わせる + ulong stt=0; + while( wrap_[tl][rl+1] < dp.ad ) + stt = wrap_[tl][++rl]; + vl += rl; + + // x座標計算 + vx = CalcLineWidth( doc_.tl(tl)+stt, dp.ad-stt ); + } + + vp->tl = dp.tl; + vp->ad = dp.ad; + vp->vl = vl; + vp->rl = rl; + vp->rx = vp->vx = vx; +} + +void ViewImpl::GetVPos( int x, int y, VPos* vp, bool linemode ) const +{ +// x座標補正 + + x = x - lna() + rlScr_.nPos; + +// まず行番号計算 + + int tl = udScr_tl_; + int vl = udScr_.nPos - udScr_vrl_; + int rl = y / fnt().H() + udScr_vrl_; + if( rl >= 0 ) // View上端より下の場合、下方向を調べる + while( tl < (int)doc_.tln() && (int)rln(tl) <= rl ) + { + vl += rln(tl); + rl -= rln(tl); + ++tl; + } + else // View上端より上の場合、上方向を調べる + while( 0<=tl && rl<0 ) + { + vl -= rln(tl); + rl += rln(tl); + --tl; + } + + if( tl == (int)doc_.tln() ) // EOFより下に行ってしまう場合の補正 + { + --tl, vl-=rln(tl), rl=rln(tl)-1; + if( linemode ) + x = 0x4fffffff; + } + else if( tl == -1 ) // ファイル頭より上に行ってしまう場合の補正 + { + tl = vl = rl = 0; + if( linemode ) + x = 0; + } + else + { + if( linemode ) // 行選択モードの場合 + { + if( tl == (int)doc_.tln()-1 ) + rl=rln(tl)-1, x=0x4fffffff; + else + vl+=rln(tl), rl=0, ++tl, x=0; + } + } + + vp->tl = tl; + vp->vl = vl + rl; + vp->rl = rl; + +// 次に、横位置を計算 + + if( rl < static_cast(wrap_[tl].rln()) ) + { + const unicode* str = doc_.tl(tl); + const ulong adend = rlend(tl,rl); + ulong ad = (rl==0 ? 0 : rlend(tl,rl-1)); + int vx = (rl==0 ? 0 : fnt().W(&str[ad++])); + + while( adad = ad; + vp->rx = vp->vx = vx; + } + else + { + vp->ad = vp->rx = vp->vx = 0; + } +} ADDED kilib.dsp Index: kilib.dsp ================================================================== --- kilib.dsp +++ kilib.dsp @@ -0,0 +1,423 @@ +# Microsoft Developer Studio Project File - Name="kilib" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** 編集しないでください ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=kilib - Win32 Debug +!MESSAGE これは有効なメイクファイルではありません。 このプロジェクトをビルドするためには NMAKE を使用してください。 +!MESSAGE [メイクファイルのエクスポート] コマンドを使用して実行してください +!MESSAGE +!MESSAGE NMAKE /f "kilib.mak". +!MESSAGE +!MESSAGE NMAKE の実行時に構成を指定できます +!MESSAGE コマンド ライン上でマクロの設定を定義します。例: +!MESSAGE +!MESSAGE NMAKE /f "kilib.mak" CFG="kilib - Win32 Debug" +!MESSAGE +!MESSAGE 選択可能なビルド モード: +!MESSAGE +!MESSAGE "kilib - Win32 Release" ("Win32 (x86) Application" 用) +!MESSAGE "kilib - Win32 Debug" ("Win32 (x86) Application" 用) +!MESSAGE "kilib - Win32 Unicode Release" ("Win32 (x86) Application" 用) +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "kilib - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "release" +# PROP Intermediate_Dir "OBJ/vc/rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /Yu"stdafx.h" /FD /c +# ADD CPP /nologo /Gr /Zp4 /W3 /Gi /Og /Oi /Os /Oy /Ob1 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "SUPERTINY" /D "USEGLOBALIME" /Yu"stdafx.h" /FD /GF /c +# SUBTRACT CPP /Gf +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x411 /d "NDEBUG" +# ADD RSC /l 0x411 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib comctl32.lib imm32.lib /nologo /subsystem:windows /map /machine:I386 /nodefaultlib /out:"release/GreenPad.exe" +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "kilib - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "OBJ/" +# PROP Intermediate_Dir "OBJ/vc/debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /Yu"stdafx.h" /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "SUPERTINY" /D "USEGLOBALIME" /FR /Yu"stdafx.h" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x411 /d "_DEBUG" +# ADD RSC /l 0x411 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib comctl32.lib imm32.lib /nologo /subsystem:windows /debug /machine:I386 /out:"OBJ/GreenPad.exe" /pdbtype:sept + +!ELSEIF "$(CFG)" == "kilib - Win32 Unicode Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "kilib___Win32_Unicode_Release" +# PROP BASE Intermediate_Dir "kilib___Win32_Unicode_Release" +# PROP BASE Ignore_Export_Lib 0 +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "release" +# PROP Intermediate_Dir "OBJ/vc/reluni" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /Gr /Zp4 /W3 /Gi /Og /Oi /Os /Oy /Ob1 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "SUPERTINY" /D "USEGLOBALIME" /Yu"stdafx.h" /FD /GF /c +# SUBTRACT BASE CPP /Gf +# ADD CPP /nologo /Gr /Zp4 /W3 /Gi /Og /Oi /Os /Oy /Ob1 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "SUPERTINY" /D "USEGLOBALIME" /D "UNICODE" /D "_UNICODE" /Yu"stdafx.h" /FD /GF /c +# SUBTRACT CPP /Gf +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x411 /d "NDEBUG" +# ADD RSC /l 0x411 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib comctl32.lib imm32.lib /nologo /subsystem:windows /map /machine:I386 /nodefaultlib /out:"release/GreenPad.exe" +# SUBTRACT BASE LINK32 /pdb:none +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib comctl32.lib imm32.lib /nologo /subsystem:windows /map /machine:I386 /nodefaultlib /out:"release/GreenPad.exe" +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "kilib - Win32 Release" +# Name "kilib - Win32 Debug" +# Name "kilib - Win32 Unicode Release" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\ConfigManager.cpp +# End Source File +# Begin Source File + +SOURCE=.\GpMain.cpp +# End Source File +# Begin Source File + +SOURCE=.\OpenSaveDlg.cpp +# End Source File +# Begin Source File + +SOURCE=.\RSearch.cpp +# End Source File +# Begin Source File + +SOURCE=.\Search.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\ConfigManager.h +# End Source File +# Begin Source File + +SOURCE=.\GpMain.h +# End Source File +# Begin Source File + +SOURCE=.\NSearch.h +# End Source File +# Begin Source File + +SOURCE=.\OpenSaveDlg.h +# End Source File +# Begin Source File + +SOURCE=.\RSearch.h +# End Source File +# Begin Source File + +SOURCE=.\Search.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=.\rsrc\exefile.ico +# End Source File +# Begin Source File + +SOURCE=.\rsrc\gp_rsrc.rc +# End Source File +# Begin Source File + +SOURCE=.\rsrc\manifest.xml +# End Source File +# Begin Source File + +SOURCE=.\rsrc\resource.h +# End Source File +# End Group +# Begin Group "kilib" + +# PROP Default_Filter "" +# Begin Group "Source" + +# PROP Default_Filter "cpp" +# Begin Source File + +SOURCE=.\kilib\app.cpp +# End Source File +# Begin Source File + +SOURCE=.\kilib\cmdarg.cpp +# End Source File +# Begin Source File + +SOURCE=.\kilib\ctrl.cpp +# End Source File +# Begin Source File + +SOURCE=.\kilib\file.cpp +# End Source File +# Begin Source File + +SOURCE=.\kilib\find.cpp +# End Source File +# Begin Source File + +SOURCE=.\kilib\log.cpp +# End Source File +# Begin Source File + +SOURCE=.\kilib\memory.cpp +# End Source File +# Begin Source File + +SOURCE=.\kilib\path.cpp +# End Source File +# Begin Source File + +SOURCE=.\kilib\registry.cpp +# End Source File +# Begin Source File + +SOURCE=.\kilib\stdafx.cpp +# ADD CPP /Yc"stdafx.h" +# End Source File +# Begin Source File + +SOURCE=.\kilib\string.cpp +# End Source File +# Begin Source File + +SOURCE=.\kilib\textfile.cpp +# End Source File +# Begin Source File + +SOURCE=.\kilib\thread.cpp +# End Source File +# Begin Source File + +SOURCE=.\kilib\window.cpp +# End Source File +# Begin Source File + +SOURCE=.\kilib\winutil.cpp +# End Source File +# End Group +# Begin Group "Header" + +# PROP Default_Filter "h" +# Begin Source File + +SOURCE=.\kilib\app.h +# End Source File +# Begin Source File + +SOURCE=.\kilib\cmdarg.h +# End Source File +# Begin Source File + +SOURCE=.\kilib\ctrl.h +# End Source File +# Begin Source File + +SOURCE=.\kilib\file.h +# End Source File +# Begin Source File + +SOURCE=.\kilib\find.h +# End Source File +# Begin Source File + +SOURCE=.\kilib\log.h +# End Source File +# Begin Source File + +SOURCE=.\kilib\memory.h +# End Source File +# Begin Source File + +SOURCE=.\kilib\path.h +# End Source File +# Begin Source File + +SOURCE=.\kilib\registry.h +# End Source File +# Begin Source File + +SOURCE=.\kilib\string.h +# End Source File +# Begin Source File + +SOURCE=.\kilib\textfile.h +# End Source File +# Begin Source File + +SOURCE=.\kilib\thread.h +# End Source File +# Begin Source File + +SOURCE=.\kilib\types.h +# End Source File +# Begin Source File + +SOURCE=.\kilib\window.h +# End Source File +# Begin Source File + +SOURCE=.\kilib\winutil.h +# End Source File +# End Group +# Begin Group "KTL" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\kilib\ktlaptr.h +# End Source File +# Begin Source File + +SOURCE=.\kilib\ktlarray.h +# End Source File +# Begin Source File + +SOURCE=.\kilib\ktlgap.h +# End Source File +# End Group +# Begin Source File + +SOURCE=.\kilib\kilib.h +# End Source File +# Begin Source File + +SOURCE=.\kilib\stdafx.h +# End Source File +# End Group +# Begin Group "editwing" + +# PROP Default_Filter "" +# Begin Group "public" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\editwing\ewCommon.h +# End Source File +# Begin Source File + +SOURCE=.\editwing\ewCtrl1.h +# End Source File +# Begin Source File + +SOURCE=.\editwing\ewDoc.h +# End Source File +# Begin Source File + +SOURCE=.\editwing\ewView.h +# End Source File +# End Group +# Begin Group "private" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\editwing\ip_ctrl1.cpp +# End Source File +# Begin Source File + +SOURCE=.\editwing\ip_cursor.cpp +# End Source File +# Begin Source File + +SOURCE=.\editwing\ip_doc.h +# End Source File +# Begin Source File + +SOURCE=.\editwing\ip_draw.cpp +# End Source File +# Begin Source File + +SOURCE=.\editwing\ip_parse.cpp +# End Source File +# Begin Source File + +SOURCE=.\editwing\ip_scroll.cpp +# End Source File +# Begin Source File + +SOURCE=.\editwing\ip_text.cpp +# End Source File +# Begin Source File + +SOURCE=.\editwing\ip_view.h +# End Source File +# Begin Source File + +SOURCE=.\editwing\ip_wrap.cpp +# End Source File +# End Group +# Begin Source File + +SOURCE=.\editwing\editwing.h +# End Source File +# End Group +# End Target +# End Project ADDED kilib.dsw Index: kilib.dsw ================================================================== --- kilib.dsw +++ kilib.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# 警告: このワークスペース ファイル を編集または削除しないでください! + +############################################################################### + +Project: "kilib"=.\kilib.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + ADDED kilib.sln Index: kilib.sln ================================================================== --- kilib.sln +++ kilib.sln @@ -0,0 +1,23 @@ +サソ +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kilib", "kilib.vcproj", "{910E868F-4E52-4D55-9C4E-4563F5934517}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Unicode Release|Win32 = Unicode Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {910E868F-4E52-4D55-9C4E-4563F5934517}.Debug|Win32.ActiveCfg = Debug|Win32 + {910E868F-4E52-4D55-9C4E-4563F5934517}.Debug|Win32.Build.0 = Debug|Win32 + {910E868F-4E52-4D55-9C4E-4563F5934517}.Release|Win32.ActiveCfg = Release|Win32 + {910E868F-4E52-4D55-9C4E-4563F5934517}.Release|Win32.Build.0 = Release|Win32 + {910E868F-4E52-4D55-9C4E-4563F5934517}.Unicode Release|Win32.ActiveCfg = Unicode Release|Win32 + {910E868F-4E52-4D55-9C4E-4563F5934517}.Unicode Release|Win32.Build.0 = Unicode Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal ADDED kilib.vcproj Index: kilib.vcproj ================================================================== --- kilib.vcproj +++ kilib.vcproj @@ -0,0 +1,696 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ADDED kilib/app.cpp Index: kilib/app.cpp ================================================================== --- kilib/app.cpp +++ kilib/app.cpp @@ -0,0 +1,173 @@ +#include "stdafx.h" +#include "app.h" +#include "log.h" +#include "memory.h" +#include "thread.h" +#include "window.h" +#include "string.h" +using namespace ki; + + + +//========================================================================= + +App* App::pUniqueInstance_; + +inline App::App() + : exitcode_ (-1) + , loadedModule_(0) + , hInst_ (::GetModuleHandle(NULL)) +{ + // 唯一のインスタンスは私です。 + pUniqueInstance_ = this; +} + +#pragma warning( disable : 4722 ) // 警告:デストラクタに値が戻りません +App::~App() +{ + // ロード済みモジュールがあれば閉じておく + if( loadedModule_ & COM ) + ::CoUninitialize(); + if( loadedModule_ & OLE ) + ::OleUninitialize(); + + // 終〜了〜 + ::ExitProcess( exitcode_ ); +} + +inline void App::SetExitCode( int code ) +{ + // 終了コードを設定 + exitcode_ = code; +} + +void App::InitModule( imflag what ) +{ + // 初期化済みでなければ初期化する + if( !(loadedModule_ & what) ) + switch( what ) + { + case CTL: ::InitCommonControls(); break; + case COM: ::CoInitialize( NULL ); break; + case OLE: ::OleInitialize( NULL );break; + } + + // 今回初期化したモノを記憶 + loadedModule_ |= what; +} + +void App::Exit( int code ) +{ + // 終了コードを設定して + SetExitCode( code ); + + // 自殺 + this->~App(); +} + + + +//------------------------------------------------------------------------- + +const OSVERSIONINFO& App::osver() +{ + static OSVERSIONINFO s_osVer; + if( s_osVer.dwOSVersionInfoSize == 0 ) + { + // 初回だけは情報取得 + s_osVer.dwOSVersionInfoSize = sizeof( s_osVer ); + ::GetVersionEx( &s_osVer ); + } + return s_osVer; +} + +bool App::isNewTypeWindows() +{ + static const OSVERSIONINFO& v = osver(); + return ( + ( v.dwPlatformId==VER_PLATFORM_WIN32_NT && v.dwMajorVersion>=5 ) + || ( v.dwPlatformId==VER_PLATFORM_WIN32_WINDOWS && + v.dwMajorVersion*100+v.dwMinorVersion>=410 ) + ); +} + +bool App::isWin95() +{ + static const OSVERSIONINFO& v = osver(); + return ( + v.dwPlatformId==VER_PLATFORM_WIN32_WINDOWS && + v.dwMajorVersion==4 && + v.dwMinorVersion==0 + ); +} + +bool App::isNT() +{ + static const OSVERSIONINFO& v = osver(); + return v.dwPlatformId==VER_PLATFORM_WIN32_NT; +} + + + +//========================================================================= + +extern int kmain(); + +namespace ki +{ + void APIENTRY Startup() + { + // Startup : + // プログラム開始すると、真っ先にここに来ます。 + + // C++のローカルオブジェクトの破棄順序の仕様に + // 自信がないので(^^;、スコープを利用して順番を強制 + // たぶん宣言の逆順だとは思うんだけど… + + LOGGER( "StartUp" ); + App myApp; + { + LOGGER( "StartUp app ok" ); + ThreadManager myThr; + { + LOGGER( "StartUp thr ok" ); + MemoryManager myMem; + { + LOGGER( "StartUp mem ok" ); + IMEManager myIME; + { + LOGGER( "StartUp ime ok" ); + String::LibInit(); + { + const int r = kmain(); + myApp.SetExitCode( r ); + } + } + } + } + } + } +} + +#ifdef SUPERTINY + + extern "C" int __cdecl _purecall(){return 0;} + #ifdef _DEBUG + int main(){return 0;} + #endif + #pragma comment(linker, "/entry:\"Startup\"") + +#else + + // VS2005でビルドしてもWin95で動くようにするため + #if _MSC_VER >= 1400 + extern "C" BOOL WINAPI _imp__IsDebuggerPresent() { return FALSE; } + #endif + + int APIENTRY WinMain( HINSTANCE, HINSTANCE, LPSTR, int ) + { + ki::Startup(); + return 0; + } + +#endif ADDED kilib/app.h Index: kilib/app.h ================================================================== --- kilib/app.h +++ kilib/app.h @@ -0,0 +1,173 @@ +#ifndef _KILIB_APP_H_ +#define _KILIB_APP_H_ +#include "types.h" +#include "log.h" +#ifndef __ccdoc__ +namespace ki { +#endif + + + +//========================================================================= +//@{ @pkg ki.Core //@} +//@{ +// アプリケーション全体の統括 +// +// アプリ起動/終了用処理を担当します。 +// 旧kilibと違って、ユーザー側のアプリケーションクラスを +// ここから派生させることは出来ません。ユーザーのコードは、 +// 必ず kmain() というグローバル関数から実行開始されます。 +// このAppクラス自体は、主にHINSTANCEの管理を行うだけ。 +//@} +//========================================================================= + +class App +{ +public: + + enum imflag { CTL=1, COM=2, OLE=4 }; + + //@{ + // 種々のモジュールを初期化する + // + // これで初期化しておくと、App終了時に自動で + // 終了処理が行われるので簡単便利でございます。 + // @param what CTL(コモンコントロール)、COM、OLE + //@} + void InitModule( imflag what ); + + //@{ プロセス強制終了 //@} + void Exit( int code ); + + //@{ リソース //@} + HACCEL LoadAccel( LPCTSTR name ); + + //@{ リソース //@} + HACCEL LoadAccel( UINT id ); + + //@{ リソース //@} + HBITMAP LoadBitmap( LPCTSTR name ); + + //@{ リソース //@} + HBITMAP LoadBitmap( UINT id ); + + //@{ リソース(OBM_XXXX) //@} + HBITMAP LoadOemBitmap( LPCTSTR obm ); + + //@{ リソース //@} + HCURSOR LoadCursor( LPCTSTR name ); + + //@{ リソース //@} + HCURSOR LoadCursor( UINT id ); + + //@{ リソース(IDC_XXXX) //@} + HCURSOR LoadOemCursor( LPCTSTR idc ); + + //@{ リソース //@} + HICON LoadIcon( LPCTSTR name ); + + //@{ リソース //@} + HICON LoadIcon( UINT id ); + + //@{ リソース(IDI_XXXX) //@} + HICON LoadOemIcon( LPCTSTR idi ); + + //@{ リソース //@} + HMENU LoadMenu( LPCTSTR name ); + + //@{ リソース //@} + HMENU LoadMenu( UINT id ); + + //@{ リソース //@} + int LoadString( UINT id, LPTSTR buf, int siz ); + +public: + + //@{ インスタンスハンドル //@} + HINSTANCE hinst() const; + + //@{ Windowsのバージョン //@} + static const OSVERSIONINFO& osver(); + static bool isWin95(); + static bool isNT(); + static bool isNewTypeWindows(); + +private: + + App(); + ~App(); + void SetExitCode( int code ); + +private: + + int exitcode_; + ulong loadedModule_; + const HINSTANCE hInst_; + static App* pUniqueInstance_; + +private: + + friend void APIENTRY Startup(); + friend inline App& app(); + NOCOPY(App); +}; + + + +//------------------------------------------------------------------------- + +//@{ 唯一のアプリ情報オブジェクトを返す //@} +inline App& app() + { return *App::pUniqueInstance_; } + +inline HACCEL App::LoadAccel( LPCTSTR name ) + { return ::LoadAccelerators( hInst_, name ); } + +inline HACCEL App::LoadAccel( UINT id ) + { return ::LoadAccelerators( hInst_, MAKEINTRESOURCE(id) ); } + +inline HBITMAP App::LoadBitmap( LPCTSTR name ) + { return ::LoadBitmap( hInst_, name ); } + +inline HBITMAP App::LoadBitmap( UINT id ) + { return ::LoadBitmap( hInst_, MAKEINTRESOURCE(id) ); } + +inline HBITMAP App::LoadOemBitmap( LPCTSTR obm ) + { return ::LoadBitmap( NULL, obm ); } + +inline HCURSOR App::LoadCursor( LPCTSTR name ) + { return ::LoadCursor( hInst_, name ); } + +inline HCURSOR App::LoadCursor( UINT id ) + { return ::LoadCursor( hInst_, MAKEINTRESOURCE(id) ); } + +inline HCURSOR App::LoadOemCursor( LPCTSTR idc ) + { return ::LoadCursor( NULL, idc ); } + +inline HICON App::LoadIcon( LPCTSTR name ) + { return ::LoadIcon( hInst_, name ); } + +inline HICON App::LoadIcon( UINT id ) + { return ::LoadIcon( hInst_, MAKEINTRESOURCE(id) ); } + +inline HICON App::LoadOemIcon( LPCTSTR idi ) + { return ::LoadIcon( NULL, idi ); } + +inline HMENU App::LoadMenu( LPCTSTR name ) + { return ::LoadMenu( hInst_, name ); } + +inline HMENU App::LoadMenu( UINT id ) + { return ::LoadMenu( hInst_, MAKEINTRESOURCE(id) ); } + +inline int App::LoadString( UINT id, LPTSTR buf, int siz ) + { return ::LoadString( hInst_, id, buf, siz ); } + +inline HINSTANCE App::hinst() const + { return hInst_; } + + + +//========================================================================= + +} // namespace ki +#endif // _KILIB_APP_H_ ADDED kilib/cmdarg.cpp Index: kilib/cmdarg.cpp ================================================================== --- kilib/cmdarg.cpp +++ kilib/cmdarg.cpp @@ -0,0 +1,44 @@ +#include "stdafx.h" +#include "cmdarg.h" +#include "string.h" +using namespace ki; + + + +//========================================================================= + +Argv::Argv( const TCHAR* cmd ) +{ + TCHAR *p, endc; + + buf_ = (p=new TCHAR[::lstrlen(cmd)+1]); + ::lstrcpy( p, cmd ); + + while( *p != TEXT('\0') ) + { + // 引数を区切る空白をスキップ + while( *p == TEXT(' ') ) + ++p; + + // " だったら、その旨記録してさらに一個進める + if( *p == TEXT('\"') ) + endc=TEXT('\"'), ++p; + else + endc=TEXT(' '); + + // 文字列終端なら終了 + if( *p == TEXT('\0') ) + break; + + // 引数リストへ保存 + arg_.Add( p ); + + // 引数の終わりへ… + while( *p!=endc && *p!=TEXT('\0') ) + ++p; + + // 終わりは'\0'にすることによって、引数を区切る + if( *p != TEXT('\0') ) + *p++ = TEXT('\0'); + } +} ADDED kilib/cmdarg.h Index: kilib/cmdarg.h ================================================================== --- kilib/cmdarg.h +++ kilib/cmdarg.h @@ -0,0 +1,62 @@ +#ifndef _KILIB_CMDARG_H_ +#define _KILIB_CMDARG_H_ +#include "types.h" +#include "memory.h" +#include "ktlaptr.h" +#include "ktlarray.h" +#ifndef __ccdoc__ +namespace ki { +#endif + + + +//========================================================================= +//@{ @pkg ki.StdLib //@} +//@{ +// コマンドライン文字列の分割 +// +// ただ単にスペースと二重引用符を考慮して区切るだけです。 +//@} +//========================================================================= + +class Argv : public Object +{ +public: + + //@{ 指定された文字列を分割する //@} + Argv( const TCHAR* cmd = GetCommandLine() ); + + //@{ 引数Get //@} + const TCHAR* operator[]( ulong i ) const; + + //@{ 引数の個数 //@} + ulong size() const; + +private: + + darr buf_; + storage arg_; + +private: + + NOCOPY(Argv); +}; + + + +//------------------------------------------------------------------------- +#ifndef __ccdoc__ + +inline const TCHAR* Argv::operator []( ulong i ) const + { return arg_[i]; } + +inline ulong Argv::size() const + { return arg_.size(); } + + + +//========================================================================= + +#endif // __ccdoc__ +} // namespace ki +#endif // _KILIB_CMDARG_H_ ADDED kilib/ctrl.cpp Index: kilib/ctrl.cpp ================================================================== --- kilib/ctrl.cpp +++ kilib/ctrl.cpp @@ -0,0 +1,66 @@ +#include "stdafx.h" +#include "app.h" +#include "ctrl.h" +using namespace ki; + + + +//========================================================================= + +StatusBar::StatusBar() +{ + app().InitModule( App::CTL ); +} + +bool StatusBar::Create( HWND parent ) +{ + HWND h = ::CreateStatusWindow( + WS_CHILD|WS_VISIBLE|SBARS_SIZEGRIP, + TEXT(""), parent, 1787 ); + if( h == NULL ) + return false; + + SetStatusBarVisible(); + SetHwnd( h ); + AutoResize( false ); + return true; +} + +int StatusBar::AutoResize( bool maximized ) +{ + // サイズ自動変更 + SendMsg( WM_SIZE ); + + // 変更後のサイズを取得 + RECT rc; + getPos( &rc ); + width_ = rc.right - rc.left; + if( !maximized ) + width_ -= 15; + return (isStatusBarVisible() ? rc.bottom - rc.top : 0); +} + +bool StatusBar::PreTranslateMessage( MSG* ) +{ + // 何もしない + return false; +} + + + +//========================================================================= + +void ComboBox::Select( const TCHAR* str ) +{ + // SELECTSTRING は先頭が合ってる物に全てにマッチするので使えない。 + // おそらくインクリメンタルサーチとかに使うべきものなのだろう。 + size_t i = + SendMsg( CB_FINDSTRINGEXACT, ~0, reinterpret_cast(str) ); + if( i != CB_ERR ) + SendMsg( CB_SETCURSEL, i ); +} + +bool ComboBox::PreTranslateMessage( MSG* ) +{ + return false; +} ADDED kilib/ctrl.h Index: kilib/ctrl.h ================================================================== --- kilib/ctrl.h +++ kilib/ctrl.h @@ -0,0 +1,108 @@ +#ifndef _KILIB_CTRL_H_ +#define _KILIB_CTRL_H_ +#include "window.h" +#ifndef __ccdoc__ +namespace ki { +#endif + + + +//========================================================================= +//@{ @pkg ki.Window //@} +//@{ +// ステータスバー +//@} +//========================================================================= + +class StatusBar : public Window +{ +public: + + StatusBar(); + bool Create( HWND parent ); + int AutoResize( bool maximized ); + void SetText( const TCHAR* str, int part=0 ); + void SetTipText( const TCHAR* tip, int part=0 ); + void SetParts( int n, int* parts ); + void SetStatusBarVisible(bool b=true); + +public: + + int width() const; + bool isStatusBarVisible() const; + +private: + + virtual bool PreTranslateMessage( MSG* ); + +private: + + int width_; + bool visible_; +}; + + + +//------------------------------------------------------------------------- +#ifndef __ccdoc__ + +inline int StatusBar::width() const + { return width_; } + +inline bool StatusBar::isStatusBarVisible() const + { return visible_; } + +inline void StatusBar::SetParts( int n, int* parts ) + { SendMsg( SB_SETPARTS, n, reinterpret_cast(parts) ); } + +inline void StatusBar::SetText( const TCHAR* str, int part ) + { SendMsg( SB_SETTEXT, part, reinterpret_cast(str) ); } + +inline void StatusBar::SetStatusBarVisible(bool b) + { ::ShowWindow( hwnd(), b?SW_SHOW:SW_HIDE ); visible_=b; } + + + +#endif // __ccdoc__ +//========================================================================= +//@{ +// コンボボックス +//@} +//========================================================================= + +class ComboBox : public Window +{ +public: + explicit ComboBox( HWND cb ); + explicit ComboBox( HWND dlg, UINT id ); + void Add( const TCHAR* str ); + void Select( const TCHAR* str ); + int GetCurSel(); +private: + virtual bool PreTranslateMessage( MSG* ); +}; + + + +//------------------------------------------------------------------------- +#ifndef __ccdoc__ + +inline ComboBox::ComboBox( HWND cb ) + { SetHwnd(cb); } + +inline ComboBox::ComboBox( HWND dlg, UINT id ) + { SetHwnd( ::GetDlgItem(dlg,id) ); } + +inline void ComboBox::Add( const TCHAR* str ) + { SendMsg( CB_ADDSTRING, 0, reinterpret_cast(str) ); } + +inline int ComboBox::GetCurSel() + { return (int) SendMsg( CB_GETCURSEL ); } + + + +//========================================================================= + +#endif // __ccdoc__ +} // namespace ki +#endif // _KILIB_CTRL_H_ ADDED kilib/file.cpp Index: kilib/file.cpp ================================================================== --- kilib/file.cpp +++ kilib/file.cpp @@ -0,0 +1,160 @@ +#include "stdafx.h" +#include "file.h" +using namespace ki; + + + +//========================================================================= + +FileR::FileR() + : handle_ ( INVALID_HANDLE_VALUE ) + , fmo_ ( NULL ) + , basePtr_( NULL ) +{ +} + +FileR::~FileR() +{ + Close(); +} + +bool FileR::Open( const TCHAR* fname ) +{ + Close(); + + // ファイルを読みとり専用で開く + handle_ = ::CreateFile( + fname, GENERIC_READ, + FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL + ); + if( handle_ == INVALID_HANDLE_VALUE ) + return false; + + // サイズを取得 + size_ = ::GetFileSize( handle_, NULL ); + + if( size_==0 ) + { + // 0バイトのファイルはマッピング出来ないので適当に回避 + basePtr_ = &size_; + } + else + { + // マッピングオブジェクトを作る + fmo_ = ::CreateFileMapping( + handle_, NULL, PAGE_READONLY, 0, 0, NULL ); + if( fmo_ == NULL ) + { + ::CloseHandle( handle_ ); + handle_ = INVALID_HANDLE_VALUE; + return false; + } + + // ビュー + basePtr_ = ::MapViewOfFile( fmo_, FILE_MAP_READ, 0, 0, 0 ); + if( basePtr_ == NULL ) + { + ::CloseHandle( fmo_ ); + ::CloseHandle( handle_ ); + handle_ = INVALID_HANDLE_VALUE; + return false; + } + } + return true; +} + +void FileR::Close() +{ + if( handle_ != INVALID_HANDLE_VALUE ) + { + // ヘンテコマッピングをしてなければここで解放 + if( basePtr_ != &size_ ) + ::UnmapViewOfFile( const_cast(basePtr_) ); + basePtr_ = NULL; + + if( fmo_ != NULL ) + ::CloseHandle( fmo_ ); + fmo_ = NULL; + + ::CloseHandle( handle_ ); + handle_ = INVALID_HANDLE_VALUE; + } +} + + + +//========================================================================= + +FileW::FileW() + : BUFSIZE( 65536 ) + , handle_( INVALID_HANDLE_VALUE ) + , buf_ ( new uchar[BUFSIZE] ) +{ +} + +FileW::~FileW() +{ + Close(); + delete [] buf_; +} + +inline void FileW::Flush() +{ + DWORD dummy; + ::WriteFile( handle_, buf_, bPos_, &dummy, NULL ); + bPos_ = 0; +} + +bool FileW::Open( const TCHAR* fname, bool creat ) +{ + Close(); + + // ファイルを書き込み専用で開く + handle_ = ::CreateFile( fname, + GENERIC_WRITE, FILE_SHARE_READ, NULL, + creat ? CREATE_ALWAYS : OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL ); + if( handle_ == INVALID_HANDLE_VALUE ) + return false; + + bPos_ = 0; + return true; +} + +void FileW::Close() +{ + if( handle_ != INVALID_HANDLE_VALUE ) + { + Flush(); + ::CloseHandle( handle_ ); + handle_ = INVALID_HANDLE_VALUE; + } +} + +void FileW::Write( const void* dat, ulong siz ) +{ + const uchar* udat = static_cast(dat); + + while( (BUFSIZE-bPos_) <= siz ) + { + memmove( buf_+bPos_, udat, BUFSIZE-bPos_ ); + siz -= (BUFSIZE-bPos_); + udat += (BUFSIZE-bPos_); + bPos_ = BUFSIZE; + Flush(); + } + + memmove( buf_+bPos_, udat, siz ); + bPos_ += siz; +} + +void FileW::WriteC( uchar ch ) +{ + if( (BUFSIZE-bPos_) <= 1 ) + Flush(); + + buf_[bPos_++] = ch; +} + ADDED kilib/file.h Index: kilib/file.h ================================================================== --- kilib/file.h +++ kilib/file.h @@ -0,0 +1,120 @@ +#ifndef _KILIB_FILE_H_ +#define _KILIB_FILE_H_ +#include "types.h" +#include "memory.h" +#ifndef __ccdoc__ +namespace ki { +#endif + + + +//========================================================================= +//@{ @pkg ki.StdLib //@} +//@{ +// 簡易ファイル読込 +// +// ファイルマッピングを用いるので、扱いが簡単でわりと高速です。 +// ただし困ったことに、4GBまでしか開けません。 +//@} +//========================================================================= + +class FileR : public Object +{ +public: + + FileR(); + ~FileR(); + + //@{ + // 開く + // @param fname ファイル名 + // @return 開けたかどうか + //@} + bool Open( const TCHAR* fname ); + + //@{ + // 閉じる + //@} + void Close(); + +public: + + //@{ ファイルサイズ //@} + ulong size() const; + + //@{ ファイル内容をマップしたアドレス取得 //@} + const uchar* base() const; + +private: + + HANDLE handle_; + HANDLE fmo_; + ulong size_; + const void* basePtr_; + +private: + + NOCOPY(FileR); +}; + + + +//------------------------------------------------------------------------- + +inline ulong FileR::size() const + { return size_; } + +inline const uchar* FileR::base() const + { return static_cast(basePtr_); } + + + +//========================================================================= +//@{ +// 簡易ファイル書き込み +// +// てきとーにバッファリングしつつ。 +//@} +//========================================================================= + +class FileW : public Object +{ +public: + + FileW(); + ~FileW(); + + //@{ 開く //@} + bool Open( const TCHAR* fname, bool creat=true ); + + //@{ 閉じる //@} + void Close(); + + //@{ 書く //@} + void Write( const void* buf, ulong siz ); + + //@{ 一文字書く //@} + void WriteC( uchar ch ); + +public: + + void Flush(); + +private: + + const int BUFSIZE; + HANDLE handle_; + uchar* const buf_; + ulong bPos_; + +private: + + NOCOPY(FileW); +}; + + + +//========================================================================= + +} // namespace ki +#endif // _KILIB_FILE_H_ ADDED kilib/find.cpp Index: kilib/find.cpp ================================================================== --- kilib/find.cpp +++ kilib/find.cpp @@ -0,0 +1,74 @@ +#include "stdafx.h" +#include "find.h" +using namespace ki; + + + +//========================================================================= + +namespace { +static inline bool isDots(const TCHAR* p) +{ + return (*p=='.' && (p[1]=='\0' || (p[1]=='.' && p[2]=='\0'))); +} +} + +bool FindFile::FindFirst( const TCHAR* wild, WIN32_FIND_DATA* pfd ) +{ + HANDLE xh = ::FindFirstFile( wild, pfd ); + if( xh==INVALID_HANDLE_VALUE ) + return false; + while( isDots(pfd->cFileName) ) + if( !::FindNextFile( xh, pfd ) ) + { + ::FindClose( xh ); + return false; + } + ::FindClose( xh ); + return true; +} + +void FindFile::Close() +{ + first_ = true; + if( han_ != INVALID_HANDLE_VALUE ) + ::FindClose( han_ ), han_ = INVALID_HANDLE_VALUE; +} + +bool FindFile::Begin( const TCHAR* wild ) +{ + Close(); + + han_ = ::FindFirstFile( wild, &fd_ ); + if( han_ == INVALID_HANDLE_VALUE ) + return false; + + while( isDots(fd_.cFileName) ) + if( !::FindNextFile( han_, &fd_ ) ) + { + Close(); + return false; + } + return true; +} + +bool FindFile::Next( WIN32_FIND_DATA* pfd ) +{ + if( han_ == INVALID_HANDLE_VALUE ) + return false; + + if( first_ ) + { + first_ = false; + memmove( pfd, &fd_, sizeof(fd_) ); + return true; + } + if( !::FindNextFile( han_, pfd ) ) + return false; + while( isDots(fd_.cFileName) ) + if( !::FindNextFile( han_, pfd ) ) + return false; + return true; +} + +#undef isDots ADDED kilib/find.h Index: kilib/find.h ================================================================== --- kilib/find.h +++ kilib/find.h @@ -0,0 +1,71 @@ +#ifndef _KILIB_FIND_H_ +#define _KILIB_FIND_H_ +#include "types.h" +#include "memory.h" +#include "path.h" +#ifndef __ccdoc__ +namespace ki { +#endif + + + +//========================================================================= +//@{ @pkg ki.WinUtil //@} +//@{ +// ファイル検索 +// +// Win32 APIのFindFirstFile等を用いて、ディレクトリ内の +// ファイルリストアップを行います。 +//@} +//========================================================================= + +class FindFile : public Object +{ +public: + + //@{ コンストラクタ //@} + FindFile(); + + //@{ デストラクタ //@} + ~FindFile(); + + //@{ 検索終了処理 //@} + void Close(); + +public: //-- 外向きインターフェイス -------------------------- + + bool Begin( const TCHAR* wild ); + bool Next( WIN32_FIND_DATA* pfd ); + +public: + + //@{ static版。マッチする最初のファイルを取得 //@} + static bool FindFirst( const TCHAR* wild, WIN32_FIND_DATA* pfd ); + +private: + + HANDLE han_; + bool first_; + WIN32_FIND_DATA fd_; + +private: + + NOCOPY(FindFile); +}; + + + +//------------------------------------------------------------------------- + +inline FindFile::FindFile() + : han_( INVALID_HANDLE_VALUE ) {} + +inline FindFile::~FindFile() + { Close(); } + + + +//========================================================================= + +} // namespace ki +#endif // _KILIB_FIND_H_ ADDED kilib/kilib.h Index: kilib/kilib.h ================================================================== --- kilib/kilib.h +++ kilib/kilib.h @@ -0,0 +1,24 @@ +#ifndef _KILIB_KILIB_H_ +#define _KILIB_KILIB_H_ + +#include "types.h" +#include "app.h" +#include "thread.h" +#include "memory.h" +#include "window.h" +#include "string.h" +#include "cmdarg.h" +#include "path.h" +#include "file.h" +#include "ctrl.h" +#include "textfile.h" +#include "winutil.h" +#include "find.h" +#include "registry.h" +#include "log.h" + +#include "ktlaptr.h" +#include "ktlarray.h" +#include "ktlgap.h" + +#endif // _KILIB_KILIB_H_ ADDED kilib/ktlaptr.h Index: kilib/ktlaptr.h ================================================================== --- kilib/ktlaptr.h +++ kilib/ktlaptr.h @@ -0,0 +1,273 @@ +#ifndef _KILIB_KTL_APTR_H_ +#define _KILIB_KTL_APTR_H_ +#include "types.h" +#ifdef _MSC_VER +#pragma warning( disable : 4284 ) // 警告:->のリターン型がうにゃうにゃ +#pragma warning( disable : 4150 ) // 警告:deleteの定義がうにょうにょ +#endif +#ifndef __ccdoc__ +namespace ki { +#endif + + + +//========================================================================= +//@{ @pkg ki.KTL //@} +//@{ +// 自動ポインタ +// +// 私の期待する範囲では概ね std::auto_ptr と同じ動作をすると思う…。 +// 車輪の最発明ばんざーい! +//@} +//========================================================================= + +template +class aptr +{ +public: + + //@{ コンストラクタ //@} + explicit aptr( T* p = NULL ) + : obj_( p ) {} + + //@{ デストラクタ //@} + ~aptr() + { delete obj_; } + + //@{ 所有権移動 //@} + aptr( aptr& r ) + : obj_ ( r.release() ) {} + + //@{ 所有権移動 //@} + aptr& operator=( aptr& r ) + { + if( obj_ != r.obj_ ) + { + delete obj_; + obj_ = r.release(); + } + return *this; + } + +public: + + //@{ 間接演算子 //@} + T& operator*() const + { return *obj_; } + + //@{ メンバ参照 //@} + T* operator->() const + { return obj_; } + + //@{ ポインタ取得 //@} + T* get() const + { return obj_; } + + //@{ 所有権解放 //@} + T* release() + { + T* ptr = obj_; + obj_ = NULL; + return ptr; + } + + //@{ 有効かどうか //@} + bool isValid() const + { + return (obj_ != NULL); + } + +private: + + mutable T* obj_; +}; + + + +//========================================================================= +//@{ +// 自動ポインタ(配列版) +//@} +//========================================================================= + +template +class aarr +{ +public: + + //@{ コンストラクタ //@} + explicit aarr( T* p = NULL ) + : obj_( p ) {} + + //@{ デストラクタ //@} + ~aarr() + { delete [] obj_; } + + //@{ 所有権移動 何故かbccで上手く行かない部分があるのでconst付き //@} + aarr( const aarr& r ) + : obj_ ( const_cast&>(r).release() ) {} + + //@{ 所有権移動 //@} + aarr& operator=( aarr& r ) + { + if( obj_ != r.obj_ ) + { + delete [] obj_; + obj_ = r.release(); + } + return *this; + } + +public: + + //@{ ポインタ取得 //@} + T* get() const + { return obj_; } + + //@{ 所有権解放 //@} + T* release() + { + T* ptr = obj_; + obj_ = NULL; + return ptr; + } + + //@{ 有効かどうか //@} + bool isValid() const + { + return (obj_ != NULL); + } + +public: + + //@{ 配列要素アクセス //@} + T& operator[]( int i ) const + { return obj_[i]; } + +private: + + mutable T* obj_; +}; + + + +//========================================================================= +//@{ +// 削除権専有ポインタ +// +// 「リソースの獲得はコンストラクタで・解放はデストラクタで」を +// 徹底できるならこんなの使わずに、迷わず const auto_ptr を用いる +// べきです。べきですが、メンバ初期化リストで this を使うとVC++の +// コンパイラに怒られるのが気持悪いので、ついこっちを使ってコンストラクタ +// 関数内で初期化してしまうのでふ…(^^; +//@} +//========================================================================= + +template +class dptr +{ +public: + + //@{ コンストラクタ //@} + explicit dptr( T* p = NULL ) + : obj_( p ) {} + + //@{ デストラクタ //@} + ~dptr() + { delete obj_; } + + //@{ 新しいオブジェクトを所有。古いのは削除 //@} + void operator=( T* p ) + { + delete obj_; // 古いのは削除 + obj_ = p; + } + + //@{ 有効かどうか //@} + bool isValid() const + { + return (obj_ != NULL); + } + +public: + + //@{ 間接演算子 //@} + T& operator*() const + { return *obj_; } + + //@{ メンバ参照 //@} + T* operator->() const + { return obj_; } + + //@{ ポインタ取得 //@} + T* get() const + { return obj_; } + +private: + + T* obj_; + +private: + + NOCOPY(dptr); +}; + + + +//========================================================================= +//@{ +// 削除権専有ポインタ(配列版) +//@} +//========================================================================= + +template +class darr +{ +public: + + //@{ コンストラクタ //@} + explicit darr( T* p = NULL ) + : obj_( p ) {} + + //@{ デストラクタ //@} + ~darr() + { delete [] obj_; } + + //@{ 新しいオブジェクトを所有。古いのは削除 //@} + void operator=( T* p ) + { + delete [] obj_; // 古いのは削除 + obj_ = p; + } + + //@{ 有効かどうか //@} + bool isValid() const + { + return (obj_ != NULL); + } + +public: + + //@{ 配列要素アクセス //@} + T& operator[]( int i ) const + { return obj_[i]; } + +private: + + T* obj_; + +private: + + NOCOPY(darr); +}; + + + +//========================================================================= + +#ifdef _MSC_VER +#pragma warning( default : 4150 ) +#pragma warning( default : 4284 ) +#endif +} // namespace ki +#endif // _KILIB_KTL_APTR_H_ ADDED kilib/ktlarray.h Index: kilib/ktlarray.h ================================================================== --- kilib/ktlarray.h +++ kilib/ktlarray.h @@ -0,0 +1,224 @@ +#ifndef _KILIB_KTL_ARRAY_H_ +#define _KILIB_KTL_ARRAY_H_ +#include "types.h" +#include "memory.h" +#ifndef __ccdoc__ +namespace ki { +#endif + + + +//========================================================================= +//@{ @pkg ki.KTL //@} +//@{ +// 基本型専用の可変長配列 +// +// 非常に簡素な作りです。アクセスは好きなところへ自由に可能ですが、 +// 要素の追加・削除は末尾に対してのみ。ビット毎のコピーだとか、 +// 色々エキセントリックな作業をしてますので、そういうことをして良い +// 型以外には使わないでください。 +//@} +//========================================================================= + +template +class storage : public Object +{ +public: + + //@{ + // コンストラクタ + // + // @param alloc_size + // 最初に確保する"メモリの"サイズ。 + // "配列の"サイズではないことに注意。 + //@} + explicit storage( ulong allocSize=20 ) + : alen_( Max( allocSize, 1UL ) ) + , len_ ( 0 ) + , buf_ ( static_cast(mem().Alloc(alen_*sizeof(T))) ) + {} + + ~storage() + { mem().DeAlloc( buf_, alen_*sizeof(T) ); } + + //@{ 末尾に要素を追加 //@} + void Add( const T& obj ) + { + if( len_ >= alen_ ) + ReAllocate( alen_<<2 ); + buf_[ len_++ ] = obj; + } + + //@{ + // 配列サイズを強制変更 + // + // 縮小/拡大のどちらも可能。コンストラクタと違い、 + // 指定した値に基づき最大indexが変化します。 + // @param new_size 新しいサイズ。 + //@} + void ForceSize( ulong newSize ) + { + if( newSize > alen_ ) + ReAllocate( newSize ); + len_ = newSize; + } + +public: + + //@{ 要素数 //@} + ulong size() const + { return len_; } + + //@{ 要素取得 //@} + T& operator[]( size_t i ) + { return buf_[i]; } + + //@{ 要素取得(const) //@} + const T& operator[]( size_t i ) const + { return buf_[i]; } + + //@{ 配列先頭のポインタを返す //@} + const T* head() const + { return buf_; } + +private: + + void ReAllocate( ulong siz ) + { + ulong p = alen_*sizeof(T); + T* newbuf = static_cast + (mem().Alloc( (alen_=siz)*sizeof(T) )); + memmove( newbuf, buf_, len_*sizeof(T) ); + mem().DeAlloc( buf_, p ); + buf_ = newbuf; + } + +private: + + ulong alen_; + ulong len_; + T* buf_; + +private: + + NOCOPY(storage); +}; + + + +//========================================================================= +//@{ +// オブジェクト型にも使える単方向リスト +// +// ほとんど何も出来ません。出来るのは末尾への追加と、独自の +// iteratorによるシーケンシャルなアクセスのみ。 +//@} +//========================================================================= + +template +class olist : public Object +{ +private: + + struct Node { + Node( const T& obj ) + : obj_ ( obj ), next_( NULL ) {} + ~Node() + { delete next_; } + Node* Add( Node* pN ) + { return next_==NULL ? next_=pN : next_->Add(pN); } + T obj_; + Node* next_; + }; + +public: + + struct iterator { + iterator( Node* p=NULL ) : ptr_(p) {} + T& operator*() { return ptr_->obj_; } + T* operator->() const { return &ptr_->obj_; } + bool operator==( const iterator& i ) { return i.ptr_==ptr_; } + bool operator!=( const iterator& i ) { return i.ptr_!=ptr_; } + iterator& operator++() { ptr_=ptr_->next_; return *this; } + private: + Node* ptr_; + }; + +public: + + //@{ コンストラクタ //@} + olist() + : top_( NULL ) {} + + //@{ デストラクタ //@} + ~olist() + { empty(); } + + //@{ 空にする //@} + void empty() + { delete top_; top_ = NULL; } + + //@{ 先頭 //@} + iterator begin() + { return iterator(top_); } + + //@{ 末尾 //@} + iterator end() + { return iterator(); } + + //@{ 末尾に要素を追加 //@} + void Add( const T& obj ) + { + Node* pN = new Node( obj ); + (top_ == NULL) ? top_=pN : top_->Add( pN ); + } + + //@{ 指定要素を削除 //@} + void Del( iterator d ) + { + if( d != end() ) + { + Node *p=top_, *q=NULL; + for( ; p!=NULL; q=p,p=p->next_ ) + if( &p->obj_ == &*d ) + break; + if( q != NULL ) + q->next_ = p->next_; + else + top_ = p->next_; + p->next_ = NULL; + delete p; + } + } + + //@{ 指定要素以降全てを削除 //@} + void DelAfter( iterator d ) + { + if( d != end() ) + if( d == begin() ) + { + empty(); + } + else + { + Node *p=top_, *q=NULL; + for( ; p!=NULL; q=p,p=p->next_ ) + if( &p->obj_ == &*d ) + break; + delete p; + q->next_ = NULL; + } + } + +private: + + Node* top_; + NOCOPY(olist); +}; + + + +//========================================================================= + +} // namespace ki +#endif // _KILIB_KTL_ARRAY_H_ ADDED kilib/ktlgap.h Index: kilib/ktlgap.h ================================================================== --- kilib/ktlgap.h +++ kilib/ktlgap.h @@ -0,0 +1,268 @@ +#ifndef _KILIB_KTL_GAP_H_ +#define _KILIB_KTL_GAP_H_ +#include "types.h" +#include "memory.h" +#ifndef __ccdoc__ +namespace ki { +#endif + + + +//========================================================================= +//@{ @pkg ki.KTL //@} +//@{ +// 基本型専用ギャップバッファ +// +// 小耳に挟んだギャップバッファというもの。 +// ものすごい勘違いをして別物ができているかもしれませんが、 +// 細かいことはあまり気にしないでください。 +// 配列のようにランダムアクセス可能で、 +// 同一箇所への連続した挿入/削除が速いというデータ構造です。 +// ( 下の図で言うと、gap_startへの挿入/削除にはデータの移動が必要ない ) +//
+//@@  D  <--0
+//@@  D
+//@@  |  <--gap_start
+//@@  |
+//@@  D  <--gap_end
+//@@  D
+//@@     <--array_end
+//	
+// メモリイメージをそのままコピーしてる実装なので、 +// プリミティブ型以外では絶対に使わないこと。 +//@} +//========================================================================= + +template +class gapbuf : public Object +{ +public: + + //@{ + // コンストラクタ + // @param alloc_size + // 最初に確保する"メモリの"サイズ。 + // "配列の"サイズではないことに注意。 + //@} + explicit gapbuf( ulong alloc_size=40 ) + : alen_( Max(alloc_size, 10UL) ) + , gs_ ( 0 ) + , ge_ ( alen_ ) + , buf_ ( new T[alen_] ) + {} + ~gapbuf() + { delete [] buf_; } + + //@{ 要素挿入 //@} + void InsertAt( ulong i, const T& x ) + { + MakeGapAt( i ); + buf_[gs_++] = x; + + if( gs_==ge_ ) + Reallocate( alen_<<1 ); + } + + //@{ 要素挿入(複数) //@} + void InsertAt( ulong i, const T* x, ulong len ) + { + MakeGapAt( size() ); + MakeGapAt( i ); + if( ge_-gs_ <= len ) + Reallocate( Max(alen_+len+1, alen_<<1) ); + + memmove( buf_+gs_, x, len*sizeof(T) ); + gs_ += len; + } + + //@{ 末尾に要素を追加 //@} + void Add( const T& x ) + { InsertAt( size(), x ); } + + //@{ 末尾に要素を追加(複数) //@} + void Add( const T* x, ulong len ) + { InsertAt( size(), x, len ); } + + //@{ 要素削除 //@} + void RemoveAt( ulong i, ulong len=1 ) + { + if( i <= gs_ && gs_ <= i+len ) + { + // この場合はメモリ移動の必要がない + // まず前半を削除 + len -= (gs_-i); + gs_ = i; + } + else + { + MakeGapAt( i ); + } + + // 後半を削除 + ge_ += len; + } + + //@{ 要素削除(全部) //@} + void RemoveAll() + { RemoveAt( 0, size() ); } + + //@{ 要素削除(指定index以降全部) //@} + void RemoveToTail( ulong i ) + { RemoveAt( i, size()-i ); } + + //@{ 要素コピー(指定index以降全部) //@} + ulong CopyToTail( ulong i, T* x ) + { return CopyAt( i, size()-i, x ); } + + //@{ 要素コピー //@} + ulong CopyAt( ulong i, ulong len, T* x ) + { + ulong copyed=0; + if( i < gs_ ) + { + // 前半 + copyed += Min( len, gs_-i ); + memmove( x, buf_+i, copyed*sizeof(T) ); + x += copyed; + len -= copyed; + i += copyed; + } + + // 後半 + memmove( x, buf_+(i-gs_)+ge_, len*sizeof(T) ); + return copyed + len; + } + +public: + + //@{ 要素数 //@} + ulong size() const + { return alen_ - (ge_-gs_); } + + //@{ 要素取得 //@} + T& operator[]( ulong i ) + { return buf_[ ( igs_ ) + { + int j = i+(ge_-gs_); + memmove( buf_+gs_, buf_+ge_, (j-ge_)*sizeof(T) ); + ge_ = j; + } + gs_ = i; + } + + void Reallocate( ulong newalen ) + { + T *tmp = new T[newalen], *old=buf_; + const ulong tail = alen_-ge_; + + memmove( tmp, old, gs_*sizeof(T) ); + memmove( tmp+newalen-tail, old+ge_, tail*sizeof(T) ); + delete [] old; + + buf_ = tmp; + ge_ = newalen-tail; + alen_ = newalen; + } + +private: + + NOCOPY(gapbuf); +}; + + + +//========================================================================= +//@{ +// gapbuf + smartptr のふり +// +// 要素削除時にdeleteを実行しっちゃったりするバージョン。 +// 任意オブジェクトをギャップバッファで使いたいときは +// これでてきとーに代用すべし。 +//@} +//========================================================================= + +template +class gapbufobj : public gapbuf +{ +public: + + explicit gapbufobj( ulong alloc_size=40 ) + : gapbuf( alloc_size ) + { } + + void RemoveAt( ulong i, ulong len=1 ) + { + ulong& gs_ = gapbuf::gs_; + ulong& ge_ = gapbuf::ge_; + T**& buf_= gapbuf::buf_; + + if( i <= gs_ && gs_ <= i+len ) + { + // 前半を削除 + for( ulong j=i, ed=gs_; j::MakeGapAt( i ); + } + + // 後半を削除 + for( ulong j=ge_, ed=ge_+len; j::size() ); } + + void RemoveAll( ulong i ) + { RemoveAt( 0, gapbuf::size() ); } + + void RemoveToTail( ulong i ) + { RemoveAt( i, gapbuf::size()-i ); } + +public: + + T& operator[]( ulong i ) + { return *gapbuf::operator[](i); } + + const T& operator[]( ulong i ) const + { return *gapbuf::operator[](i); } + +private: + + NOCOPY(gapbufobj); +}; + + + +//========================================================================= + +} // namespace ki +#endif // _KILIB_KTL_GAP_H_ ADDED kilib/log.cpp Index: kilib/log.cpp ================================================================== --- kilib/log.cpp +++ kilib/log.cpp @@ -0,0 +1,63 @@ +#include "stdafx.h" +#include "log.h" +#include "app.h" +#include "string.h" +using namespace ki; + + + +//========================================================================= + +void Logger::WriteLine( const String& str ) +{ + WriteLine( str.c_str(), str.len()*sizeof(TCHAR) ); +} + +void Logger::WriteLine( const TCHAR* str ) +{ + WriteLine( str, ::lstrlen(str)*sizeof(TCHAR) ); +} + +void Logger::WriteLine( const TCHAR* str, int siz ) +{ +#ifdef DO_LOGGING + // Fileクラス自体のデバッグに使うかもしれないので、 + // Fileクラスを使用することは出来ない。API直叩き + static bool st_firsttime = true; + DWORD dummy; + + // ファイル名 + TCHAR fname[MAX_PATH]; + ::GetModuleFileName( ::GetModuleHandle(NULL), fname, countof(fname) ); + ::lstrcat( fname, TEXT("_log") ); + + // ファイルを書き込み専用で開く + HANDLE h = ::CreateFile( fname, + GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL ); + if( h == INVALID_HANDLE_VALUE ) + return; + + // 初回限定処理 + if( st_firsttime ) + { + ::SetEndOfFile( h ); + #ifdef _UNICODE + ::WriteFile( h, "\xff\xfe", 2, &dummy, NULL ); + #endif + st_firsttime = false; + } + else + { + ::SetFilePointer( h, 0, NULL, FILE_END ); + } + + // 書く + ::WriteFile( h, str, siz, &dummy, NULL ); + ::WriteFile( h, TEXT("\r\n"), sizeof(TEXT("\r")), &dummy, NULL ); + + // 閉じる + ::CloseHandle( h ); + +#endif +} ADDED kilib/log.h Index: kilib/log.h ================================================================== --- kilib/log.h +++ kilib/log.h @@ -0,0 +1,48 @@ +#ifndef _KILIB_LOG_H_ +#define _KILIB_LOG_H_ +#include "types.h" +#ifndef __ccdoc__ +namespace ki { +#endif + + + +class String; + +//========================================================================= +//@{ @pkg ki.Core //@} +//@{ +// ログ取り機能(デバッグ用) +// +// アプリ起動/終了用処理を担当します。 +// 旧kilibと違って、ユーザー側のアプリケーションクラスを +// ここから派生させることは出来ません。ユーザーのコードは、 +// 必ず kmain() というグローバル関数から実行開始されます。 +// このAppクラス自体は、主にHINSTANCEの管理を行うだけ。 +//@} +//========================================================================= + +class Logger +{ +public: + + Logger() {} + void WriteLine( const String& str ); + void WriteLine( const TCHAR* str ); + void WriteLine( const TCHAR* str, int siz ); + +private: + + NOCOPY(Logger); +}; + +#ifdef DO_LOGGING + #define LOGGER(str) Logger().WriteLine(TEXT(str)) +#else + #define LOGGER(x) +#endif + +//========================================================================= + +} // namespace ki +#endif // _KILIB_LOG_H_ ADDED kilib/memory.cpp Index: kilib/memory.cpp ================================================================== --- kilib/memory.cpp +++ kilib/memory.cpp @@ -0,0 +1,492 @@ +#include "stdafx.h" +#include "memory.h" +using namespace ki; + + + +//========================================================================= + +#ifdef SUPERTINY + + static HANDLE g_heap; + + #ifndef _DEBUG + + void* __cdecl operator new( size_t siz ) + { + return ::HeapAlloc( g_heap, HEAP_GENERATE_EXCEPTIONS, siz ); + } + + void __cdecl operator delete( void* ptr ) + { + ::HeapFree( g_heap, 0, ptr ); + } + + #else + + // デバッグ用に回数計測版(^^; + static int allocCounter = 0; + void* __cdecl operator new( size_t siz ) + { + ++allocCounter; + return ::HeapAlloc( g_heap, HEAP_GENERATE_EXCEPTIONS, siz ); + } + void __cdecl operator delete( void* ptr ) + { + ::HeapFree( g_heap, 0, ptr ); + if( ptr != NULL ) + --allocCounter; + } + + #endif + + extern "C" + void* __cdecl memset( void* buf, int ch, size_t n ) + { + BYTE v = BYTE(ch&0xff); + BYTE* ptr = (BYTE*)buf; + DWORD vvvv = (v<<24) | (v<<16) | (v<<8) | v; + for(;n>3;n-=4,ptr+=4) *(DWORD*)ptr = vvvv; + for(;n;--n,++ptr) *ptr = v; + return buf; + } + + void* __cdecl memmove( void* dst, const void* src, size_t cnt ) + { + __asm { + mov esi, [src] ;U esi = const void* src + mov edx, [cnt] ;V edx = void* cnt + mov edi, [dst] ;U edi = ulong dst + mov ebx, edx ;V + mov eax, 03h ;U eax = const ulong 3 (for masking) + add ebx, esi ;V ebx = const void* src+cnt + + cmp edi, esi ; + jbe CopyUp ; + cmp edi, ebx ; if( src < dst < src+cnt ) + jb CopyDown ; downward copy + + CopyUp: + cmp edx, eax ; if( cnt<=3 ) + jbe MiniCopy ; byte by byte copy + + mov ebx, edi ;U + mov ecx, eax ;V + and ebx, eax ;U ebx = (dst&3) + inc ecx ;V + sub ecx, ebx ; ecx = (4-(dst&3)) + and ecx, eax ; ecx = {dst%4 0->0 1->3 2->2 3->1} + sub edx, ecx ; + rep movsb ;N BYTE MOVE (align dst) + + mov ecx, edx ; + shr ecx, 2 ; ecx = [rest bytes]/4 + and edx, eax ; edx = [rest bytes]%4 + rep movsd ;N DWORD MOVE + jmp MiniCopy ; + + CopyDown: + std ; + lea esi,[esi+edx-1] ; + lea edi,[edi+edx-1] ; + + cmp edx, 4 ; if( cnt<=4 ) + jbe MiniCopy ; byte by byte copy + + mov ecx, edi ; + and ecx, eax ; + inc ecx ; ecx = {dst%4 0->1 1->2 2->3 3->4} + sub edx, ecx ; + rep movsb ;N BYTE MOVE (align dst @ dword) + + sub edi, eax ;U + mov ecx, edx ;V + sub esi, eax ;U + shr ecx, 2 ;V ecx = [rest bytes]/4 + and edx, eax ; edx = [rest bytes]%4 + rep movsd ;N DWORD MOVE + add edi, eax ;U + add esi, eax ;V + + MiniCopy: + mov ecx, edx ; + rep movsb ;N BYTE MOVE + + cld ;U + mov eax, [dst] ;V return dst + } + return dst; + } + +#endif + + + +#ifdef USE_ORIGINAL_MEMMAN +//========================================================================= +// 効率的なメモリ管理を目指すアロケータ +//========================================================================= + +// +// メモリブロック +// 「sizバイト * num個」分の領域を一括確保するのが仕事 +// +// 空きブロックには、先頭バイトに [次の空きブロックのindex] を格納。 +// これを用いて、先頭への出し入れのみが可能な単方向リストとして扱う。 +// + +struct ki::MemBlock +{ +public: + void Construct( byte siz, byte num ); + void Destruct(); + void* Alloc( byte siz ); + void DeAlloc( void* ptr, byte siz ); + bool isAvail(); + bool isEmpty( byte num ); + bool hasThisPtr( void* ptr, size_t len ); +private: + byte* buf_; + byte first_, avail_; +}; + +void MemBlock::Construct( byte siz, byte num ) +{ + // 確保 + buf_ = ::new byte[siz*num]; + first_ = 0; + avail_ = num; + + // 連結リスト初期化 + for( byte i=0,*p=buf_; i(ptr); + *blk = first_; + first_ = static_cast((blk-buf_)/siz); + ++avail_; +} + +inline bool MemBlock::isAvail() +{ + // 空きがある? + return (avail_ != 0); +} + +inline bool MemBlock::isEmpty( byte num ) +{ + // 完全に空? + return (avail_ == num); +} + +inline bool MemBlock::hasThisPtr( void* ptr, size_t len ) +{ + // このブロックのポインタ? + return ( buf_<=ptr && ptr( Min( npb, 255 ) ); + fixedSize_ = siz; + + // 一個だけブロック作成 + blocks_[0].Construct( fixedSize_, numPerBlock_ ); + + a.Release(); + lastA_ = 0; + lastDA_ = 0; + blockNum_ = 1; + blockNumReserved_ = 4; +} + +void MemoryManager::FixedSizeMemBlockPool::Destruct() +{ + // 各ブロックを解放 + for( int i=0; i=0 ) + if( blocks_[u].hasThisPtr(ptr,ln) ) + { + lastDA_ = u; + break; + } + else if( u==mx ) + { + u = -1; + } + else + { + ++u; + } + if( d>=0 ) + if( blocks_[d].hasThisPtr(ptr,ln) ) + { + lastDA_ = d; + break; + } + else + { + --d; + } + } + + // 解放を実行 + blocks_[lastDA_].DeAlloc( ptr, fixedSize_ ); + + // この削除でブロックが完全に空になった場合 + if( blocks_[lastDA_].isEmpty( numPerBlock_ ) ) + { + // しかも一番後ろのブロックでなかったら + int end = blockNum_-1; + if( lastDA_ != end ) + { + // 一番後ろが空だったら解放 + if( blocks_[end].isEmpty( numPerBlock_ ) ) + { + blocks_[end].Destruct(); + --blockNum_; + if( lastA_ > --end ) + lastA_ = end; + } + + // 後ろと交換 + MemBlock tmp( blocks_[lastDA_] ); + blocks_[lastDA_] = blocks_[end]; + blocks_[end] = tmp; + } + } +} + +inline bool MemoryManager::FixedSizeMemBlockPool::isValid() +{ + // 既に使用開始されているか? + return (blockNum_ != 0); +} + + + +//------------------------------------------------------------------------- + +// +// 最上位層 +// 指定サイズにあった FixedSizeMemBlockPool に処理をまわす +// +// lokiの実装では固定サイズアロケータも、必要に応じて +// 動的確保していたが、それは面倒なのでやめました。(^^; +// 最初に64個確保したからと言って、そんなにメモリも喰わないし…。 +// + +MemoryManager* MemoryManager::pUniqueInstance_; + +MemoryManager::MemoryManager() +{ + g_heap = ::GetProcessHeap(); + + // メモリプールをZEROクリア + mem00( pools_, sizeof(pools_) ); + + // 唯一のインスタンスは私です + pUniqueInstance_ = this; +} + +MemoryManager::~MemoryManager() +{ + // 構築済みメモリプールを全て解放 + for( int i=0; i( siz-1 ); + if( i >= SMALL_MAX ) + return ::operator new( siz ); + + // マルチスレッド対応 + AutoLock al(this); + + // このサイズのメモリ確保が初めてなら + // ここでメモリプールを作成する。 + if( !pools_[i].isValid() ) + pools_[i].Construct( static_cast(siz) ); + + // ここで割り当て + return pools_[i].Alloc(); +} + +void MemoryManager::DeAlloc( void* ptr, size_t siz ) +{ +#if defined(SUPERTINY) && defined(_DEBUG) + --allocCounter; +#endif + + // サイズが零か大きすぎるなら + // デフォルトの delete 演算子に任せる + uint i = static_cast( siz-1 ); + if( i >= SMALL_MAX ) + { + ::operator delete( ptr ); + return; // VCで return void が出来ないとは… + } + + // マルチスレッド対応 + AutoLock al(this); + + // ここで解放 + pools_[i].DeAlloc( ptr ); +} + +#else // USE_ORIGNAL_MEMMAN + +MemoryManager* MemoryManager::pUniqueInstance_; + +MemoryManager::MemoryManager() +{ + g_heap = ::GetProcessHeap(); + + // 唯一のインスタンスは私です + pUniqueInstance_ = this; +} + +MemoryManager::~MemoryManager() +{ +#if defined(SUPERTINY) && defined(_DEBUG) + // リーク検出用 + if( allocCounter != 0 ) + ::MessageBox( NULL, TEXT("MemoryLeak!"), NULL, MB_OK ); +#endif +} + +void* MemoryManager::Alloc( size_t siz ) +{ + return ::operator new(siz); +} + +void MemoryManager::DeAlloc( void* ptr, size_t siz ) +{ + ::operator delete(ptr); +} + +#endif ADDED kilib/memory.h Index: kilib/memory.h ================================================================== --- kilib/memory.h +++ kilib/memory.h @@ -0,0 +1,142 @@ +#ifndef _KILIB_MEMORY_H_ +#define _KILIB_MEMORY_H_ +#include "types.h" +#include "thread.h" +#ifndef __ccdoc__ +namespace ki { +#endif + + +// W版ではHeapAllocを直接呼び出すバージョンを使う +//#if !defined(_UNICODE) && defined(SUPERTINY) +// #define USE_ORIGINAL_MEMMAN +//#endif + +// 小規模と見なすオブジェクトの最大サイズ +#define SMALL_MAX 64 +// 一度に確保するヒープブロックのサイズ +#define BLOCK_SIZ 4096 +// 内部実装 +struct MemBlock; + + + +//========================================================================= +//@{ @pkg ki.Memory //@} +//@{ +// メモリ割り当て・解放機構 +// +// SUPERTINYオプションを付けてコンパイルすると、標準の +// mallocやfreeを使えなくなるため、HeapAlloc等のAPIを +// 直接呼び出す必要が出てきます。しかし、こいつらを本当に +// 毎回直に呼んでいると、遅い。もうアホかと、バカかと、 +// って勢いで遅い。そこで、主にnewで動的に小規模メモリを +// 確保することに主眼を据えた簡単なアロケータを使うことにしました。 +// +// loki +// ライブラリほぼそのまんまな実装です。 +//@} +//========================================================================= + +class MemoryManager : public EzLockable +{ +public: + + //@{ メモリ割り当て //@} + void* Alloc( size_t siz ); + + //@{ メモリ解放 //@} + void DeAlloc( void* ptr, size_t siz ); + +#ifdef USE_ORIGINAL_MEMMAN +private: + struct FixedSizeMemBlockPool + { + void Construct( byte siz ); + void Destruct(); + void* Alloc(); + void DeAlloc( void* ptr ); + bool isValid(); + private: + MemBlock* blocks_; + int blockNum_; + int blockNumReserved_; + byte fixedSize_; + byte numPerBlock_; + int lastA_; + int lastDA_; + }; + FixedSizeMemBlockPool pools_[ SMALL_MAX ]; +#endif + +private: + + MemoryManager(); + ~MemoryManager(); + +private: + + static MemoryManager* pUniqueInstance_; + +private: + + friend void APIENTRY Startup(); + friend inline MemoryManager& mem(); + NOCOPY(MemoryManager); +}; + + + +//------------------------------------------------------------------------- + +//@{ 唯一のメモリ管理オブジェクトを返す //@} +inline MemoryManager& mem() + { return *MemoryManager::pUniqueInstance_; } + +//@{ ゼロ埋め作業 //@} +inline void mem00( void* ptrv, int siz ) + { BYTE* ptr = (BYTE*)ptrv; + for(;siz>3;siz-=4,ptr+=4) *(DWORD*)ptr = 0x00000000; + for(;siz;--siz,++ptr) *ptr = 0x00; } + +//@{ FF埋め作業 //@} +inline void memFF( void* ptrv, int siz ) + { BYTE* ptr = (BYTE*)ptrv; + for(;siz>3;siz-=4,ptr+=4) *(DWORD*)ptr = 0xffffffff; + for(;siz;--siz,++ptr) *ptr = 0xff; } + + + +//========================================================================= +//@{ +// 標準基底クラス +// +// JavaのObject や MFCのCObject みたいに使う…わけではなく、 +// 単にここから派生すると自動で operator new/delete が高速版に +// なるので便利だよ、という使い方のための基底クラスです。 +//@} +//========================================================================= + +class Object +{ +#ifdef USE_ORIGINAL_MEMMAN +public: + + static void* operator new( size_t siz ) + { return mem().Alloc( siz ); } + + static void operator delete( void* ptr, size_t siz ) + { mem().DeAlloc( ptr, siz ); } +#endif + +protected: + virtual ~Object() + {} +}; + + + +//========================================================================= + +} // namespace ki +#endif // _KILIB_MEMORY_H_ ADDED kilib/path.cpp Index: kilib/path.cpp ================================================================== --- kilib/path.cpp +++ kilib/path.cpp @@ -0,0 +1,188 @@ +#include "stdafx.h" +#include "path.h" +using namespace ki; + + + +//========================================================================= + +Path& Path::BeSpecialPath( int nPATH, bool bs ) +{ + TCHAR* buf = AllocMem( MAX_PATH+1 ); + + switch( nPATH ) + { + case Win: ::GetWindowsDirectory( buf, MAX_PATH ); break; + case Sys: ::GetSystemDirectory( buf, MAX_PATH ); break; + case Tmp: ::GetTempPath( MAX_PATH, buf ); break; + case Cur: ::GetCurrentDirectory( MAX_PATH, buf ); break; + case Exe: + case ExeName: ::GetModuleFileName( NULL, buf, MAX_PATH ); break; + default: + *buf = TEXT('\0'); + { + LPITEMIDLIST il; + if( NOERROR==::SHGetSpecialFolderLocation( NULL, nPATH, &il ) ) + { + ::SHGetPathFromIDList( il, buf ); + ::CoTaskMemFree( il ); + } + } + } + + UnlockMem(); + if( nPATH != ExeName ) + { + if( nPATH == Exe ) + BeDirOnly(); + BeBackSlash( bs ); + } + return *this; +} + +Path& Path::BeBackSlash( bool add ) +{ + // 最後の一文字を得る + const TCHAR* last=c_str(); + for( const TCHAR *p=last; *p!=TEXT('\0'); p=next(p) ) + last = p; + + if( *last==TEXT('\\') || *last==TEXT('/') ) + { + if( !add ) + TrimRight( 1 ); + } + else if( add && *last!=TEXT('\0') ) + { + *this += TEXT('\\'); + } + + return *this; +} + +Path& Path::BeDirOnly() +{ + const TCHAR* lastslash = c_str()-1; + for( const TCHAR* p=lastslash+1; *p!=TEXT('\0'); p=next(p) ) + if( *p==TEXT('\\') || *p==TEXT('/') ) + lastslash = p; + + TrimRight( len() - ulong((lastslash+1)-c_str()) ); + return *this; +} + +Path& Path::BeDriveOnly() +{ + if( len() > 2 ) + { + const TCHAR* p = c_str()+2; + for( ; *p!=TEXT('\0'); p=next(p) ) + if( *p==TEXT('\\') || *p==TEXT('/') ) + break; + if( *p!=TEXT('\0') ) + TrimRight( len() - ulong((p+1)-c_str()) ); + } + return *this; +} + +Path& Path::BeShortStyle() +{ + TCHAR* buf = ReallocMem( len()+1 ); + ::GetShortPathName( buf, buf, len()+1 ); + UnlockMem(); + return *this; +} + +Path& Path::BeShortLongStyle() +{ + WIN32_FIND_DATA fd; + HANDLE h = ::FindFirstFile( c_str(), &fd ); + if( h == INVALID_HANDLE_VALUE ) + return *this; + ::FindClose( h ); + + TCHAR t; + TCHAR* buf = ReallocMem( MAX_PATH*2 ); + TCHAR* nam = const_cast(name(buf)); + + if( nam != buf ) + t = *(nam-1), *(nam-1) = TEXT('\0'); + ::lstrcpy( nam, fd.cFileName ); + if( nam != buf ) + *(nam-1) = t; + + UnlockMem(); + return *this; +} + +String Path::CompactIfPossible( int Mx ) +{ + HMODULE hshl = ::LoadLibrary( TEXT("shlwapi.dll") ); + if( !hshl ) return *this; + + typedef BOOL (STDAPICALLTYPE *PCPE_t)( LPTSTR, LPCTSTR, UINT, DWORD ); +#ifdef _UNICODE + PCPE_t MyPathCompactPathEx = (PCPE_t)::GetProcAddress( hshl, "PathCompactPathExW" ); +#else + PCPE_t MyPathCompactPathEx = (PCPE_t)::GetProcAddress( hshl, "PathCompactPathExA" ); +#endif + if( !MyPathCompactPathEx ) return *this; + + TCHAR* buf = new TCHAR[Mx+2]; + MyPathCompactPathEx( buf, c_str(), Mx+1, 0 ); + ::FreeLibrary( hshl ); + + String ans = buf; + delete [] buf; + return ans; +} + +const TCHAR* Path::name( const TCHAR* str ) +{ + const TCHAR* ans = str - 1; + for( const TCHAR* p=str; *p!=TEXT('\0'); p=next(p) ) + if( *p==TEXT('\\') || *p==TEXT('/') ) + ans = p; + return (ans+1); +} + +const TCHAR* Path::ext( const TCHAR* str ) +{ + const TCHAR *ans = NULL, *p; + for( p=name(str); *p!=TEXT('\0'); p=next(p) ) + if( *p==TEXT('.') ) + ans = p; + return ans ? (ans+1) : p; +} + +const TCHAR* Path::ext_all( const TCHAR* str ) +{ + const TCHAR* p; + for( p=name(str); *p!=TEXT('\0'); p=next(p) ) + if( *p==TEXT('.') ) + return (p+1); + return p; +} + +Path Path::body() const +{ + const TCHAR* nm = name(); + const TCHAR* ex = ext() - 1; + TCHAR t = *ex; + *const_cast(ex) = TEXT('\0'); + Path ans = nm; + *const_cast(ex) = t; + return ans; +} + +Path Path::body_all() const +{ + const TCHAR* nm = name(); + const TCHAR* ex = ext_all() - 1; + TCHAR t = *ex; + *const_cast(ex) = TEXT('\0'); + Path ans = nm; + *const_cast(ex) = t; + return ans; +} + ADDED kilib/path.h Index: kilib/path.h ================================================================== --- kilib/path.h +++ kilib/path.h @@ -0,0 +1,148 @@ +#ifndef _KILIB_PATH_H_ +#define _KILIB_PATH_H_ +#include "types.h" +#include "string.h" +#ifndef __ccdoc__ +namespace ki { +#endif + + + +//========================================================================= +//@{ @pkg ki.StdLib //@} +//@{ +// ファイル名処理 +// +// 基本的には文字列ですが、特にファイル名として扱うときに便利な +// クラスです。名前部分のみ取り出しとか拡張子のみ取り出しとか、 +// 属性を取ってくるとか色々。 +//@} +//========================================================================= + +class Path : public String +{ +public: + + Path() {} + + //@{ 別のPathのコピー //@} + Path( const Path& s ) : String( s ){} + Path( const String& s ) : String( s ){} + Path( const TCHAR* s, long siz=-1 ) : String( s, siz ){} + + //@{ 単純代入 //@} + Path& operator=( const Path& s ) + { return static_cast(String::operator=(s)); } + Path& operator=( const String& s ) + { return static_cast(String::operator=(s)); } + Path& operator=( const TCHAR* s ) + { return static_cast(String::operator=(s)); } + + //@{ 加算代入 //@} + Path& operator+=( const Path& s ) + { return static_cast(String::operator+=(s)); } + Path& operator+=( const String& s ) + { return static_cast(String::operator+=(s)); } + Path& operator+=( const TCHAR* s ) + { return static_cast(String::operator+=(s)); } + Path& operator+=( TCHAR c ) + { return static_cast(String::operator+=(c)); } + + //@{ 特殊パス取得して初期化 //@} + explicit Path( int nPATH, bool bs=true ) + { BeSpecialPath( nPATH, bs ); } + + //@{ 特殊パス取得 //@} + Path& BeSpecialPath( int nPATH, bool bs=true ); + + //@{ 特殊パス指定用定数 //@} + enum { Win=0x1787, Sys, Tmp, Exe, Cur, ExeName, + Snd=CSIDL_SENDTO, Dsk=CSIDL_DESKTOPDIRECTORY }; + + //@{ 最後にバックスラッシュを入れる(true)/入れない(false) //@} + Path& BeBackSlash( bool add ); + + //@{ ドライブ名ないしルートのみ //@} + Path& BeDriveOnly(); + + //@{ ディレクトリ名のみ //@} + Path& BeDirOnly(); + + //@{ 短いパス名 //@} + Path& BeShortStyle(); + + //@{ ファイル名だけは確実に長く //@} + Path& BeShortLongStyle(); + + //@{ ...とかを入れて短く //@} + String CompactIfPossible(int Mx); + + //@{ ディレクトリ情報以外 //@} + const TCHAR* name() const; + + //@{ 最後の拡張子 //@} + const TCHAR* ext() const; + + //@{ 最初の.以降全部と見なした拡張子 //@} + const TCHAR* ext_all() const; + + //@{ ディレクトリ情報と最後の拡張子を除いた名前部分 //@} + Path body() const; + + //@{ ディレクトリ情報と最初の.以降全部を除いた名前部分 //@} + Path body_all() const; + +public: + + //@{ ファイルかどうか //@} + bool isFile() const; + + //@{ ディレクトリかどうか //@} + bool isDirectory() const; + + //@{ 存在するかどうか。isFile() || isDirectory() //@} + bool exist() const; + + //@{ 読み取り専用かどうか //@} + bool isReadOnly() const; + +public: + + static const TCHAR* name( const TCHAR* str ); + static const TCHAR* ext( const TCHAR* str ); + static const TCHAR* ext_all( const TCHAR* str ); +}; + + + +//------------------------------------------------------------------------- + +inline bool Path::isFile() const + { return 0==(::GetFileAttributes(c_str())&FILE_ATTRIBUTE_DIRECTORY); } + +inline bool Path::isDirectory() const + { DWORD x=::GetFileAttributes(c_str()); + return x!=0xffffffff && (x&FILE_ATTRIBUTE_DIRECTORY)!=0; } + +inline bool Path::exist() const + { return 0xffffffff != ::GetFileAttributes(c_str()); } + +inline bool Path::isReadOnly() const + { DWORD x=::GetFileAttributes(c_str()); + return x!=0xffffffff && (x&FILE_ATTRIBUTE_READONLY)!=0; } + +inline const TCHAR* Path::name() const + { return name(c_str()); } + +inline const TCHAR* Path::ext() const + { return ext(c_str()); } + +inline const TCHAR* Path::ext_all() const + { return ext_all(c_str()); } + + + +//========================================================================= + +} // namespace ki +#endif // _KILIB_PATH_H_ ADDED kilib/registry.cpp Index: kilib/registry.cpp ================================================================== --- kilib/registry.cpp +++ kilib/registry.cpp @@ -0,0 +1,147 @@ +#include "stdafx.h" +#include "registry.h" +using namespace ki; + + + +//========================================================================= + +void IniFile::SetFileName( const TCHAR* ini, bool exepath ) +{ + iniName_ = + (exepath ? iniName_.BeSpecialPath(Path::Exe) : Path(TEXT(""))); + if( ini != NULL ) + iniName_ += ini; + else + iniName_ += (Path(Path::ExeName).body()+=TEXT(".ini")); +} + +void IniFile::SetSectionAsUserName() +{ + TCHAR usr[256]; + DWORD siz = countof(usr); + if( !::GetUserName( usr, &siz ) ) + ::lstrcpy( usr, TEXT("Default") ); + SetSection( usr ); +} + +bool IniFile::HasSectionEnabled( const TCHAR* section ) const +{ + return (0!=::GetPrivateProfileInt( + section, TEXT("Enable"), 0, iniName_.c_str() )); +} + +int IniFile::GetInt ( const TCHAR* key, int defval ) const +{ + return ::GetPrivateProfileInt( + section_.c_str(), key, defval, iniName_.c_str() ); +} + +bool IniFile::GetBool( const TCHAR* key, bool defval ) const +{ + return (0!=::GetPrivateProfileInt( + section_.c_str(), key, defval?1:0, iniName_.c_str() )); +} + +String IniFile::GetStr ( const TCHAR* key, const String& defval ) const +{ + RawString str; + ulong l=256, s; + for(;;) + { + TCHAR* x = str.AllocMem(l); + s = ::GetPrivateProfileString( section_.c_str(), key, + defval.c_str(), x, l, iniName_.c_str() ); + if( s < l-1 ) + break; + l <<= 1; + } + str.UnlockMem(); + return str; +} + +Path IniFile::GetPath( const TCHAR* key, const Path& defval ) const +{ +#ifdef _UNICODE + String s = GetStr( key, defval ); + if( s.len()==0 || s[0]!='#' ) + return s; + + // UTF-decoder + String buf; + for(uint i=0; 4*i+4>12) & 0xf]; + buf += hex[(u>> 8) & 0xf]; + buf += hex[(u>> 4) & 0xf]; + buf += hex[(u>> 0) & 0xf]; + } + return PutStr( key, buf.c_str() ); +#else + return PutStr( key, val.c_str() ); +#endif +} + + ADDED kilib/registry.h Index: kilib/registry.h ================================================================== --- kilib/registry.h +++ kilib/registry.h @@ -0,0 +1,80 @@ +#ifndef _KILIB_REGISTRY_H_ +#define _KILIB_REGISTRY_H_ +#include "types.h" +#include "memory.h" +#include "path.h" +#ifndef __ccdoc__ +namespace ki { +#endif + + + +//========================================================================= +//@{ @pkg ki.WinUtil //@} +//@{ +// INIファイル読み込み +// +//@} +//========================================================================= + +class IniFile : public Object +{ +public: + + //@{ コンストラクタ //@} + IniFile( const TCHAR* ini=NULL, bool exeppath=true ); + + //@{ iniファイル名を設定 //@} + void SetFileName( const TCHAR* ini=NULL, bool exepath=true ); + + //@{ セクション名を設定 //@} + void SetSection( const TCHAR* section ); + + //@{ セクション名をユーザー名に設定 //@} + void SetSectionAsUserName(); + + //@{ ある特定の名前のセクションがあるかどうか? //@} + bool HasSectionEnabled( const TCHAR* section ) const; + + //@{ 整数値読み込み //@} + int GetInt ( const TCHAR* key, int defval ) const; + //@{ 真偽値読み込み //@} + bool GetBool( const TCHAR* key, bool defval ) const; + //@{ 文字列読み込み //@} + String GetStr ( const TCHAR* key, const String& defval ) const; + //@{ パス文字列読み込み //@} + Path GetPath ( const TCHAR* key, const Path& defval ) const; + + //@{ 整数値書き込み //@} + bool PutInt ( const TCHAR* key, int val ); + //@{ 真偽値書き込み //@} + bool PutBool( const TCHAR* key, bool val ); + //@{ 文字列書き込み //@} + bool PutStr ( const TCHAR* key, const TCHAR* val ); + //@{ パス書き込み //@} + bool PutPath( const TCHAR* key, const Path& val ); + +private: + + Path iniName_; + String section_; + char m_StrBuf[256]; +}; + + + +//------------------------------------------------------------------------- + +inline IniFile::IniFile( const TCHAR* ini, bool exepath ) + { SetFileName( ini, exepath ); } + +inline void IniFile::SetSection( const TCHAR* section ) + { section_ = section; } + + + + +//========================================================================= + +} // namespace ki +#endif // _KILIB_REGISTRY_H_ ADDED kilib/stdafx.cpp Index: kilib/stdafx.cpp ================================================================== --- kilib/stdafx.cpp +++ kilib/stdafx.cpp @@ -0,0 +1,1 @@ +#include "stdafx.h" ADDED kilib/stdafx.h Index: kilib/stdafx.h ================================================================== --- kilib/stdafx.h +++ kilib/stdafx.h @@ -0,0 +1,69 @@ +#ifndef _KILIB_STDAFX_H_ +#define _KILIB_STDAFX_H_ + +#undef WINVER +#define WINVER 0x0400 +#undef _WIN32_IE +#define _WIN32_IE 0x0200 +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0400 + +#define OEMRESOURCE +#define NOMINMAX +#ifdef SUPERTINY + #define memset memset_default +#endif + +#include +#include +#include +#include +#include +// dimm.hが無くてエラーになる場合、プロジェクトの設定でUSEGLOBALIMEの定義を +// 削除するか、最新の Platform SDK を導入すればビルドが通るようになります。 +#ifdef USEGLOBALIME +#include +#endif + +#ifdef SUPERTINY + #undef memset +#endif + +#ifndef WM_MOUSEWHEEL +#define WM_MOUSEWHEEL 0x020A +#endif +#ifndef OFN_ENABLESIZING +#define OFN_ENABLESIZING 0x00800000 +#endif +#ifndef IMR_RECONVERTSTRING +#define IMR_RECONVERTSTRING 0x0004 +#define IMR_CONFIRMRECONVERTSTRING 0x0005 +typedef struct tagRECONVERTSTRING { + DWORD dwSize; + DWORD dwVersion; + DWORD dwStrLen; + DWORD dwStrOffset; + DWORD dwCompStrLen; + DWORD dwCompStrOffset; + DWORD dwTargetStrLen; + DWORD dwTargetStrOffset; +} RECONVERTSTRING, *PRECONVERTSTRING, NEAR *NPRECONVERTSTRING, FAR *LPRECONVERTSTRING; +#endif +#ifndef WM_IME_REQUEST +#define WM_IME_REQUEST 0x0288 +#endif + +#ifdef _MSC_VER +#pragma warning( disable: 4355 ) +#endif + +#ifdef __DMC__ + #define SetWindowLongPtr SetWindowLong + #define GetWindowLongPtr GetWindowLong + #define UINT_PTR UINT + #define LONG_PTR LONG + #define GWLP_WNDPROC GWL_WNDPROC + #define GWLP_USERDATA GWL_USERDATA +#endif + +#endif // _KILIB_STDAFX_H_ ADDED kilib/string.cpp Index: kilib/string.cpp ================================================================== --- kilib/string.cpp +++ kilib/string.cpp @@ -0,0 +1,238 @@ +#include "stdafx.h" +#include "app.h" +#include "memory.h" +#include "string.h" +using namespace ki; + + + +//========================================================================= + +String::StringData* String::nullData_; +char String::lb_[256]; + +void String::LibInit() +{ + static int nullstr_image[4]; + nullstr_image[0] = 1; + nullstr_image[1] = 1; + nullstr_image[2] = 4; + nullData_ = reinterpret_cast(nullstr_image); + +#if !defined(_UNICODE) && defined(_MBCS) + for( int c=0; c<256; ++c ) + lb_[c] = (::IsDBCSLeadByte(c) ? 2 : 1); +#endif +} + + + +//------------------------------------------------------------------------- + +String::String( const TCHAR* s, long len ) +{ + // 長さ指定が無い場合は計算 + if( len==-1 ) + len = ::lstrlen(s); + + if( len==0 ) + { + // 0文字用の特殊バッファ + SetData( null() ); + } + else + { + // 新規バッファ作成 + data_ = static_cast + (mem().Alloc( sizeof(StringData)+(len+1)*sizeof(TCHAR) )); + data_->ref = 1; + data_->len = len+1; + data_->alen = len+1; + memmove( data_+1, s, (len+1)*sizeof(TCHAR) ); + } +} + +inline void String::ReleaseData() +{ + if( --data_->ref <= 0 ) + mem().DeAlloc( + data_, sizeof(StringData)+sizeof(TCHAR)*data_->alen ); +} + +String::~String() +{ + ReleaseData(); +} + +TCHAR* String::ReallocMem( ulong minimum=0 ) +{ + return AllocMemHelper( minimum, c_str(), len()+1 ); +} + +String& String::SetString( const TCHAR* str, ulong siz ) +{ + TCHAR* buf = AllocMem( siz+1 ); + + memmove( buf, str, siz*sizeof(TCHAR) ); + buf[siz] = TEXT('\0'); + + UnlockMem( siz ); + return *this; +} + +String& String::CatString( const TCHAR* str, ulong siz ) +{ + const int plen = len(); + TCHAR* buf = ReallocMem( plen + siz + 1 ); + + memmove( buf+plen, str, siz*sizeof(TCHAR) ); + buf[plen+siz] = TEXT('\0'); + + UnlockMem( plen+siz ); + return *this; +} + +TCHAR* String::AllocMemHelper( ulong minimum, const TCHAR* str, ulong siz ) +{ + if( data_->ref > 1 || data_->alen < minimum ) + { + minimum = Max( minimum, data_->alen ); + + StringData* pNew = static_cast + (mem().Alloc( sizeof(StringData)+minimum*sizeof(TCHAR) )); + pNew->ref = 1; + pNew->alen = minimum; + pNew->len = siz; + memmove( pNew->buf(), str, siz*sizeof(TCHAR) ); + + ReleaseData(); + data_ = pNew; + } + + return data_->buf(); +} + +String& String::operator = ( const String& obj ) +{ + if( data() != obj.data() ) + { + ReleaseData(); + SetData( obj.data() ); + } + return *this; +} + +#ifdef _UNICODE +String& String::operator = ( const char* s ) +{ + long len = ::MultiByteToWideChar( CP_ACP, 0, s, -1, NULL, 0 ); + ::MultiByteToWideChar( CP_ACP, 0, s, -1, AllocMem(len+1), len+1 ); +#else +String& String::operator = ( const wchar_t* s ) +{ + long len = ::WideCharToMultiByte(CP_ACP,0,s,-1,NULL,0,NULL,NULL); + ::WideCharToMultiByte(CP_ACP,0,s,-1,AllocMem(len+1),len+1,NULL,NULL); +#endif + UnlockMem( len ); + return *this; +} + +String& String::Load( UINT rsrcID ) +{ + const int step=256; + + // 256バイトの固定長バッファへまず読んでみる + TCHAR tmp[step], *buf; + int red = app().LoadString( rsrcID, tmp, countof(tmp) ); + if( countof(tmp) - red > 2 ) + return (*this = tmp); + + // 少しずつ増やして対応してみる + int siz = step; + do + { + siz+= step; + buf = AllocMem( siz ); + red = app().LoadString( rsrcID, buf, siz ); + } while( siz - red <= 2 ); + + buf[red] = TEXT('\0'); + UnlockMem( red ); + return *this; +} + +void String::TrimRight( ulong siz ) +{ + if( siz >= len() ) + { + ReleaseData(); + SetData( null() ); + } + else + { + // 文字列バッファの参照カウントを確実に1にする + ReallocMem(); + + // 指定文字数分削る + data_->len -= siz; + data_->buf()[data_->len-1] = TEXT('\0'); + } +} + +int String::GetInt( const TCHAR* x ) +{ + int n=0; + bool minus = (*x==TEXT('-')); + for( const TCHAR* p=(minus?x+1:x); *p!=TEXT('\0'); p=next(p) ) + { + if( *p=0; --i ) + { + tmp[i] = TEXT('0') + n%10; + n /= 10; + if( n==0 ) + break; + } + + if( minus ) + tmp[--i] = TEXT('-'); + + *this = tmp+i; + } + return *this; +} + +const wchar_t* String::ConvToWChar() const +{ +#ifdef _UNICODE + return c_str(); +#else + int ln = ::MultiByteToWideChar( CP_ACP, 0, c_str(), -1 , 0, 0 ); + wchar_t* p = new wchar_t[ln+1]; + ::MultiByteToWideChar( CP_ACP, 0, c_str(), -1 , p, ln+1 ); + return p; +#endif +} + ADDED kilib/string.h Index: kilib/string.h ================================================================== --- kilib/string.h +++ kilib/string.h @@ -0,0 +1,309 @@ +#ifndef _KILIB_STRING_H_ +#define _KILIB_STRING_H_ +#include "types.h" +#include "memory.h" +#include "ktlaptr.h" +#ifndef __ccdoc__ +namespace ki { +#endif +#ifdef _UNICODE + #define XTCHAR char +#else + #define XTCHAR wchar_t +#endif + + + +//========================================================================= +//@{ @pkg ki.StdLib //@} +//@{ +// 文字列処理 +// +// かなりMFCのCStringをパクってます。とりあえず operator= による +// 単純代入にはほとんどコストがかからないようにしました。SubStr()の +// 時もコピーしないようにしようかとも思ったんですが、そこまでは +// 要らないだろうという気もするので…。 +//@} +//========================================================================= + +class String : public Object +{ +public: + + //@{ 空文字列作成 //@} + String(); + ~String(); + + //@{ 別のStringのコピー //@} + String( const String& obj ); + + //@{ 別の文字配列のコピー //@} + String( const TCHAR* str, long siz=-1 ); + + //@{ リソースから作成 //@} + explicit String( UINT rsrcID ); + + //@{ 大文字小文字を区別する比較 //@} + bool operator==( LPCTSTR s ) const; + bool operator==( const String& obj ) const; + + //@{ 大文字小文字を区別しない比較 //@} + bool isSame( LPCTSTR s ) const; + bool isSame( const String& obj ) const; + + //@{ 単純代入 //@} + String& operator=( const String& obj ); + String& operator=( const TCHAR* s ); + String& operator=( const XTCHAR* s ); + + //@{ 加算代入 //@} + String& operator+=( const String& obj ); + String& operator+=( const TCHAR* s ); + String& operator+=( TCHAR c ); + + //@{ リソースロード //@} + String& Load( UINT rsrcID ); + + //@{ 右を削る //@} + void TrimRight( ulong siz ); + + //@{ intから文字列へ変換 //@} + String& SetInt( int n ); + + //@{ 文字列からintへ変換 //@} + int GetInt(); + +public: + + //@{ 文字列バッファを返す //@} + const TCHAR* c_str() const; + + //@{ 長さ //@} + ulong len() const; + + //@{ 要素 //@} + const TCHAR operator[](int n) const; + + //@{ ワイド文字列に変換して返す //@} + const wchar_t* ConvToWChar() const; + + //@{ ConvToWCharの返値バッファの解放 //@} + void FreeWCMem( const wchar_t* wc ) const; + +public: + + //@{ 次の一文字 //@} + static TCHAR* next( TCHAR* p ); + static const TCHAR* next( const TCHAR* p ); + + //@{ 2バイト文字の先頭かどうか? //@} + static bool isLB( TCHAR c ); + + //@{ 文字列からintへ変換 //@} + static int GetInt( const TCHAR* p ); + +protected: + + // 書き込み可能なバッファを、終端含めて最低でもminimum文字分用意する + TCHAR* AllocMem( ulong minimum ); + TCHAR* ReallocMem( ulong minimum ); + + // 書き込み終了後、長さを再設定 + void UnlockMem( long siz=-1 ); + +private: + + struct StringData + { + long ref; // 参照カウンタ + ulong len; // 終端'\0'を含める長さ + ulong alen; // 割り当てられているメモリのサイズ + TCHAR* buf() const // TCHAR buf[alen] + { return reinterpret_cast( + const_cast(this+1) + ); } + }; + +private: + + TCHAR* AllocMemHelper( ulong minimum, const TCHAR* str, ulong siz ); + String& CatString( const TCHAR* str, ulong siz ); + String& SetString( const TCHAR* str, ulong siz ); + void SetData( StringData* d ); + void ReleaseData(); + static StringData* null(); + StringData* data() const; + +private: + + StringData* data_; + static StringData* nullData_; + static char lb_[256]; + +private: + + static void LibInit(); + friend void APIENTRY Startup(); +}; + + + +//------------------------------------------------------------------------- +#ifndef __ccdoc__ + +// 初期化 +inline String::String() + { SetData( null() ); } +// 初期化 +inline String::String( UINT rsrcID ) + { SetData( null() ), Load( rsrcID ); } +// 初期化 +inline String::String( const String& obj ) + { SetData( obj.data() ); } + +// ポインタ計算サポート +#if !defined(_UNICODE) && defined(_MBCS) + inline TCHAR* String::next( TCHAR* p ) + { return p + lb_[*(uchar*)p]; } + inline const TCHAR* String::next( const TCHAR* p ) + { return p + lb_[*(const uchar*)p]; } + inline bool String::isLB( TCHAR c ) + { return lb_[(uchar)c]==2; } +#else // _UNICODE or _SBCS + inline TCHAR* String::next( TCHAR* p ) + { return p + 1; } + inline const TCHAR* String::next( const TCHAR* p ) + { return p + 1; } + inline bool String::isLB( TCHAR c ) + { return false; } +#endif + +// 内部メモリ確保 +inline TCHAR* String::AllocMem( ulong minimum ) + { return AllocMemHelper( minimum, TEXT(""), 1 ); } +// 内部メモリ固定 +inline void String::UnlockMem( long siz ) + { data_->len = 1 + (siz==-1 ? ::lstrlen(c_str()) : siz); } + +// 0文字データ +inline String::StringData* String::null() + { return nullData_; } +// 内部データ構造 +inline String::StringData* String::data() const + { return data_; } +// 初期化 +inline void String::SetData( String::StringData* d ) + { data_=d, data_->ref++; } + +// 属性 +inline const TCHAR* String::c_str() const + { return data_->buf(); } +// 属性 +inline ulong String::len() const + { return data_->len-1; } +// 要素 +inline const TCHAR String::operator[](int n) const + { return data_->buf()[n]; } + +// 比較 +inline bool String::operator==( LPCTSTR s ) const + { return 0==::lstrcmp( c_str(), s ); } +// 比較 +inline bool String::operator==( const String& obj ) const + { return (data_==obj.data_ ? true : operator==( obj.c_str() )); } +// 比較 +inline bool String::isSame( LPCTSTR s ) const + { return 0==::lstrcmpi( c_str(), s ); } +// 比較 +inline bool String::isSame( const String& obj ) const + { return (data_==obj.data_ ? true : operator==( obj.c_str() )); } + +// 要コピー代入 +inline String& String::operator = ( const TCHAR* s ) + { return SetString( s, ::lstrlen(s) ); } +// 合成 +inline String& String::operator += ( const String& obj ) + { return CatString( obj.c_str(), obj.len() ); } +// 合成 +inline String& String::operator += ( const TCHAR* s ) + { return CatString( s, ::lstrlen(s) ); } +// 合成 +inline String& String::operator += ( TCHAR c ) + { return CatString( &c, 1 ); } + +// 変換 +inline int String::GetInt() + { return GetInt( data_->buf() ); } + +//@{ String + String //@} +inline const String operator+( const String& a, const String& b ) + { return String(a) += b; } +//@{ String + TCHAR* //@} +inline const String operator+( const String& a, const TCHAR* b ) + { return String(a) += b; } +//@{ TCHAR* + String //@} +inline const String operator+( const TCHAR* a, const String& b ) + { return String(a) += b; } + +// ConvToWCharの返値バッファの解放 +inline void String::FreeWCMem( const wchar_t* wc ) const +#ifdef _UNICODE + {} +#else // _MBCS or _SBCS + { delete [] const_cast(wc); } +#endif + + + +#endif // __ccdoc__ +#undef XTCHAR +//========================================================================= +//@{ +// 文字列処理+α +// +// Stringクラス内のバッファ確保関数を呼べるようにした版Stringです。 +//@} +//========================================================================= + +struct RawString : public String +{ + TCHAR* AllocMem( ulong m ) { return String::AllocMem(m); } + void UnlockMem() { String::UnlockMem(); } +}; + +} // namespace ki + + +//========================================================================= +//@{ +// 文字列処理+α2 +// +// Wide文字版関数を自前で +//@} +//========================================================================= + +#ifdef _UNICODE + #define my_lstrlenW ::lstrlenW + #define my_lstrcpyW ::lstrcpyW +#else + inline static + unicode* my_lstrcpyW( unicode* const d, const unicode* s ) + { + for(unicode* n=d; *n++=*s++;); + return d; + } + + inline static + int my_lstrlenW( const unicode* const d ) + { + const unicode* n; + for(n=d; *n; ++n); + return static_cast(n-d); + } +#endif + + + +//========================================================================= + +#endif // _KILIB_STRING_H_ ADDED kilib/textfile.cpp Index: kilib/textfile.cpp ================================================================== --- kilib/textfile.cpp +++ kilib/textfile.cpp @@ -0,0 +1,1392 @@ +#include "stdafx.h" +#include "app.h" +#include "textfile.h" +#include "ktlarray.h" +using namespace ki; + + + +//========================================================================= +// テキストファイル読み出し共通インターフェイス +//========================================================================= + +struct ki::TextFileRPimpl : public Object +{ + inline TextFileRPimpl() + : state(EOL) {} + + virtual size_t ReadLine( unicode* buf, ulong siz ) + = 0; + + enum { EOF=0, EOL=1, EOB=2 } state; +}; + + + +//------------------------------------------------------------------------- +// Unicode系用のベースクラス +// UTF-8以外はそんなに出会わないだろうから遅くてもよしとする。 +//------------------------------------------------------------------------- + +struct rBasicUTF : public ki::TextFileRPimpl +{ + virtual unicode PeekC() = 0; + virtual unicode GetC() {unicode ch=PeekC(); Skip(); return ch;} + virtual void Skip() = 0; + virtual bool Eof() = 0; + + size_t ReadLine( unicode* buf, ulong siz ) + { + state = EOF; + + // 改行が出るまで読む + unicode *w=buf, *e=buf+siz; + while( !Eof() ) + { + *w = GetC(); + if( *w==L'\r' || *w==L'\n' ) + { + state = EOL; + break; + } + else if( *w!=0xfeff && ++w==e ) + { + state = EOB; + break; + } + } + + // 改行コードスキップ処理 + if( state == EOL ) + if( *w==L'\r' && !Eof() && PeekC()==L'\n' ) + Skip(); + + // 読んだ文字数 + return w-buf; + } +}; + + + +//------------------------------------------------------------------------- +// UCS2ベタ/UCS4ベタ。それぞれUTF16, UTF32の代わりとして使う。 +// ついでに同じtemplateで、ISO-8859-1も処理してしまう。^^; +//------------------------------------------------------------------------- + +template +struct rUCS : public rBasicUTF +{ + rUCS( const uchar* b, ulong s, bool bigendian ) + : fb( reinterpret_cast(b) ) + , fe( reinterpret_cast(b+(s/sizeof(T))*sizeof(T)) ) + , be( bigendian ) {} + + const T *fb, *fe; + const bool be; + + // エンディアン変換 + inline byte swap( byte val ) { return val; } + inline dbyte swap( dbyte val ) { return (val<<8) |(val>>8); } + inline qbyte swap( qbyte val ) { return ((val>>24)&0xff | + (val>>8)&0xff00 | + (val<<8)&0xff0000| + (val<<24)); } + + virtual void Skip() { ++fb; } + virtual bool Eof() { return fb==fe; } + virtual unicode PeekC() { return (unicode)(be ? swap(*fb) : *fb); } +}; + +typedef rUCS< byte> rWest; +typedef rUCS rUtf16; + +// UTF-32読み込み +struct rUtf32 : public rUCS +{ + rUtf32( const uchar* b, ulong s, bool bigendian ) + : rUCS(b,s,bigendian) + , state(0) {} + + int state; + qbyte curChar() { return be ? swap(*fb) : *fb; } + bool inBMP(qbyte c) { return c<0x10000; } + + virtual unicode PeekC() + { + qbyte c = curChar(); + if( inBMP(c) ) + return (unicode)c; + return (unicode)(state==0 ? 0xD800 + (((c-0x10000) >> 10)&0x3ff) + : 0xDC00 + ( (c-0x10000) &0x3ff)); + } + + virtual void Skip() + { + if( inBMP(curChar()) ) + ++fb; + else if( state==0 ) + state=1; + else + ++fb, state=0; + } +}; + + + +//------------------------------------------------------------------------- +// UTF-5 +// 0- F : 1bbbb +// 10- FF : 1bbbb 0bbbb +// 100-FFF : 1bbbb 0bbbb 0bbbb +// というように、16進での一桁を一文字で表していくフォーマット。 +// 各 0bbbb は '0', '1', ... '9', 'A', ... 'F' +// 各 1bbbb は 'G', 'H', ... 'P', 'Q', ... 'V' の字で表現。 +//------------------------------------------------------------------------- + +struct rUtf5 : public rBasicUTF +{ + rUtf5( const uchar* b, ulong s ) + : fb( b ) + , fe( b+s ) {} + + const uchar *fb, *fe; + + // 16進文字から整数値へ変換 + inline byte conv( uchar x ) + { + if( '0'<=x && x<='9' ) return x-'0'; + else return x-'A'+0x0A; + } + + void Skip() { do ++fb; while( fb>4); + case 5: buf[1]|=(u7c[fb[4]]<< 2); + case 4: buf[1]|=(u7c[fb[3]]<< 8); + case 3: buf[1]|=(u7c[fb[2]]<<14), buf[0]|=(u7c[fb[2]]>>2); + case 2: buf[0]|=(u7c[fb[1]]<< 4); + case 1: buf[0]|=(u7c[fb[0]]<<10); + unicode t; + rest = 1; + if( N==8 ) + rest=3, t=buf[0], buf[0]=buf[2], buf[2]=t; + else if( N>=6 ) + rest=2, t=buf[0], buf[0]=buf[1], buf[1]=t; + } + + // 使った分進む + if( N( p+GetMaskIndex(uchar(*p)) ); + } + + // Win95対策。 + // http://support.microsoft.com/default.aspx?scid=%2Fisapi%2Fgomscom%2Easp%3Ftarget%3D%2Fjapan%2Fsupport%2Fkb%2Farticles%2Fjp175%2F3%2F92%2Easp&LN=JA + // MSDNにはWin95以降でサポートと書いてあるのにCP_UTF8は + // 使えないらしいので、自前の変換関数で。 + typedef int (WINAPI * uConvFunc)(UINT,DWORD,const char*,int,wchar_t*,int); + static int WINAPI Utf8ToWideChar( UINT, DWORD, const char* sb, int ss, wchar_t* wb, int ws ) + { + const uchar *p = reinterpret_cast(sb); + const uchar *e = reinterpret_cast(sb+ss); + wchar_t *w = wb; // バッファサイズチェック無し(仕様) + + for( int t; p>10)&0x3ff)), + *w = (wchar_t)(0xDC00 + (((qch-0x10000) )&0x3ff)); + } + return int(w-wb); + } +} + +struct rMBCS : public TextFileRPimpl +{ + // ファイルポインタ&コードページ + const char* fb; + const char* fe; + const int cp; + uNextFunc next; + uConvFunc conv; + + // 初期設定 + rMBCS( const uchar* b, ulong s, int c ) + : fb( reinterpret_cast(b) ) + , fe( reinterpret_cast(b+s) ) + , cp( c==UTF8 ? UTF8N : c ) + , next( cp==UTF8N ? CharNextUtf8 : CharNextExA ) + , conv( cp==UTF8N && app().isWin95() + ? Utf8ToWideChar : MultiByteToWideChar ) + { + if( cp==UTF8N && fe-fb>=3 + && b[0]==0xef && b[1]==0xbb && b[2]==0xbf ) + fb += 3; // BOMスキップ + } + + size_t ReadLine( unicode* buf, ulong siz ) + { + // バッファの終端か、ファイルの終端の近い方まで読み込む + const char *p, *end = Min( fb+siz/2, fe ); + state = (end==fe ? EOF : EOB); + + // 改行が出るまで進む + for( p=fb; p> +// 1B 28 42 : G0へ ASCII +// 1B 28 4A : G0へ JIS X 0201 ローマ字 (のかわりにASCII) +// 1B 29 4A : G1へ JIS X 0201 ローマ字 (のかわりにASCII) +// 1B 2A 4A : G2へ JIS X 0201 ローマ字 (のかわりにASCII) +// 1B 3B 4A : G3へ JIS X 0201 ローマ字 (のかわりにASCII) +// 1B 2E 41 : G2へ ISO-8859-1 +// <> +// 1B 28 49 : G0へ JIS X 0201 カナ +// 1B 29 49 : G1へ JIS X 0201 カナ +// 1B 2A 49 : G2へ JIS X 0201 カナ +// 1B 2B 49 : G3へ JIS X 0201 カナ +// 1B 24 40 : G0へ JIS X 0208(1978) +// 1B 24 42 : G0へ JIS X 0208(1983) (年度は区別しない) +// <> +// 1B 24 41 : G0へ GB 2312 +// 1B 24 29 41 : G1へ GB 2312 +// 1B 24 2A 41 : G2へ GB 2312 +// 1B 24 2B 41 : G3へ GB 2312 +// <> +// 1B 24 28 43 : G0へ KS X 1001 +// 1B 24 29 43 : G1へ KS X 1001 +// 1B 24 2A 43 : G2へ KS X 1001 +// 1B 24 2B 43 : G3へ KS X 1001 +// +// 各面に呼び出した文字集合は、 +// GL (0x21〜0xfe) GR (0xa0〜0xff) +// のどちらかへマップすることで、実際のバイト値となる。 +// マップ命令となるバイト列は、次の通り +// +// 0F : GL へG0を呼び出し +// 0E : GL へG1を呼び出し +// 1B 7E : GR へG1を呼び出し +// 8E : GL/GR両方 へG2を一瞬だけ呼び出し。1B 4E も同義 +// 8F : GL/GR両方 へG3を一瞬だけ呼び出し。1B 4F も同義 +// +//------------------------------------------------------------------------- + +enum CodeSet { ASCII, LATIN, KANA, JIS, KSX, GB }; + +struct rIso2022 : public TextFileRPimpl +{ + // Helper: JIS X 0208 => SJIS + void jis2sjis( uchar k, uchar t, char* s ) + { + if(k>=0x3f) s[0] = (char)(((k+1)>>1)+0xc0); + else s[0] = (char)(((k+1)>>1)+0x80); + if( k&1 ) s[1] = (char)((t>>6) ? t+0x40 : t+0x3f); + else s[1] = (char)(t+0x9e); + } + + // ファイルポインタ + const uchar* fb; + const uchar* fe; + bool fixed; // ESCによる切り替えを行わないならtrue + bool mode_hz; // HZの場合。 + + // 作業変数 + CodeSet *GL, *GR, G[4]; + int gWhat; // 次の字は 1:GL/GR 2:G2 3:G3 で出力 + ulong len; + + // 初期化 + rIso2022( const uchar* b, ulong s, bool f, bool hz, + CodeSet g0, CodeSet g1, CodeSet g2=ASCII, CodeSet g3=ASCII ) + : fb( b ) + , fe( b+s ) + , fixed( f ) + , mode_hz( hz ) + , GL( &G[0] ) + , GR( &G[1] ) + , gWhat( 1 ) + { + G[0]=g0, G[1]=g1, G[2]=g2, G[3]=g3; + } + + void DoSwitching( const uchar*& p ) + { + if( fixed ) + { + if( p[0]==0x24 && p[1]!=0x40 && p[1]!=0x41 && p[1]!=0x42 + && p+2 < fe && (p[2]==0x41 || p[2]==0x43) ) + ++p; + } + else + { + if( p[1]==0x4A ) + G[ (p[0]-0x28)%4 ] = ASCII; // 1B [28-2B] 4A + else if( p[1]==0x49 ) + G[ (p[0]-0x28)%4 ] = KANA; // 1B [28-2B] 49 + else if( *reinterpret_cast(p)==0x4228 ) + G[ 0 ] = ASCII; // 1B 28 42 + else if( *reinterpret_cast(p)==0x412E ) + G[ 2 ] = LATIN; // 1B 2E 41 + else if( p[0]==0x24 ) + if( p[1]==0x40 || p[1]==0x42 ) + G[ 0 ] = JIS; // 1B 24 [40|42] + else if( p[1]==0x41 ) + G[ 0 ] = GB; // 1B 24 41 + else if( p+2 < fe ) + if( p[2]==0x41 ) + G[ ((*++p)-0x28)%4 ] = GB; // 1B 24 [28-2B] 41 + else if( p[2]==0x43 ) + G[ ((*++p)-0x28)%4 ] = KSX; // 1B 24 [28-2B] 43 + } + ++p; + } + + void DoOutput( unicode*& buf, const uchar*& p ) + { + // 文字集合取り出し + CodeSet cs = + (gWhat==2 ? G[2] : + (gWhat==3 ? G[3] : + (*p&0x80 ? *GR : *GL))); + + char c[2]; + ulong wt=1; + switch( cs ) + { + case ASCII: + *buf = (*p)&0x7f; + break; + case LATIN: + *buf = (*p)|0x80; + break; + case KANA: + c[0] = (*p)|0x80; + wt = ::MultiByteToWideChar( + 932, MB_PRECOMPOSED, c, 1, buf, 2 ); + break; + case GB: + case KSX: + c[0] = (* p)|0x80; + c[1] = (*++p)|0x80; + wt = ::MultiByteToWideChar( + (cs==GB?936:949), MB_PRECOMPOSED, c, 2, buf, 2 ); + break; + case JIS: + jis2sjis( (p[0]&0x7f)-0x20, (p[1]&0x7f)-0x20, c ); + ++p; + wt = ::MultiByteToWideChar( + 932, MB_PRECOMPOSED, c, 2, buf, 2 ); + break; + } + buf+=wt; + len+=wt; + } + + size_t ReadLine( unicode* buf, ulong siz ) + { + len=0; + + // バッファの終端か、ファイルの終端の近い方まで読み込む + const uchar *p, *end = Min( fb+siz/2, fe ); + state = (end==fe ? EOF : EOB); + + // 改行が出るまで進む + for( p=fb; pReadLine( buf, siz ); +} + +int TextFileR::state() const +{ + return impl_->state; +} + +void TextFileR::Close() +{ + fp_.Close(); +} + +bool TextFileR::Open( const TCHAR* fname ) +{ + // ファイルを開く + if( !fp_.Open(fname) ) + return false; + const uchar* buf = fp_.base(); + const ulong siz = fp_.size(); + + // 必要なら自動判定 + cs_ = AutoDetection( cs_, buf, Min(siz,16<<10) ); // 先頭16KB + + // 対応するデコーダを作成 + switch( cs_ ) + { + case Western: impl_ = new rWest(buf,siz,true); break; + case UTF16b: + case UTF16BE: impl_ = new rUtf16(buf,siz,true); break; + case UTF16l: + case UTF16LE: impl_ = new rUtf16(buf,siz,false); break; + case UTF32b: + case UTF32BE: impl_ = new rUtf32(buf,siz,true); break; + case UTF32l: + case UTF32LE: impl_ = new rUtf32(buf,siz,false); break; + case UTF5: impl_ = new rUtf5(buf,siz); break; + case UTF7: impl_ = new rUtf7(buf,siz); break; + case EucJP: impl_ = new rIso2022(buf,siz,true,false,ASCII,JIS,KANA); break; + case IsoJP: impl_ = new rIso2022(buf,siz,false,false,ASCII,KANA); break; + case IsoKR: impl_ = new rIso2022(buf,siz,true,false,ASCII,KSX); break; + case IsoCN: impl_ = new rIso2022(buf,siz,true,false,ASCII,GB); break; + case HZ: impl_ = new rIso2022(buf,siz,true,true, ASCII,GB); break; + default: impl_ = new rMBCS(buf,siz,cs_); break; + } + + return true; +} + +int TextFileR::AutoDetection( int cs, const uchar* ptr, ulong siz ) +{ +//-- まず、文字の出現回数の統計を取る + + int freq[256]; + bool bit8 = false; + mem00( freq, sizeof(freq) ); + for( ulong i=0; i= 0x80 ) + bit8 = true; + ++freq[ ptr[i] ]; + } + +//-- 改行コード決定 (UTF16/32/7のとき問題あり。UTF5に至っては判定不可…) + + if( freq['\r'] > freq['\n']*2 ) lb_ = CR; + else if( freq['\n'] > freq['\r']*2 ) lb_ = LF; + else lb_ = CRLF; + nolbFound_ = freq['\r']==0 && freq['\n']==0; + +//-- デフォルトコード + + int defCs = ::GetACP(); + +//-- 小さすぎる場合はここで終了 + + if( siz <= 4 ) + return cs==AutoDetect ? defCs : cs; + +//-- 明示指定がある場合はここで終了 + + ulong bom4 = (ptr[0]<<24) + (ptr[1]<<16) + (ptr[2]<<8) + (ptr[3]); + ulong bom2 = (ptr[0]<<8) + (ptr[1]); + + if( cs==UTF8 || cs==UTF8N ) + cs = (bom4>>8==0xefbbbf ? UTF8 : UTF8N); + else if( cs==UTF32b || cs==UTF32BE ) + cs = (bom4==0x0000feff ? UTF32b : UTF32BE); + else if( cs==UTF32l || cs==UTF32LE ) + cs = (bom4==0xfffe0000 ? UTF32l : UTF32LE); + else if( cs==UTF16b || cs==UTF16BE ) + cs = (bom2==0xfeff ? UTF16b : UTF16BE); + else if( cs==UTF16l || cs==UTF16LE ) + cs = (bom2==0xfffe ? UTF16l : UTF16LE); + + if( cs != AutoDetect ) + return cs; + +//-- BOMチェック・7bitチェック + + bool Jp = ::IsValidCodePage(932)!=FALSE; + + if( (bom4>>8) == 0xefbbbf ) cs = UTF8; + else if( bom4 == 0x0000feff ) cs = UTF32b; + else if( bom4 == 0xfffe0000 ) cs = UTF32l; + else if( bom2 == 0xfeff ) cs = UTF16b; + else if( bom2 == 0xfffe ) cs = UTF16l; + else if( bom4 == 0x1b242943 && ::IsValidCodePage(949) ) cs = IsoKR; + else if( bom4 == 0x1b242941 && ::IsValidCodePage(936) ) cs = IsoCN; + else if( Jp && !bit8 && freq[0x1b]>0 ) cs = IsoJP; + + if( cs != AutoDetect ) + return cs; + +//-- UTF-5 チェック + + ulong u5sum = 0; + for( uchar c='0'; c<='9'; ++c ) u5sum += freq[c]; + for( uchar c='A'; c<='V'; ++c ) u5sum += freq[c]; + if( siz == u5sum ) + return UTF5; + +//-- 暫定版 UTF-8 / 日本語EUC チェック + + cs = defCs; + + // 改行コードがLFか、ある程度の大きさか、でないと + // 無条件で ANSI-CP と見なしてしまう。 + if( bit8 && (siz>4096 || lb_==1 + || freq[0xfd]>0 || freq[0xfe]>0 || freq[0xff]>0 || freq[0x80]>0) ) + { + // UHCやGBKはEUC-JPと非常に混同しやすいので、そっちがデフォルトの場合は + // EUC-JP自動判定を切る + if( Jp && ::GetACP()!=UHC && ::GetACP()!=GBK && ::GetACP()!=Big5 ) + { + // EUCとしておかしい値が無いかチェック + bool be=true; + for( int k=0x90; k<=0xa0; ++k )if( freq[k]>0 ){be=false;break;} + for( int k=0x7f; k<=0x8d; ++k )if( freq[k]>0 ){be=false;break;} + if( be ) + return EucJP; + } + { + // UTF8として読めるかどうかチェック + bool b8=true; + int mi=1; + for( ulong i=0; i=0xc0 ) + b8 = false; + } + else + { + mi = 1; + if( ptr[i] > 0x7f ) + { + mi = GetMaskIndex( ptr[i] ); + if( mi == 1 )//ptr[i] >= 0xfe ) + b8 = false; + } + } + if( b8 ) + return UTF8N; + } + } + +//-- 判定結果 + + return cs; +} + + + +//========================================================================= +// テキストファイル出力共通インターフェイス +//========================================================================= + +struct ki::TextFileWPimpl : public Object +{ + virtual void WriteLine( const unicode* buf, ulong siz ) + { while( siz-- ) WriteChar( *buf++ ); } + + virtual void WriteLB( const unicode* buf, ulong siz ) + { WriteLine( buf, siz ); } + + virtual void WriteChar( unicode ch ) + {} + + ~TextFileWPimpl() + { delete [] buf_; } + +protected: + + TextFileWPimpl( FileW& w ) + : fp_ (w) + , bsiz_ (65536) + , buf_ (new char[bsiz_]) {} + + void ReserveMoreBuffer() + { + char* nBuf = new char[bsiz_<<=1]; + delete [] buf_; + buf_ = nBuf; + } + + FileW& fp_; + ulong bsiz_; + char* buf_; +}; + + + +//------------------------------------------------------------------------- +// Unicodeテキスト +//------------------------------------------------------------------------- + +struct wUtf16LE : public TextFileWPimpl +{ + wUtf16LE( FileW& w, bool bom ) : TextFileWPimpl(w) + { if(bom){ unicode ch=0xfeff; fp_.Write(&ch,2); } } + void WriteLine( const unicode* buf, ulong siz ) {fp_.Write(buf,siz*2);} +}; + +struct wUtf16BE : public TextFileWPimpl +{ + wUtf16BE( FileW& w, bool bom ) : TextFileWPimpl(w) + { if(bom) WriteChar(0xfeff); } + void WriteChar( unicode ch ) { fp_.WriteC(ch>>8), fp_.WriteC(ch&0xff); } +}; + +struct wUtf32LE : public TextFileWPimpl +{ + wUtf32LE( FileW& w, bool bom ) : TextFileWPimpl(w) + { if(bom) {unicode c=0xfeff; WriteLine(&c,1);} } +// void WriteChar( unicode ch ) +// { fp_.WriteC(ch&0xff), fp_.WriteC(ch>>8), fp_.WriteC(0), fp_.WriteC(0); } + virtual void WriteLine( const unicode* buf, ulong siz ) + { + while( siz-- ) + { + unicode c = *buf++; + qbyte cc = c; + if( (0xD800<=c && c<=0xDBFF) && siz>0 ) // trail char が正しいかどうかはチェックする気がない + { + unicode c2 = *buf++; siz--; + cc = 0x10000 + (((c-0xD800)&0x3ff)<<10) + ((c2-0xDC00)&0x3ff); + } + for(int i=0; i<=3; ++i) + fp_.WriteC( (uchar)(cc>>(8*i)) ); + } + } +}; + +struct wUtf32BE : public TextFileWPimpl +{ + wUtf32BE( FileW& w, bool bom ) : TextFileWPimpl(w) + { if(bom) {unicode c=0xfeff; WriteLine(&c,1);} } +// void WriteChar( unicode ch ) +// { fp_.WriteC(0), fp_.WriteC(0), fp_.WriteC(ch>>8), fp_.WriteC(ch&0xff); } + virtual void WriteLine( const unicode* buf, ulong siz ) + { + while( siz-- ) + { + unicode c = *buf++; + qbyte cc = c; + if( (0xD800<=c && c<=0xDBFF) && siz>0 ) // trail char が正しいかどうかはチェックする気がない + { + unicode c2 = *buf++; siz--; + cc = 0x10000 + (((c-0xD800)&0x3ff)<<10) + ((c2-0xDC00)&0x3ff); + } + for(int i=3; i>=0; --i) + fp_.WriteC( (uchar)(cc>>(8*i)) ); + } + } +}; + +struct wWest : public TextFileWPimpl +{ + wWest( FileW& w ) : TextFileWPimpl(w) {} + void WriteChar( unicode ch ) { fp_.WriteC(ch>0xff ? '?' : (uchar)ch); } +}; + +struct wUtf5 : public TextFileWPimpl +{ + wUtf5( FileW& w ) : TextFileWPimpl(w) {} + void WriteChar( unicode ch ) + { + static const char conv[] = { + '0','1','2','3','4','5','6','7', + '8','9','A','B','C','D','E','F' }; + if(ch<0x10) + { + fp_.WriteC(ch+'G'); + } + else if(ch<0x100) + { + fp_.WriteC((ch>>4)+'G'); + fp_.WriteC(conv[ch&0xf]); + } + else if(ch<0x1000) + { + fp_.WriteC((ch>>8)+'G'); + fp_.WriteC(conv[(ch>>4)&0xf]); + fp_.WriteC(conv[ch&0xf]); + } + else + { + fp_.WriteC((ch>>12)+'G'); + fp_.WriteC(conv[(ch>>8)&0xf]); + fp_.WriteC(conv[(ch>>4)&0xf]); + fp_.WriteC(conv[ch&0xf]); + } + } +}; + + + +//------------------------------------------------------------------------- +// Win95対策の自前UTF8/UTF7処理 +//------------------------------------------------------------------------- +#ifndef _UNICODE + +struct wUTF8 : public TextFileWPimpl +{ + wUTF8( FileW& w, int cp ) + : TextFileWPimpl(w) + { + if( cp == UTF8 ) // BOM書き込み + fp_.Write( "\xEF\xBB\xBF", 3 ); + } + + void WriteLine( const unicode* str, ulong len ) + { + // 0000-0000-0xxx-xxxx | 0xxxxxxx + // 0000-0xxx-xxyy-yyyy | 110xxxxx 10yyyyyy + // xxxx-yyyy-yyzz-zzzz | 1110xxxx 10yyyyyy 10zzzzzz + // x-xxyy-yyyy-zzzz-zzww-wwww | 11110xxx 10yyyyyy 10zzzzzz 10wwwwww + // ... + while( len-- ) + { + qbyte ch = *str; + if( (0xD800<=ch&&ch<=0xDBFF) && len ) + ch = 0x10000 + (((ch-0xD800)&0x3ff)<<10) + ((*++str-0xDC00)&0x3ff), len--; + + if( ch <= 0x7f ) + fp_.WriteC( static_cast(ch) ); + else if( ch <= 0x7ff ) + fp_.WriteC( 0xc0 | static_cast(ch>>6) ), + fp_.WriteC( 0x80 | static_cast(ch&0x3f) ); + else if( ch<= 0xffff ) + fp_.WriteC( 0xe0 | static_cast(ch>>12) ), + fp_.WriteC( 0x80 | static_cast((ch>>6)&0x3f) ), + fp_.WriteC( 0x80 | static_cast(ch&0x3f) ); + else if( ch<= 0x1fffff ) + fp_.WriteC( 0xf0 | static_cast(ch>>18) ), + fp_.WriteC( 0x80 | static_cast((ch>>12)&0x3f) ), + fp_.WriteC( 0x80 | static_cast((ch>>6)&0x3f) ), + fp_.WriteC( 0x80 | static_cast(ch&0x3f) ); + else if( ch<= 0x3ffffff ) + fp_.WriteC( 0xf8 | static_cast(ch>>24) ), + fp_.WriteC( 0x80 | static_cast((ch>>18)&0x3f) ), + fp_.WriteC( 0x80 | static_cast((ch>>12)&0x3f) ), + fp_.WriteC( 0x80 | static_cast((ch>>6)&0x3f) ), + fp_.WriteC( 0x80 | static_cast(ch&0x3f) ); + else + fp_.WriteC( 0xfc | static_cast(ch>>30) ), + fp_.WriteC( 0x80 | static_cast((ch>>24)&0x3f) ), + fp_.WriteC( 0x80 | static_cast((ch>>18)&0x3f) ), + fp_.WriteC( 0x80 | static_cast((ch>>12)&0x3f) ), + fp_.WriteC( 0x80 | static_cast((ch>>6)&0x3f) ), + fp_.WriteC( 0x80 | static_cast(ch&0x3f) ); + ++str; + } + } +}; + + + +struct wUTF7 : public TextFileWPimpl +{ + wUTF7( FileW& w ) : TextFileWPimpl(w) {} + + void WriteLine( const unicode* str, ulong len ) + { + static const uchar mime[64] = { + 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', + 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f', + 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v', + 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'}; + + // XxxxxxYyyyyyZzzz | zzWwwwwwUuuuuuVv | vvvvTtttttSsssss + bool mode_m = false; + while( len ) + { + if( *str <= 0x7f ) + { + fp_.WriteC( static_cast(*str) ); + if( *str == L'+' ) + fp_.WriteC( '-' ); + ++str, --len; + } + else + { + if(!mode_m) fp_.WriteC( '+' ), mode_m=true; + unicode tx[3] = {0,0,0}; + int n=0; + tx[0] = *str, ++str, --len, ++n; + if( len && *str>0x7f ) + { + tx[1] = *str, ++str, --len, ++n; + if( len && *str>0x7f ) + tx[2] = *str, ++str, --len, ++n; + } + { + fp_.WriteC( mime[ tx[0]>>10 ] ); + fp_.WriteC( mime[ (tx[0]>>4)&0x3f ] ); + fp_.WriteC( mime[ (tx[0]<<2|tx[1]>>14)&0x3f ] ); + if( n>=2 ) + { + fp_.WriteC( mime[ (tx[1]>>8)&0x3f ] ); + fp_.WriteC( mime[ (tx[1]>>2)&0x3f ] ); + fp_.WriteC( mime[ (tx[1]<<4|tx[2]>>12)&0x3f ] ); + if( n>=3 ) + { + fp_.WriteC( mime[ (tx[2]>>6)&0x3f ] ); + fp_.WriteC( mime[ tx[2]&0x3f ] ); + } + } + } + if( len && *str<=0x7f ) + fp_.WriteC( '-' ), mode_m = false; + } + } + } +}; + + + +#endif +//------------------------------------------------------------------------- +// Windows頼りの変換 +//------------------------------------------------------------------------- + +struct wMBCS : public TextFileWPimpl +{ + wMBCS( FileW& w, int cp ) + : TextFileWPimpl(w), cp_(cp) + { + if( cp == UTF8 ) + { + // BOM書き込み + cp_ = UTF8N; + fp_.Write( "\xEF\xBB\xBF", 3 ); + } + } + + void WriteLine( const unicode* str, ulong len ) + { + // WideCharToMultiByte API を利用した変換 + int r; + while( + 0==(r=::WideCharToMultiByte(cp_,0,str,len,buf_,bsiz_,NULL,NULL)) + && ::GetLastError()==ERROR_INSUFFICIENT_BUFFER ) + ReserveMoreBuffer(); + + // ファイルへ書き込み + fp_.Write( buf_, r ); + } + + int cp_; +}; + + + +//------------------------------------------------------------------------- +// ISO-2022 サブセットその1。 +// ASCIIともう一つしか文字集合を使わないもの +//------------------------------------------------------------------------- + +struct wIso2022 : public TextFileWPimpl +{ + wIso2022( FileW& w, int cp ) + : TextFileWPimpl(w), hz_(cp==HZ) + { + switch( cp ) + { + case IsoKR: + fp_.Write( "\x1B\x24\x29\x43", 4 ); + cp_ = UHC; + break; + + case IsoCN: + fp_.Write( "\x1B\x24\x29\x41", 4 ); + // fall through... + default: + cp_ = GBK; + break; + } + } + + void WriteLine( const unicode* str, ulong len ) + { + // まず WideCharToMultiByte API を利用して変換 + int r; + while( + 0==(r=::WideCharToMultiByte(cp_,0,str,len,buf_,bsiz_,NULL,NULL)) + && ::GetLastError()==ERROR_INSUFFICIENT_BUFFER ) + ReserveMoreBuffer(); + + bool ascii = true; + for( int i=0; i JIS X 0208 +static void sjis2jis( uchar s1, uchar s2, char* k ) +{ + if( ((s1==0xfa || s1==0xfb) && s2>=0x40) || (s1==0xfc && (s2&0xf0)==0x40) ) + { + // IBM外字のマッピング +static const WORD IBM_sjis2kuten[] = { +/*fa40*/0x5c51,0x5c52,0x5c53,0x5c54,0x5c55,0x5c56,0x5c57,0x5c58,0x5c59,0x5c5a,0x0d15,0x0d16,0x0d17,0x0d18,0x0d19,0x0d1a, +/*fa50*/0x0d1b,0x0d1c,0x0d1d,0x0d1e,0x022c,0x5c5c,0x5c5d,0x5c5e,0x0d4a,0x0d42,0x0d44,0x0248,0x5901,0x5902,0x5903,0x5904, +/*fa60*/0x5905,0x5906,0x5907,0x5908,0x5909,0x590a,0x590b,0x590c,0x590d,0x590e,0x590f,0x5910,0x5911,0x5912,0x5913,0x5914, +/*fa70*/0x5915,0x5916,0x5917,0x5918,0x5919,0x591a,0x591b,0x591c,0x591d,0x591e,0x591f,0x5920,0x5921,0x5922,0x5923,/**/0x0109, +/*fa80*/0x5924,0x5925,0x5926,0x5927,0x5928,0x5929,0x592a,0x592b,0x592c,0x592d,0x592e,0x592f,0x5930,0x5931,0x5932,0x5933, +/*fa90*/0x5934,0x5935,0x5936,0x5937,0x5938,0x5939,0x593a,0x593b,0x593c,0x593d,0x593e,0x593f,0x5940,0x5941,0x5942,0x5943, +/*faa0*/0x5944,0x5945,0x5946,0x5947,0x5948,0x5949,0x594a,0x594b,0x594c,0x594d,0x594e,0x594f,0x5950,0x5951,0x5952,0x5953, +/*fab0*/0x5954,0x5955,0x5956,0x5957,0x5958,0x5959,0x595a,0x595b,0x595c,0x595d,0x595e,0x5a01,0x5a02,0x5a03,0x5a04,0x5a05, +/*fac0*/0x5a06,0x5a07,0x5a08,0x5a09,0x5a0a,0x5a0b,0x5a0c,0x5a0d,0x5a0e,0x5a0f,0x5a10,0x5a11,0x5a12,0x5a13,0x5a14,0x5a15, +/*fad0*/0x5a16,0x5a17,0x5a18,0x5a19,0x5a1a,0x5a1b,0x5a1c,0x5a1d,0x5a1e,0x5a1f,0x5a20,0x5a21,0x5a22,0x5a23,0x5a24,0x5a25, +/*fae0*/0x5a26,0x5a27,0x5a28,0x5a29,0x5a2a,0x5a2b,0x5a2c,0x5a2d,0x5a2e,0x5a2f,0x5a30,0x5a31,0x5a32,0x5a33,0x5a34,0x5a35, +/*faf0*/0x5a36,0x5a37,0x5a38,0x5a39,0x5a3a,0x5a3b,0x5a3c,0x5a3d,0x5a3e,0x5a3f,0x5a40,0x5a41,0x5a42,/**/0x0109,0x0109,0x0109, +/*fb40*/0x5a43,0x5a44,0x5a45,0x5a46,0x5a47,0x5a48,0x5a49,0x5a4a,0x5a4b,0x5a4c,0x5a4d,0x5a4e,0x5a4f,0x5a50,0x5a51,0x5a52, +/*fb50*/0x5a53,0x5a54,0x5a55,0x5a56,0x5a57,0x5a58,0x5a59,0x5a5a,0x5a5b,0x5a5c,0x5a5d,0x5a5e,0x5b01,0x5b02,0x5b03,0x5b04, +/*fb60*/0x5b05,0x5b06,0x5b07,0x5b08,0x5b09,0x5b0a,0x5b0b,0x5b0c,0x5b0d,0x5b0e,0x5b0f,0x5b10,0x5b11,0x5b12,0x5b13,0x5b14, +/*fb70*/0x5b15,0x5b16,0x5b17,0x5b18,0x5b19,0x5b1a,0x5b1b,0x5b1c,0x5b1d,0x5b1e,0x5b1f,0x5b20,0x5b21,0x5b22,0x5b23,/**/0x0109, +/*fb80*/0x5b24,0x5b25,0x5b26,0x5b27,0x5b28,0x5b29,0x5b2a,0x5b2b,0x5b2c,0x5b2d,0x5b2e,0x5b2f,0x5b30,0x5b31,0x5b32,0x5b33, +/*fb90*/0x5b34,0x5b35,0x5b36,0x5b37,0x5b38,0x5b39,0x5b3a,0x5b3b,0x5b3c,0x5b3d,0x5b3e,0x5b3f,0x5b40,0x5b41,0x5b42,0x5b43, +/*fba0*/0x5b44,0x5b45,0x5b46,0x5b47,0x5b48,0x5b49,0x5b4a,0x5b4b,0x5b4c,0x5b4d,0x5b4e,0x5b4f,0x5b50,0x5b51,0x5b52,0x5b53, +/*fbb0*/0x5b54,0x5b55,0x5b56,0x5b57,0x5b58,0x5b59,0x5b5a,0x5b5b,0x5b5c,0x5b5d,0x5b5e,0x5c01,0x5c02,0x5c03,0x5c04,0x5c05, +/*fbc0*/0x5c06,0x5c07,0x5c08,0x5c09,0x5c0a,0x5c0b,0x5c0c,0x5c0d,0x5c0e,0x5c0f,0x5c10,0x5c11,0x5c12,0x5c13,0x5c14,0x5c15, +/*fbd0*/0x5c16,0x5c17,0x5c18,0x5c19,0x5c1a,0x5c1b,0x5c1c,0x5c1d,0x5c1e,0x5c1f,0x5c20,0x5c21,0x5c22,0x5c23,0x5c24,0x5c25, +/*fbe0*/0x5c26,0x5c27,0x5c28,0x5c29,0x5c2a,0x5c2b,0x5c2c,0x5c2d,0x5c2e,0x5c2f,0x5c30,0x5c31,0x5c32,0x5c33,0x5c34,0x5c35, +/*fbf0*/0x5c36,0x5c37,0x5c38,0x5c39,0x5c3a,0x5c3b,0x5c3c,0x5c3d,0x5c3e,0x5c3f,0x5c40,0x5c41,0x5c42,/**/0x0109,0x0109,0x0109, +/*fc40*/0x5c43,0x5c44,0x5c45,0x5c46,0x5c47,0x5c48,0x5c49,0x5c4a,0x5c4b,0x5c4c,0x5c4d,0x5c4e,/**/0x0109,0x0109,0x0109,0x0109, +}; + k[0] = IBM_sjis2kuten[ (s1-0xfa)*12*16 + (s2-0x40) ]>>8; + k[1] = IBM_sjis2kuten[ (s1-0xfa)*12*16 + (s2-0x40) ]&0xff; + } + else + { + // その他 + if( s2>=0x9f ) + { + if( s1>=0xe0 ) k[0] = ((s1-0xc0)<<1); + else k[0] = ((s1-0x80)<<1); + k[1] = s2-0x9e; + } + else + { + if( s1>=0xe0 ) k[0] = ((s1-0xc0)<<1)-1; + else k[0] = ((s1-0x80)<<1)-1; + + if( s2 & 0x80 ) k[1] = s2-0x40; + else k[1] = s2-0x3f; + } + } + + k[0] += 0x20; + k[1] += 0x20; +} + +struct wEucJp : public TextFileWPimpl +{ + wEucJp( FileW& w ) + : TextFileWPimpl(w) {} + + void WriteLine( const unicode* str, ulong len ) + { + // まず WideCharToMultiByte API を利用して変換 + int r; + while( + 0==(r=::WideCharToMultiByte(932,0,str,len,buf_,bsiz_,NULL,NULL)) + && ::GetLastError()==ERROR_INSUFFICIENT_BUFFER ) + ReserveMoreBuffer(); + + for( int i=0; iWriteLine( buf, siz ); + if( !lastline ) + { + static const ulong lbLst[] = {0x0D, 0x0A, 0x000A000D}; + static const ulong lbLen[] = { 1, 1, 2}; + impl_->WriteLB( + reinterpret_cast(&lbLst[lb_]), lbLen[lb_] ); + } +} + +bool TextFileW::Open( const TCHAR* fname ) +{ + if( !fp_.Open( fname, true ) ) + return false; + + switch( cs_ ) + { + case Western: impl_ = new wWest( fp_ ); break; + case UTF5: impl_ = new wUtf5( fp_ ); break; + case UTF16l: + case UTF16LE: impl_ = new wUtf16LE( fp_, cs_==UTF16l ); break; + case UTF16b: + case UTF16BE: impl_ = new wUtf16BE( fp_, cs_==UTF16b ); break; + case UTF32l: + case UTF32LE: impl_ = new wUtf32LE( fp_, cs_==UTF32l ); break; + case UTF32b: + case UTF32BE: impl_ = new wUtf32BE( fp_, cs_==UTF32b ); break; + case EucJP: impl_ = new wEucJp( fp_ ); break; + case IsoJP: impl_ = new wIsoJp( fp_ ); break; + case IsoKR: impl_ = new wIso2022( fp_, cs_ ); break; + case IsoCN: impl_ = new wIso2022( fp_, cs_ ); break; + case HZ: impl_ = new wIso2022( fp_, cs_ ); break; + case UTF8: + case UTF8N: + default: +#ifndef _UNICODE + if( app().isWin95() && (cs_==UTF8 || cs_==UTF8N) ) + impl_ = new wUTF8( fp_, cs_ ); + else if( app().isWin95() && cs_==UTF7 ) + impl_ = new wUTF7( fp_ ); + else +#endif + impl_ = new wMBCS( fp_, cs_ ); + break; + } + return true; +} ADDED kilib/textfile.h Index: kilib/textfile.h ================================================================== --- kilib/textfile.h +++ kilib/textfile.h @@ -0,0 +1,215 @@ +#ifndef _KILIB_TEXTFILE_H_ +#define _KILIB_TEXTFILE_H_ +#include "types.h" +#include "ktlaptr.h" +#include "memory.h" +#include "file.h" +#ifndef __ccdoc__ +namespace ki { +#endif + + + +//========================================================================= +//@{ @pkg ki.StdLib //@} +//@{ +// 利用可能コードセット +// +// ただし、ここでリストアップされたもののうち、Windowsにちゃんと +// 言語サポートがインストールされているものだけが実際には対応可能。 +// 値が-100より小さいコードは、そのすぐ上にあるコードページの言語 +// サポートを利用して変換を行うため、それに依存する。 +//@} +//========================================================================= + +enum charset { + AutoDetect = 0, // 自動判定 + // SJIS/EucJP/IsoJP/IsoKR/IsoCN + // UTF5/UTF8/UTF8N/UTF16b/UTF16l/UTF32b/UTF32l + // を判定する。他は知らない。(^^; + + Western = 1252, // 欧米 (Windows1252 >> ISO-8859-1) + Turkish = 1254, // トルコ語 (Windows1254 >> ISO-8859-9) + Hebrew = 1255, // ヘブライ語(Windows1255 >> ISO-8859-8) + Arabic = 1256, // アラビア語(Windows1256 〜 ISO-8859-6) + Baltic = 1257, // バルト語 (Windows1257 >> ISO-8859-13) + Vietnamese = 1258, // ベトナム語(Windows1258 != VISCII) + Central = 1250, // 中央ヨーロッパ(Windows1250 〜 ISO-8859-2) + Greek = 1253, // ギリシャ語(Windows1253 〜 ISO-8859-7) + Thai = 874, // タイ語 + + Cyrillic = 1251, // キリル語(Windows1251 != ISO-8859-5) + Koi8R = 20866,// キリル語(KOI8-R) + Koi8U = 21866,// キリル語(KOI8-U ウクライナ系) + + UHC = 949, // 韓国語1 (Unified Hangle Code >> EUC-KR) + IsoKR = -950, // 韓国語2 (ISO-2022-KR) + Johab = 1361, // 韓国語3 (Johab) + + GBK = 936, // 中国語1 (簡体字 GBK >> EUC-CN) + IsoCN = -936, // 中国語2 (簡体字 ISO-2022-CN) + HZ = -937, // 中国語3 (簡体字 HZ-GB2312) + Big5 = 950, // 中国語4 (繁体字 Big5) + + SJIS = 932, // 日本語1 (Shift_JIS) + EucJP = -932, // 日本語2 (日本語EUC) + IsoJP = -933, // 日本語3 (ISO-2022-JP) + + UTF5 = -2, // Unicode (UTF-5) : BOM無し + UTF7 = 65000,// Unicode (UTF-7) : BOM無し + UTF8 =-65001,// Unicode (UTF-8) : BOM有り + UTF8N = 65001,// Unicode (UTF-8N) : BOM無し + UTF16b = -3, // Unicode (UTF-16) : BOM有り BE + UTF16l = -4, // Unicode (UTF-16) : BOM有り LE + UTF16BE = -5, // Unicode (UTF-16BE): BOM無し + UTF16LE = -6, // Unicode (UTF-16LE): BOM無し + UTF32b = -7, // Unicode (UTF-32) : BOM有り BE + UTF32l = -8, // Unicode (UTF-32) : BOM有り LE + UTF32BE = -9, // Unicode (UTF-32BE): BOM無し + UTF32LE = -10, // Unicode (UTF-32LE): BOM無し + + DOSUS = 437 // DOSLatinUS (CP437) +}; + +//========================================================================= +//@{ +// 改行コード +//@} +//========================================================================= + +enum lbcode { + CR = 0, + LF = 1, + CRLF = 2 +}; + +struct TextFileRPimpl; +struct TextFileWPimpl; + + + +//========================================================================= +//@{ +// テキストファイル読込 +// +// ファイルを指定された文字コードで解釈し、Unicode文字列として +// 一行毎に返す。文字コードや改行コードの自動判定も可能。 +//@} +//========================================================================= + +class TextFileR : public Object +{ +public: + + //@{ コンストラクタ(コード指定)//@} + TextFileR( int charset=AutoDetect ); + + //@{ デストラクタ //@} + ~TextFileR(); + + //@{ 開く //@} + bool Open( const TCHAR* fname ); + + //@{ 閉じる //@} + void Close(); + + //@{ + // 読み込み (読んだ長さを返す) + // + // 少なくとも20くらいのサイズを確保したバッファを指定してください。 + //@} + size_t ReadLine( unicode* buf, ulong siz ); + +public: + + //@{ 読んでるファイルのコードページ //@} + int codepage() const; + + //@{ 改行コード (0:CR, 1:LF, 2:CRLF) //@} + int linebreak() const; + + //@{ 読み込み状況 (0:EOF, 1:EOL, 2:EOB) //@} + int state() const; + + //@{ ファイルサイズ //@} + ulong size() const; + + //@{ 改行が一個も見つからなかったフラグ //@} + bool nolb_found() const; +private: + + dptr impl_; + FileR fp_; + int cs_; + int lb_; + bool nolbFound_; + +private: + + int AutoDetection( int cs, const uchar* ptr, ulong siz ); + +private: + + NOCOPY(TextFileR); +}; + + + +//------------------------------------------------------------------------- + +inline int TextFileR::codepage() const + { return cs_; } + +inline int TextFileR::linebreak() const + { return lb_; } + +inline ulong TextFileR::size() const + { return fp_.size(); } + +inline bool TextFileR::nolb_found() const + { return nolbFound_; } + + +//========================================================================= +//@{ +// テキストファイル書込 +// +// Unicode文字列を受け取り、指定された文字コードに変換しながら出力する。 +//@} +//========================================================================= + +class TextFileW : public Object +{ +public: + + //@{ コンストラクタ(文字,改行コード指定)//@} + TextFileW( int charset, int linebreak ); + ~TextFileW(); + + //@{ 開く //@} + bool Open( const TCHAR* fname ); + + //@{ 閉じる //@} + void Close(); + + //@{ 一行書き出し //@} + void WriteLine( const unicode* buf, ulong siz, bool lastline ); + +private: + + dptr impl_; + FileW fp_; + const int cs_; + const int lb_; + +private: + + NOCOPY(TextFileW); +}; + + + +//========================================================================= + +} // namespace ki +#endif // _KILIB_TEXTFILE_H_ ADDED kilib/thread.cpp Index: kilib/thread.cpp ================================================================== --- kilib/thread.cpp +++ kilib/thread.cpp @@ -0,0 +1,77 @@ +#include "stdafx.h" +#include "thread.h" +using namespace ki; + + + +//========================================================================= + +ThreadManager* ThreadManager::pUniqueInstance_; + +ThreadManager::ThreadManager() + : threadNum_(0) +{ + // 唯一のインスタンスは私です。 + pUniqueInstance_ = this; +} + +//========================================================================= + +DWORD WINAPI ThreadManager::ThreadStubFunc(void* param) +{ + Runnable* r = static_cast(param); + r->StartThread(); + r->FinalizeThread(); + return 0; +} + +void ThreadManager::Run( Runnable& r ) +{ + r.PleaseExit(); + + DWORD id; + r.hThread_ = ::CreateThread( + NULL, 0, &ThreadStubFunc, &r, CREATE_SUSPENDED, &id ); + r.hEvent_ = ::CreateEvent( NULL, TRUE, FALSE, NULL ); + ::ResumeThread( r.hThread_ ); +} + +//========================================================================= + +Runnable::Runnable() + : hThread_( NULL ) + , hEvent_ ( NULL ) +{ +} + +Runnable::~Runnable() +{ + PleaseExit(); // あくまで最終手段 +} + +bool Runnable::isRunning() const +{ + return hThread_ != NULL; +} + +bool Runnable::isExitRequested() const +{ + return WAIT_OBJECT_0 == ::WaitForSingleObject( hEvent_, 0 ); +} + +void Runnable::FinalizeThread() +{ + ::CloseHandle( hThread_ ); + hThread_ = NULL; + ::CloseHandle( hEvent_ ); + hEvent_ = NULL; +} + +void Runnable::PleaseExit() +{ + if( HANDLE ht = hThread_ ) + { + ::SetEvent( hEvent_ ); + ::WaitForSingleObject( ht, INFINITE ); + } +} ADDED kilib/thread.h Index: kilib/thread.h ================================================================== --- kilib/thread.h +++ kilib/thread.h @@ -0,0 +1,202 @@ +#ifndef _KILIB_THREAD_H_ +#define _KILIB_THREAD_H_ +#include "types.h" +#ifndef __ccdoc__ +namespace ki { +#endif + + + +//========================================================================= +//@{ @pkg ki.Core //@} +//@{ +// マルチスレッドの管理 +// +// まだ何もしません +//@} +//========================================================================= + + +class ThreadManager +{ +public: + //@{ 実行可能オブジェクトをに起動をかける //@} + void Run( class Runnable& r ); + + //@{ 複数スレッドが走っているかどうか? //@} + bool isMT() const; + +private: + + ThreadManager(); + +private: + + int threadNum_; + static ThreadManager* pUniqueInstance_; + +private: + + static DWORD WINAPI ThreadStubFunc(void*); + +private: + + friend void APIENTRY Startup(); + friend inline ThreadManager& thd(); + NOCOPY(ThreadManager); +}; + + + +//------------------------------------------------------------------------- + +//@{ 唯一のスレッド管理オブジェクトを返す //@} +inline ThreadManager& thd() + { return *ThreadManager::pUniqueInstance_; } + +inline bool ThreadManager::isMT() const + { return 0 != threadNum_; } + + + +//========================================================================= +//@{ +// 実行可能オブジェクト基底 +//@} +//========================================================================= + +class Runnable +{ +protected: + Runnable(); + virtual ~Runnable(); + + virtual void StartThread() = 0; + void PleaseExit(); + bool isExitRequested() const; + bool isRunning() const; + +private: + void FinalizeThread(); + friend class ThreadManager; + HANDLE hEvent_; + HANDLE hThread_; +}; + + + +//========================================================================= +//@{ +// マルチスレッド・ポリシー1 +// +// このクラスから派生すると、AutoLock クラスを使えるようになります。 +// このクラスに this ポインタを渡すことで排他状態に入り、デストラクタで +// 抜け出せるようになります。NoLockable::AutoLock は実際は何もしません。 +//@} +//========================================================================= + +class NoLockable +{ +protected: + struct AutoLock + { + AutoLock( NoLockable* host ) {} + }; +}; + + + +//========================================================================= +//@{ +// マルチスレッド・ポリシー2 +// +// このクラスから派生すると、AutoLock クラスを使えるようになります。 +// このクラスに this ポインタを渡すことで排他状態に入り、デストラクタで +// 抜け出せるようになります。EzLockable::AutoLock は、シングルスレッドで +// 動作するときがほとんどというアプリケーション向けに、高速だけれど +// 不完全な排他制御を行います。2本目のスレッド立ち上げの瞬間が危ない。 +//@} +//========================================================================= + +class EzLockable +{ +protected: + EzLockable() + { ::InitializeCriticalSection( &csection_ ); } + ~EzLockable() + { ::DeleteCriticalSection( &csection_ ); } + + struct AutoLock + { + AutoLock( EzLockable* host ) + { + if( NULL != (pCs_=(thd().isMT() ? &host->csection_ : NULL)) ) + ::EnterCriticalSection( pCs_ ); + } + ~AutoLock() + { + if( pCs_ ) + ::LeaveCriticalSection( pCs_ ); + } + private: + NOCOPY(AutoLock); + CRITICAL_SECTION* pCs_; + }; + +private: + CRITICAL_SECTION csection_; + #ifdef __DMC__ + friend struct EzLockable::AutoLock; + #else + friend struct AutoLock; + #endif +}; + + + +//========================================================================= +//@{ +// マルチスレッド・ポリシー3 +// +// このクラスから派生すると、AutoLock クラスを使えるようになります。 +// このクラスに this ポインタを渡すことで排他状態に入り、デストラクタで +// 抜け出せるようになります。Lockable::AutoLock は、完全な排他制御を +// 行います。万全を期すならかならずこのクラスを用いましょう。 +//@} +//========================================================================= + +class Lockable +{ +protected: + Lockable() + { ::InitializeCriticalSection( &csection_ ); } + ~Lockable() + { ::DeleteCriticalSection( &csection_ ); } + + struct AutoLock + { + AutoLock( Lockable* host ) + { + pCs_ = &host->csection_; + ::EnterCriticalSection( pCs_ ); + } + ~AutoLock() + { + ::LeaveCriticalSection( pCs_ ); + } + private: + NOCOPY(AutoLock); + CRITICAL_SECTION* pCs_; + }; + friend struct AutoLock; + +private: + CRITICAL_SECTION csection_; +}; + + + +//========================================================================= + +} // namespace ki +#endif // _KILIB_THREAD_H_ ADDED kilib/types.h Index: kilib/types.h ================================================================== --- kilib/types.h +++ kilib/types.h @@ -0,0 +1,39 @@ +#ifndef _KILIB_TYPES_H_ +#define _KILIB_TYPES_H_ + + + +//========================================================================= +//@{ @pkg ki.Types //@} +//========================================================================= + +// 変数のサイズを明示的に指示するときに使う名前 +typedef unsigned char byte; +typedef unsigned short dbyte; +typedef unsigned long qbyte; +typedef wchar_t unicode; + +// unsigned って毎回打つの面倒 +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; + +// 配列の要素数 +#define countof(_array) (sizeof(_array)/sizeof(_array[0])) + +// 大きい方、小さい方 +template inline T Min(T x,T y) { return (x inline T Max(T x,T y) { return (yQueryInterface( + IID_IActiveIMMMessagePumpOwner, (void**)&immMsg_ ); + } + } + #endif + + // 唯一のインスタンスは私です + pUniqueInstance_ = this; +} + +IMEManager::~IMEManager() +{ + #ifdef USEGLOBALIME + if( immMsg_ != NULL ) + immMsg_->Release(); + if( immApp_ != NULL ) + immApp_->Release(); + #endif +} + +void IMEManager::EnableGlobalIME( bool enable ) +{ + #ifdef USEGLOBALIME + if( immApp_ ) + if( enable ) immApp_->Activate( TRUE ); + else immApp_->Deactivate(); + #endif +} + +void IMEManager::FilterWindows( ATOM* lst, UINT siz ) +{ + #ifdef USEGLOBALIME + if( immApp_ ) + immApp_->FilterClientWindows( lst, siz ); + #endif +} + +inline void IMEManager::TranslateMsg( MSG* msg ) +{ + #ifdef USEGLOBALIME + if( immMsg_ ) + if( S_OK == immMsg_->OnTranslateMessage( msg ) ) + return; + #endif + ::TranslateMessage( msg ); +} + +inline LRESULT IMEManager::DefProc( HWND h, UINT m, WPARAM w, LPARAM l ) +{ + #ifdef USEGLOBALIME + if( immApp_ ) + { + LRESULT res; + if( S_OK == immApp_->OnDefWindowProc( h,m,w,l,&res ) ) + return res; + } + #endif + return ::DefWindowProc( h, m, w, l ); +} + +inline void IMEManager::MsgLoopBegin() +{ + #ifdef USEGLOBALIME + if( immMsg_ ) + immMsg_->Start(); + #endif +} + +inline void IMEManager::MsgLoopEnd() +{ + #ifdef USEGLOBALIME + if( immMsg_ ) + immMsg_->End(); + #endif +} + +void IMEManager::SetFont( HWND wnd, const LOGFONT& lf ) +{ + HIMC ime; + LOGFONT* plf = const_cast(&lf); + + #ifdef USEGLOBALIME + if( immApp_ ) + { + immApp_->GetContext( wnd, &ime ); + #ifdef _UNICODE + immApp_->SetCompositionFontW( ime, plf ); + #else + immApp_->SetCompositionFontA( ime, plf ); + #endif + immApp_->ReleaseContext( wnd, ime ); + } + else + #endif + { + ime = ::ImmGetContext( wnd ); + ::ImmSetCompositionFont( ime, plf ); + ::ImmReleaseContext( wnd, ime ); + } +} + +void IMEManager::SetPos( HWND wnd, int x, int y ) +{ + HIMC ime; + COMPOSITIONFORM cf; + cf.dwStyle = CFS_POINT; + cf.ptCurrentPos.x = x; + cf.ptCurrentPos.y = y; + + #ifdef USEGLOBALIME + if( immApp_ ) + { + immApp_->GetContext( wnd, &ime ); + immApp_->SetCompositionWindow( ime, &cf ); + immApp_->ReleaseContext( wnd, ime ); + } + else + #endif + { + ime = ::ImmGetContext( wnd ); + ::ImmSetCompositionWindow( ime, &cf ); + ::ImmReleaseContext( wnd, ime ); + } +} + +void IMEManager::GetString( HWND wnd, unicode** str, ulong* len ) +{ + *str = NULL; + HIMC ime; + + #ifdef USEGLOBALIME + if( immApp_ ) + { + long s=0; + immApp_->GetContext( wnd, &ime ); + immApp_->GetCompositionStringW( ime, GCS_RESULTSTR, 0, &s, NULL ); + *str = new unicode[ (*len=s/2)+1 ]; + immApp_->GetCompositionStringW( ime, GCS_RESULTSTR, s, &s, *str ); + immApp_->ReleaseContext( wnd, ime ); + } + else + #endif + { + ime = ::ImmGetContext( wnd ); + long s = ::ImmGetCompositionStringW( ime,GCS_RESULTSTR,NULL,0 ); + + #ifndef _UNICODE + if( s <= 0 ) + { + s = ::ImmGetCompositionStringA(ime,GCS_RESULTSTR,NULL,0); + if( s > 0 ) + { + char* tmp = new char[s]; + *str = new unicode[*len=s*2]; + ::ImmGetCompositionStringA( ime,GCS_RESULTSTR,tmp,s ); + *len = ::MultiByteToWideChar( + CP_ACP, MB_PRECOMPOSED, tmp, s, *str, *len ); + delete [] tmp; + } + } + else + #endif + { + *str = new unicode[ (*len=s/2)+1 ]; + ::ImmGetCompositionStringW( ime, GCS_RESULTSTR, *str, s ); + } + + ::ImmReleaseContext( wnd, ime ); + } +} + + + +//========================================================================= +// Windowに関するあれこれ +//========================================================================= + +Window::Window() + : wnd_ (NULL) + , isLooping_(false) +{ +} + +void Window::SetHwnd( HWND wnd ) +{ + wnd_ = wnd; +} + +void Window::MsgLoop() +{ + // thisをメインウインドウとして、 + // メインメッセージループを回す + isLooping_ = true; + ime().MsgLoopBegin(); + + for( MSG msg; ::GetMessage( &msg, NULL, 0, 0 ); ) + if( !PreTranslateMessage( &msg ) ) + { + ime().TranslateMsg( &msg ); + ::DispatchMessage( &msg ); + } + + ime().MsgLoopEnd(); + isLooping_ = false; +} + +void Window::ProcessMsg() +{ + // こっちはグローバル関数。 + // 未処理メッセージを一掃 + + ime().MsgLoopBegin(); + for( MSG msg; ::PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ); ) + { + ime().TranslateMsg( &msg ); + ::DispatchMessage( &msg ); + } + ime().MsgLoopEnd(); +} + + + +//------------------------------------------------------------------------- + +void Window::SetCenter( HWND hwnd, HWND rel ) +{ + // 自分のサイズを取得 + RECT rc,pr; + ::GetWindowRect( hwnd, &rc ); + + // 親の位置、ないしは全画面の位置を取得 + if( rel != NULL ) + ::GetWindowRect( rel, &pr ); + else + ::SystemParametersInfo( SPI_GETWORKAREA, 0, &pr, 0 ); + + // 中央を計算 + ::SetWindowPos( hwnd, 0, + pr.left + ( (pr.right-pr.left)-(rc.right-rc.left) )/2, + pr.top + ( (pr.bottom-pr.top)-(rc.bottom-rc.top) )/2, + 0, 0, SWP_NOSIZE|SWP_NOZORDER ); +} + +void Window::SetFront( HWND hwnd ) +{ + // kazubon氏の TClock のソースを参考にしました。感謝! + + if( app().isNewTypeWindows() ) + { + DWORD pid; + HWND fore= ::GetForegroundWindow(); + DWORD th1 = ::GetWindowThreadProcessId( fore, &pid ); + DWORD th2 = ::GetCurrentThreadId(); + ::AttachThreadInput( th2, th1, TRUE ); + ::SetForegroundWindow( hwnd ); + ::AttachThreadInput( th2, th1, FALSE ); + ::BringWindowToTop( hwnd ); + } + else + { + ::SetForegroundWindow( hwnd ); + } +} + +//========================================================================= + +WndImpl::WndImpl( LPCTSTR className, DWORD style, DWORD styleEx ) + : className_( className ) + , style_ ( style ) + , styleEx_ ( styleEx ) + , thunk_ ( static_cast( + ::VirtualAlloc( NULL, THUNK_SIZE, MEM_COMMIT, PAGE_EXECUTE_READWRITE )) ) +{ +} + +WndImpl::~WndImpl() +{ + // ウインドウを破棄し忘れてたら閉じる + // …が、この時点で既に vtable は破棄されかかっているので + // 正しい on_destroy が呼ばれる保証は全くない。あくまで + // 緊急脱出用(^^; と考えること。 + Destroy(); + ::VirtualFree( thunk_, 0, MEM_RELEASE ); +} + +void WndImpl::Destroy() +{ + if( hwnd() != NULL ) + ::DestroyWindow( hwnd() ); +} + +ATOM WndImpl::Register( WNDCLASSEX* cls ) +{ + // WndImpl派生クラスで使うWndClassを登録。 + // プロシージャはkilib謹製のものに書き換えちゃいます。 + cls->cbSize = sizeof(WNDCLASSEX); + cls->hInstance = app().hinst(); + cls->lpfnWndProc = StartProc; + return ::RegisterClassEx( cls ); +} + +struct ThisAndParam +{ + // ユーザ指定のパラメータ以外にも渡したいモノが少々… + WndImpl* pThis; + void* pParam; +}; + +bool WndImpl::Create( + LPCTSTR wndName, HWND parent, int x, int y, int w, int h, void* param ) +{ + // ここでthisポインタを忍び込ませておく + ThisAndParam z = { this, param }; + + LOGGER("WndImpl::Create before CreateWindowEx API call"); + + return (NULL != ::CreateWindowEx( + styleEx_, className_, wndName, style_, + x, y, w, h, parent, NULL, app().hinst(), &z + )); +} + + + +//------------------------------------------------------------------------- + +LRESULT CALLBACK WndImpl::StartProc( + HWND wnd, UINT msg, WPARAM wp, LPARAM lp ) +{ + // WM_CREATE以外はスルーの方針で + if( msg != WM_CREATE ) + return ::DefWindowProc( wnd, msg, wp, lp ); + + LOGGER("WndImpl::StartProc WM_CREATE kitaaaaa!!"); + + // 忍ばせて置いたthisポインタを取り出し + CREATESTRUCT* cs = reinterpret_cast(lp); + ThisAndParam* pz = static_cast(cs->lpCreateParams); + WndImpl* pThis = pz->pThis; + cs->lpCreateParams = pz->pParam; + + // サンク + pThis->SetUpThunk( wnd ); + + // WM_CREATE用メッセージを呼ぶ + pThis->on_create( cs ); + return 0; +} + +void WndImpl::SetUpThunk( HWND wnd ) +{ + SetHwnd( wnd ); + + // ここで動的にx86の命令列 + // | mov dword ptr [esp+4] this + // | jmp MainProc + // あるいはAMD64の命令列 + // | mov rcx this + // | mov rax MainProc + // | jmp rax + // を生成し、メッセージプロシージャとして差し替える。 + // + // これで次回からは、第一引数が hwnd のかわりに + // thisポインタになった状態でMainProcが呼ばれる + // …と見なしたプログラムが書ける。 + // + // 参考資料:ATLのソース + +#ifdef _M_AMD64 + *reinterpret_cast (thunk_+ 0) = 0xb948; + *reinterpret_cast(thunk_+ 2) = this; + *reinterpret_cast (thunk_+10) = 0xb848; + *reinterpret_cast (thunk_+12) = MainProc; + *reinterpret_cast (thunk_+20) = 0xe0ff; +#else + *reinterpret_cast (thunk_+0) = 0x042444C7; + *reinterpret_cast(thunk_+4) = this; + *reinterpret_cast< byte*> (thunk_+8) = 0xE9; + *reinterpret_cast (thunk_+9) = + reinterpret_cast((void*)MainProc)-(thunk_+13); +#endif + + ::FlushInstructionCache( ::GetCurrentProcess(), thunk_, THUNK_SIZE ); + ::SetWindowLongPtr( wnd, GWLP_WNDPROC, reinterpret_cast(&thunk_[0]) ); +} + +LRESULT CALLBACK WndImpl::MainProc( + WndImpl* ptr, UINT msg, WPARAM wp, LPARAM lp ) +{ + if( msg == WM_COMMAND ) + { + if( !ptr->on_command( LOWORD(wp), (HWND)lp ) ) + return ::DefWindowProc( ptr->hwnd(), msg, wp, lp ); + } + else if( msg == WM_DESTROY ) + { + ptr->on_destroy(); + ::SetWindowLongPtr( ptr->hwnd(), GWLP_WNDPROC, + reinterpret_cast(StartProc) ); + if( ptr->isMainWnd() ) + ::PostQuitMessage( 0 ); + ptr->SetHwnd(NULL); + } + else + { + return ptr->on_message( msg, wp, lp ); + } + + return 0; +} + + + +//------------------------------------------------------------------------- + +void WndImpl::on_create( CREATESTRUCT* cs ) +{ + // 何もしない +} + +void WndImpl::on_destroy() +{ + // 何もしない +} + +bool WndImpl::on_command( UINT, HWND ) +{ + // 何もしない + return false; +} + +LRESULT WndImpl::on_message( UINT msg, WPARAM wp, LPARAM lp ) +{ + // 何もしない + return ime().DefProc( hwnd(), msg, wp, lp ); +} + +bool WndImpl::PreTranslateMessage( MSG* ) +{ + // 何もしない + return false; +} + + + +//========================================================================= + +DlgImpl::DlgImpl( UINT id ) + : rsrcID_( id ) +{ +} + +DlgImpl::~DlgImpl() +{ + // ウインドウを破棄し忘れてたら閉じる + if( hwnd() != NULL ) + End( IDCANCEL ); +} + +void DlgImpl::End( UINT code ) +{ + endCode_ = code; + + if( type() == MODAL ) + ::EndDialog( hwnd(), code ); + else + ::DestroyWindow( hwnd() ); +} + +void DlgImpl::GoModal( HWND parent ) +{ + type_ = MODAL; + ::DialogBoxParam( app().hinst(), MAKEINTRESOURCE(rsrcID_), parent, + (DLGPROC)MainProc, reinterpret_cast(this) ); +} + +void DlgImpl::GoModeless( HWND parent ) +{ + type_ = MODELESS; + ::CreateDialogParam( app().hinst(), MAKEINTRESOURCE(rsrcID_), parent, + (DLGPROC)MainProc, reinterpret_cast(this) ); +} + + + +//------------------------------------------------------------------------- + +BOOL CALLBACK DlgImpl::MainProc( + HWND dlg, UINT msg, WPARAM wp, LPARAM lp ) +{ + if( msg == WM_INITDIALOG ) + { + ::SetWindowLongPtr( dlg, GWLP_USERDATA, lp ); + + DlgImpl* ptr = reinterpret_cast(lp); + ptr->SetHwnd( dlg ); + ptr->on_init(); + return FALSE; + } + + DlgImpl* ptr = + reinterpret_cast(::GetWindowLongPtr(dlg,GWLP_USERDATA)); + + if( ptr != NULL ) + switch( msg ) + { + case WM_COMMAND: + switch( LOWORD(wp) ) + { + case IDOK: + if( ptr->on_ok() ) + ptr->End( IDOK ); + return TRUE; + + case IDCANCEL: + if( ptr->on_cancel() ) + ptr->End( IDCANCEL ); + return TRUE; + + default: + return ptr->on_command( HIWORD(wp), LOWORD(wp), + reinterpret_cast(lp) ) ? TRUE : FALSE; + } + + case WM_DESTROY: + ptr->on_destroy(); + if( ptr->isMainWnd() ) + ::PostQuitMessage( 0 ); + ptr->SetHwnd(NULL); + break; + + default: + return ptr->on_message( msg, wp, lp ) ? TRUE : FALSE; + } + + return FALSE; +} + + + +//------------------------------------------------------------------------- + +void DlgImpl::on_init() +{ + // 何もしない +} + +void DlgImpl::on_destroy() +{ + // 何もしない +} + +bool DlgImpl::on_ok() +{ + // 何もしない + return true; +} + +bool DlgImpl::on_cancel() +{ + // 何もしない + return true; +} + +bool DlgImpl::on_command( UINT, UINT, HWND ) +{ + // 何もしない + return false; +} + +bool DlgImpl::on_message( UINT, WPARAM, LPARAM ) +{ + // 何もしない + return false; +} + +bool DlgImpl::PreTranslateMessage( MSG* msg ) +{ + // モードレスの時用。ダイアログメッセージ処理。 + return (FALSE != ::IsDialogMessage( hwnd(), msg )); +} ADDED kilib/window.h Index: kilib/window.h ================================================================== --- kilib/window.h +++ kilib/window.h @@ -0,0 +1,418 @@ +#ifndef _KILIB_WINDOW_H_ +#define _KILIB_WINDOW_H_ +#include "types.h" +#include "memory.h" +#include "ktlaptr.h" +#ifndef __ccdoc__ +namespace ki { +#endif + + + +// タイムアウト付きMsgBoxの返値 +#define IDTIMEOUT 0 + + + +//========================================================================= +//@{ @pkg ki.Window //@} +//@{ +// 窓操作用クラス +// +// 外側から、ダイアログやコントロールやプロパティシートや +// 普通のウインドウを全部まとめて共通に扱うためのインターフェイス。 +// 実際の実装は、下位クラス XxxImpl で行われている。 +//@} +//========================================================================= + +class Window : public Object +{ +public: + + //@{ メインメッセージループ //@} + void MsgLoop(); + + //@{ メッセージを送って処理されるまで待機 //@} + LRESULT SendMsg( UINT msg, WPARAM wp=0, LPARAM lp=0 ); + + //@{ メッセージを送ってすぐ帰る //@} + BOOL PostMsg( UINT msg, WPARAM wp=0, LPARAM lp=0 ); + + //@{ + // 自動消滅機能付きメッセージボックス + // @param msg 表示する文字列 + // @param caption ダイアログの題名 + // @param type Win32SDKの説明を見てね + //@} + int MsgBox( LPCTSTR msg, LPCTSTR caption=NULL, UINT type=MB_OK ) const; + + //@{ テキスト設定 //@} + void SetText( const TCHAR* str ); + + //@{ 表示 //@} + void ShowUp( int sw=SW_SHOW ); + + //@{ 移動 //@} + void MoveTo( int l, int t, int r, int b ); + + //@{ フォーカス //@} + void SetFocus(); + + //@{ 最前面へGo! //@} + void SetFront(); + + //@{ 画面中央へGo! //@} + void SetCenter(); + +public: + + //@{ ウインドウハンドル //@} + HWND hwnd() const; + + //@{ 位置・サイズ //@} + void getPos( RECT* rc ) const; + + //@{ サイズ //@} + void getClientRect( RECT* rc ) const; + + //@{ メインループを回してるウインドウかどうか //@} + bool isMainWnd() const; + + //@{ 生きてる? //@} + bool isAlive() const; + +public: + + //@{ 未処理メッセージを適当に処理 //@} + static void ProcessMsg(); + + //@{ 最前面へGo! //@} + static void SetFront( HWND hwnd ); + + //@{ + // 画面中央へGo! + // @param hwnd 動かすウインドウ + // @param rel 基準にするウインドウ + //@} + static void SetCenter( HWND hwnd, HWND rel=NULL ); + +protected: + + // 何もしないコンストラクタ + Window(); + + // Hwndをセット + void SetHwnd( HWND wnd ); + + // アクセラレータを通すとかダイアログメッセージの処理とか + virtual bool PreTranslateMessage( MSG* ) = 0; + +private: + + HWND wnd_; + bool isLooping_; + +private: + + NOCOPY(Window); +}; + + + +//------------------------------------------------------------------------- +#ifndef __ccdoc__ + +inline LRESULT Window::SendMsg( UINT msg, WPARAM wp, LPARAM lp ) + { return ::SendMessage( wnd_, msg, wp, lp ); } + +inline BOOL Window::PostMsg( UINT msg, WPARAM wp, LPARAM lp ) + { return ::PostMessage( wnd_, msg, wp, lp ); } + +inline int Window::MsgBox( LPCTSTR m, LPCTSTR c, UINT y ) const + { return ::MessageBox( wnd_, m, c, y ); } + +inline void Window::ShowUp( int sw ) + { ::ShowWindow( wnd_, sw ), ::UpdateWindow( wnd_ ); } + +inline void Window::SetText( const TCHAR* str ) + { ::SetWindowText( wnd_, str ); } + +inline void Window::MoveTo( int l, int t, int r, int b ) + { ::MoveWindow( wnd_, l, t, r-l, b-t, TRUE ); } + +inline void Window::SetFocus() + { ::SetFocus( wnd_ ); } + +inline void Window::SetFront() + { SetFront( wnd_ ); } + +inline void Window::SetCenter() + { SetCenter( wnd_ ); } + +inline HWND Window::hwnd() const + { return wnd_; } + +inline bool Window::isMainWnd() const + { return isLooping_; } + +inline void Window::getPos( RECT* rc ) const + { ::GetWindowRect( wnd_, rc ); } + +inline void Window::getClientRect( RECT* rc ) const + { ::GetClientRect( wnd_, rc ); } + +inline bool Window::isAlive() const + { return FALSE != ::IsWindow( wnd_ ); } + + + +#endif // __ccdoc__ +//========================================================================= +//@{ +// IME制御マネージャ +// +// Global IME をサポートするには、ウインドウメッセージの処理を +// 根本的に入れ替える必要がある。そこで、処理をこのクラスにまとめ +// Windowクラスと連携処理を行うことで、ライブラリの外からは一切 +// 気にせず処理をできるようにしておく。なお、Global IMEに対応 +// するにはバージョンの新しいPlatform SDKが必要なため +// マクロ USEGLOBALIME が定義されていなければその辺は処理しない。 +//@} +//========================================================================= + +class IMEManager +{ +public: + + //@{ フォント指定 //@} + void SetFont( HWND wnd, const LOGFONT& lf ); + + //@{ 位置指定 //@} + void SetPos( HWND wnd, int x, int y ); + + //@{ 確定文字列ゲット。受け取ったら delete すること。 //@} + void GetString( HWND wnd, unicode** str, ulong* len ); + + //@{ GlobalIMEを利用可能状態にする //@} + void EnableGlobalIME( bool enable ); + + //@{ GlobalIMEを使えるWindowのリストを登録 //@} + void FilterWindows( ATOM* lst, UINT siz ); + +private: + + IMEManager(); + ~IMEManager(); + void TranslateMsg( MSG* msg ); + LRESULT DefProc( HWND wnd, UINT msg, WPARAM wp, LPARAM lp ); + void MsgLoopBegin(); + void MsgLoopEnd(); + +private: + + #ifdef USEGLOBALIME + IActiveIMMApp* immApp_; + IActiveIMMMessagePumpOwner* immMsg_; + #endif + static IMEManager* pUniqueInstance_; + +private: + + friend class Window; + friend class WndImpl; + friend void APIENTRY Startup(); + friend inline IMEManager& ime(); + NOCOPY(IMEManager); +}; + + + +//------------------------------------------------------------------------- + +//@{ 唯一のIME管理オブジェクトを返す //@} +inline IMEManager& ime() + { return *IMEManager::pUniqueInstance_; } + + + +//========================================================================= +//@{ +// 普通のウインドウ実装 +// +// 派生クラスを定義して、コンストラクタの引数で WndImpl に +// WNDCLASS名やスタイルを渡し、初回なら WNDCLASS を Register +// して、あとは適当なタイミングで Create() という使い方を想定。 +// +// HWNDからWndImpl*への変換はx86/x86-64専用のサンクで行っています。 +// ので、他のアーキテクチャではこのままでは動作しません。移植 +// する場合は、GWL_USERDATA を使うなり clsExtra に入れるなりで +// もう少し汎用性のある方法に適宜変えてください。 +//@} +//========================================================================= + +class WndImpl : public Window +{ + enum { THUNK_SIZE = 22 }; + +public: + + //@{ ウインドウ作成 //@} + bool Create( LPCTSTR wndName=NULL, HWND parent=NULL, + int x =CW_USEDEFAULT, int y =CW_USEDEFAULT, + int width =CW_USEDEFAULT, int height =CW_USEDEFAULT, + void* param=NULL ); + + //@{ ウインドウ破棄 //@} + void Destroy(); + +protected: + + //@{ + // コンストラクタ + // @param className ウインドウクラス名 + // @param style 標準スタイル + // @param styleEx 標準拡張スタイル + //@} + WndImpl( LPCTSTR className, DWORD style, DWORD styleEx=0 ); + ~WndImpl(); + + //@{ クラス名用の型 //@} + typedef const TCHAR* const ClsName; + + //@{ ウインドウクラス登録 //@} + static ATOM Register( WNDCLASSEX* cls ); + + // てけとーに実装して反応してください。 + // on_commandは、処理しなかったらfalseを返すこと。 + // on_messageは、処理しなかったらWndImpl::on_messageを呼び出すこと。 + // PreTranslateMessageは、処理してもしなくても中で呼び出すこと。 + virtual void on_create( CREATESTRUCT* cs ); + virtual void on_destroy(); + virtual bool on_command( UINT id, HWND ctrl ); + virtual LRESULT on_message( UINT msg, WPARAM wp, LPARAM lp ); + virtual bool PreTranslateMessage( MSG* msg ); + +private: + + static LRESULT CALLBACK StartProc( HWND, UINT, WPARAM, LPARAM ); + static LRESULT CALLBACK MainProc( WndImpl*, UINT, WPARAM, LPARAM ); + void SetUpThunk( HWND wnd ); + +private: + + LPCTSTR className_; + const DWORD style_, styleEx_; + byte* thunk_; +}; + + + +//========================================================================= +//@{ +// ダイアログ実装 +// +// 派生クラスを定義して、コンストラクタの引数で DlgImpl に +// リソースIDを渡し、後は適当なタイミングで GoModal なり +// Modeless なり、という使い方を想定。 +// +// HWNDからDlgImpl*への変換は GWL_USERDATA で行っています。 +// ので、それは使わないようにしましょう。 +//@} +//========================================================================= + +class DlgImpl : public Window +{ +public: + + enum dlgtype { MODAL, MODELESS }; + + //@{ モーダルで実行 //@} + void GoModal( HWND parent=NULL ); + + //@{ モードレスで作成 //@} + void GoModeless( HWND parent=NULL ); + + //@{ 強制的に終わらせる //@} + void End( UINT code ); + +public: + + //@{ モーダルかモードレスか //@} + dlgtype type() const; + + //@{ 終了コード取得 //@} + UINT endcode() const; + +protected: + + //@{ コンストラクタ //@} + DlgImpl( UINT id ); + ~DlgImpl(); + + //@{ 子アイテムID→HWND変換 //@} + HWND item( UINT id ) const; + + //@{ アイテムに対してメッセージ送信 //@} + LRESULT SendMsgToItem( UINT id, UINT msg, WPARAM wp=0, LPARAM lp=0 ); + + //@{ アイテムに対してメッセージ送信(ポインタ送る版) //@} + LRESULT SendMsgToItem( UINT id, UINT msg, void* lp ); + + //@{ アイテムに対してメッセージ送信(文字列送る版) //@} + LRESULT SendMsgToItem( UINT id, UINT msg, const TCHAR* lp ); + + // てけとーに実装して反応してください。 + // on_ok/on_cancelは、終了して良いならtrueを返すこと。 + // on_cmd/on_msgは、処理済みならtrueを返すこと。 + virtual void on_init(); + virtual void on_destroy(); + virtual bool on_ok(); + virtual bool on_cancel(); + virtual bool on_command( UINT cmd, UINT id, HWND ctrl ); + virtual bool on_message( UINT msg, WPARAM wp, LPARAM lp ); + virtual bool PreTranslateMessage( MSG* msg ); + +private: + + static BOOL CALLBACK MainProc( HWND, UINT, WPARAM, LPARAM ); + +private: + + dlgtype type_; + UINT endCode_; + const UINT rsrcID_; +}; + + + +//------------------------------------------------------------------------- +#ifndef __ccdoc__ + +inline DlgImpl::dlgtype DlgImpl::type() const + { return type_; } + +inline UINT DlgImpl::endcode() const + { return endCode_; } + +inline HWND DlgImpl::item( UINT id ) const + { return ::GetDlgItem( hwnd(), id ); } + +inline LRESULT DlgImpl::SendMsgToItem + ( UINT id, UINT msg, WPARAM wp, LPARAM lp ) + { return ::SendDlgItemMessage( hwnd(), id, msg, wp, lp ); } + +inline LRESULT DlgImpl::SendMsgToItem( UINT id, UINT msg, void* lp ) + { return ::SendDlgItemMessage( hwnd(), id, msg, 0, + reinterpret_cast(lp) ); } + +inline LRESULT DlgImpl::SendMsgToItem( UINT id, UINT msg, const TCHAR* lp ) + { return ::SendDlgItemMessage( hwnd(), id, msg, 0, + reinterpret_cast(lp) ); } + + + +//========================================================================= + +#endif // __ccdoc__ +} // namespace ki +#endif // _KILIB_WINDOW_H_ ADDED kilib/winutil.cpp Index: kilib/winutil.cpp ================================================================== --- kilib/winutil.cpp +++ kilib/winutil.cpp @@ -0,0 +1,54 @@ +#include "stdafx.h" +#include "app.h" +#include "winutil.h" +using namespace ki; + + + +//========================================================================= + +Clipboard::Clipboard( HWND owner, bool read ) + : opened_( false ) +{ + if( ::OpenClipboard(owner) ) + if( read || ::EmptyClipboard() ) + opened_ = true; + else + ::CloseClipboard(); +} + +Clipboard::~Clipboard() +{ + if( opened_ ) + ::CloseClipboard(); +} + +Clipboard::Text Clipboard::GetUnicodeText() const +{ + if( app().isNT() ) + { + // NTなら直接Unicodeでとれる + HANDLE h = GetData( CF_UNICODETEXT ); + if( h != NULL ) + { + unicode* ustr = static_cast( ::GlobalLock( h ) ); + return Text( ustr, Text::GALLOC ); + } + } + else + { + // 9xなら変換が必要 + HANDLE h = GetData( CF_TEXT ); + if( h != NULL ) + { + char* cstr = static_cast( ::GlobalLock( h ) ); + int Lu = ::lstrlenA( cstr ) * 3; + unicode* ustr = new unicode[Lu]; + ::MultiByteToWideChar( CP_ACP, 0, cstr, -1, ustr, Lu ); + ::GlobalUnlock( h ); + return Text( ustr, Text::NEW ); + } + } + + return Text( NULL, Text::NEW ); +} ADDED kilib/winutil.h Index: kilib/winutil.h ================================================================== --- kilib/winutil.h +++ kilib/winutil.h @@ -0,0 +1,145 @@ +#ifndef _KILIB_WINUTIL_H_ +#define _KILIB_WINUTIL_H_ +#include "types.h" +#include "memory.h" +#include "ktlaptr.h" +#ifndef __ccdoc__ +namespace ki { +#endif + + + +//========================================================================= +//@{ @pkg ki.WinUtil //@} +//@{ +// クリップボード管理 +// +// OpenClipboard や CloseClipboard 辺りの呼び出しを適当に自動化します。 +//@} +//========================================================================= + +class Clipboard : public Object +{ +public: + + //@{ 開く //@} + Clipboard( HWND owner, bool read=true ); + + //@{ 閉じる //@} + ~Clipboard(); + + //@{ データ読み込み //@} + HANDLE GetData( UINT uFormat ) const; + + //@{ 指定フォーマットのデータがクリップボード上にあるか? //@} + bool IsAvail( UINT uFormat ) const; + + //@{ 指定フォーマットのデータがクリップボード上にあるか?(複数) //@} + bool IsAvail( UINT uFormats[], int num ) const; + + //@{ テキスト情報保持クラス //@} + class Text { + friend class Clipboard; + + mutable unicode* str_; + enum Tp { NEW, GALLOC } mem_; + + Text( unicode* s, Tp m ) : str_(s), mem_(m) {} + void operator=( const Text& ); + + public: + Text( const Text& t ) + : str_(t.str_), mem_(t.mem_) { t.str_=NULL; } + ~Text() + { + if( str_ != NULL ) + if( mem_==NEW ) delete [] str_; + else GlobalUnlock( str_ ); + } + const unicode* data() const { return str_; } + }; + + //@{ テキスト読み込み //@} + Text GetUnicodeText() const; + + //@{ データ書き込み //@} + bool SetData( UINT uFormat, HANDLE hData ); + + //@{ 独自フォーマットの登録 //@} + static UINT RegisterFormat( const TCHAR* name ); + +public: + + //@{ 正常に開かれているかチェック //@} + bool isOpened() const; + +private: + + bool opened_; + +private: + + NOCOPY(Clipboard); +}; + + + +//------------------------------------------------------------------------- + +inline bool Clipboard::isOpened() const + { return opened_; } + +inline HANDLE Clipboard::GetData( UINT uFormat ) const + { return ::GetClipboardData( uFormat ); } + +inline bool Clipboard::SetData( UINT uFormat, HANDLE hData ) + { return NULL != ::SetClipboardData( uFormat, hData ); } + +inline bool Clipboard::IsAvail( UINT uFormat ) const + { return false!=::IsClipboardFormatAvailable(uFormat); } + +inline bool Clipboard::IsAvail( UINT uFormats[], int num ) const + { return -1!=::GetPriorityClipboardFormat(uFormats,num); } + +inline UINT Clipboard::RegisterFormat( const TCHAR* name ) + { return ::RegisterClipboardFormat(name); } + + + +//========================================================================= +//@{ +// 排他制御 +// +// 名前付きMutexを扱います +//@} +//========================================================================= + +class Mutex : public Object +{ +public: + Mutex( const TCHAR* name ); + ~Mutex(); + +private: + const HANDLE mtx_; + +private: + NOCOPY(Mutex); +}; + + + +//------------------------------------------------------------------------- + +inline Mutex::Mutex( const TCHAR* name ) + : mtx_( ::CreateMutex( NULL, TRUE, name ) ) {} + +inline Mutex::~Mutex() + { if( mtx_ != NULL ) ::ReleaseMutex( mtx_ ), ::CloseHandle( mtx_ ); } + + + +//========================================================================= + +} // namespace ki +#endif // _KILIB_WINUTIL_H_ ADDED release/readme.en.txt Index: release/readme.en.txt ================================================================== --- release/readme.en.txt +++ release/readme.en.txt @@ -0,0 +1,228 @@ + + +=<> +=<> GreenPad ver 1.08+ +=<> 2008/07/11 + + +<> + + GreenPad is a tiny text editor for Windows. + + It aims to be a handy Notepad replacement with minimal but + complete features, not to be a rich, bloated monsterous + editor. GreenPad supports: + * Unicode 2.0 + * Proportional Fonts + * Syntax Highlighting + * Searching with Regular Expressions + while keeping the size of .exe very small (around 50KB!). + + Freeware, distributed under the NYSL licence. + The source code is available at: http://www.kmonos.net/lib/gp.en.html + + +<> + + * Fixed: Several potential access violations bugs + * Partial support for surrogate pairs (proper rendering and carret moves, + reading/writing UTF-32 and UTF-8 text beyond BMP). + * Changed the tab-order of the Find/Replace dialog. + * Changed the behavior of [Home] and [End] key to be more compatible with NotePad. + +<> + + Ctrl+R Reopen + Shift+Ctrl+S Save as... + Ctrl+Y Redo + F5 Insert date & time + + Ctrl+F Find + F3 Find next + Shift+F3 Find prev + Ctrl+H Replace + Ctrl+J Jump to line # + Ctrl+G Grep + + Ctrl+1 No Wrapping + Ctrl+2 Wrap by specified width + Ctrl+3 Wrap by the size of window + + Ctrl+Up Curosr Up 3 lines + Ctrl+Dn Curosr Down 3 lines + + and Windows-common shortcuts ( Ctrl+S to save, Ctrl+C to copy, ... ) + + +<> + + * How to change fonts and colors ? + + In the types/ dir, you'll see some .lay files. Please edit them manually. + ========================================================= + ct=Color of Text (RGB) + ck=Color of Keyword + cb=Color of BackGround + cc=Color of ControlCharactor + cn=Color of commeNt + cl=Color of Line no. + ft=FonT name + sz=font SiZe + tb=TaB width + sc=11000 + wp=WraP type (-1: no wrap 0: right edge 1: ww chars ) + ww=Wrap Width + ln=show LineNo. + ========================================================= + Year, editing manually, is very inconvinient. + I'll create GUI configurator someday ... + + * How to create syntax hilighting modes? + + Write you own .kwd files and put them into the types/ directory. + The format of .kwd files is as follows: + ========================================================= + 1111 # Four Boolean Flags, 0:false 1:true (explained later) + /* # beginning symbol for block-comments + */ # ending symbol for block-comments + // # beggining symbol for one-line comments + auto # the list of keywords follows... + bool + _Bool + break + case + ... + ========================================================= + The meanings of the four flags are, from left to right: + - CaseSensitive (if set to 1, keywords are treated as case-sensitive.) + - EnableSingleQuotation (if set to 1, keywords inside '...' is not highlighted.) + - EnableDoubleQuoatation (if set to 1, keywords inside "..." is not highlighted.) + - EnableEscapeSequences (if set to 1, "..\".." is teated as a single string.) + Usually, 0111 or 1111 is recommended. + + * Which regular expressions can be used? + + Here is the complete list of the regular expressions available in GreenPad: + ========================================================= + quanitification: + ? : 0 or 1 + * : 0 or more + + : 1 or more + + alternation: + a|b + + grouping(parentheses) + + special escape characters: + \t : tab + \\ : '\' itself + \[ : '[' + + positional match: + ^ : start of line + $ : end of line + + character classes: + [abc] : matches a single character 'a', 'b', or 'c' + [^abc] : matches any single character other than 'a', 'b', or 'c' + [d-h] : matches 'd', 'e', 'f', 'g', 'h' + \w : [0-9a-zA-Z_] + \W : [^0-9a-zA-Z_] + \d : [0-9] + \D : [^0-9] + \s : [\t ] + \S : [^\t ] + ========================================================= + There are some limitations: + * GreenPad does searching line by line, thus + you cannot search "aaa\nbbb" or something like it. + * No forward/backward references. + * No shortest matches (every * is greedy) + + * External Grep Program ? + + You can enter some GUI grep program here. For example, + C:\Software\Gj\GrepJuice.exe "%D" + is set in my environment. + %D is automatically replaced by the current directory + %F is replaced with the full path of the current file. + %N is replaced with the name (without path info) of the current file. + + * Command Line Options ? + + greenpad ([-l LineNumber] [-c CharacterSet] filename)* + + For example: + greenpad -l543 -c932 aaaa.txt + opens a file named "aaaa.txt" assuming the Shift_JIS encoding, + and brings its 543rd line to the view area. CharacterSet number + supported by default is: + iso-8859-1 = -1 + UTF5 = -2 + UTF8 = -65001 + UTF16BE = -5 + UTF16LE = -6 + UTF32BE = -9 + UTF32LE = -10 + If you have installed "Language Support" for your Windows, + the character sets of installed languages become + readable/writable in GreenPad. You should consult with + the "area and language option" control panel to get the + CharacterSet numbet for those languages. Note however that + for some east asian encodings, special CharacterSet numbers + are assigned for a technical reason. + EUC-JP = -932 + iso-2022-jp = -933 + iso-2022-kr = -950 + iso-2022-cn = -936 + GB2312 = -937 + + * How to share GreenPad's configurations between users of same machine? + + Usually, GreenPad saves and loads its configuration for each + machine user account. However, sometimes you want to use only one + setting for one GreenPad.exe. (for example, when you have GreenPad + in an emergency floppy disk and log in different users accounts.) + + In this case, you should add the following two lines to GreenPad.ini + file: + [SharedConfig] + Enable=1 + then GreenPad will be executed in user-independent-settings-mode. + + +<> + + * The icon image of GreenPad is the work of + SB( http://homepage3.nifty.com/scriba/ ). Thanks. + + +<> + + NYSL Version 0.9982 http://www.kmonos.net/nysl/ + + A. This software is "Everyone'sWare". It means: + Anybody who has this software can use it as if you're + the author. + + A-1. Freeware. No fee is required. + A-2. You can freely redistribute this software. + A-3. You can freely modify this software. And the source + may be used in any software with no limitation. + A-4. When you release a modified version to public, you + must publish it with your name. + + B. The author is not responsible for any kind of damages or loss + while using or misusing this software, which is distributed + "AS IS". No warranty of any kind is expressed or implied. + You use AT YOUR OWN RISK. + + C. Copyrighted to k.inaba. + + D. Above three clauses are applied both to source and binary + form of this software. + + +--------------------------------------------------------------------------- + by k.inaba( http://www.kmonos.net/ ) ADDED release/readme.txt Index: release/readme.txt ================================================================== --- release/readme.txt +++ release/readme.txt @@ -0,0 +1,580 @@ + +=<> +=<> GreenPad ver 1.08+ +=<> 2008/07/11 + + +<<これは何?>> + + Windows上で動作する、テキストエディタです。 + なんでもできる高機能エディタではなく、メモ帳の代わりとして + 使えるような、最低限の機能は備えつつ高速に動作する簡易テキ + ストエディタを目指して開発されています。 + + NYSLライセンスに従うフリーウェアです。ソースコードが + http://www.kmonos.net/lib/gp.html + で公開されています。 + + 最新版での更新内容は、 + ・S版で「開く」「保存」ダイアログが出なくなっていたバグ修正 + ・タイトルバーをクリックしたときシステムメニューが出ないバグ修正 + ・環境によってステータスバーにカーソル位置表示が出ないバグ修正 + です。 + + +<<1.08+ での更新内容>> + + * A版が起動しなくなっていたバグを修正 + (U版には変更ありません) + + +<<1.08 での主な更新内容>> + + * メモリアクセス違反で強制終了するバグを何カ所か修正 + * サロゲートペアに少し対応(描画やカーソル移動でサロゲートペアを分解しないようになった + && UTF-32 と UTF-8 ファイルのBMP外文字の読み書きに対応) + 注: BMP外の文字は、対応するフォント(XANO明朝 等)を指定した時のみ描画されます。 + * 検索/置換ダイアログのタブ順変更 + * Home/End キーを論理行ではなく表示行単位の動作に変更 + + +<<特徴>> + + ・内部処理はUnicode + ・プロポーショナルフォントが使える + ・キーワード色分けが出来る + ・文字コードはいわゆる SJIS, EUC, JIS, UTF8, UTF7, UTF16 などの + 他にも、Windowsがサポートしていればそれに合わせて色々対応 + ・改行コードは CRLF/CR/LF に対応 + ・出来る限りの無限Undo可能 + ・ショボめの正規表現検索機能内蔵 + ・exeのサイズがわりと小さい + ・選択時に行と行の間に隙間が見えるのは作者の趣味 + + +<<使い方>> + + 基本的な使い方は、Windowsの標準的なテキスト編集方法と + 同じです。適当に勢いで使ってください。 + + Windowsの一般的なものではないかもしれないキーボード + ショートカットは次の通りです。 + | + | Ctrl+R 開き直す + | Shift+Ctrl+S 別名保存 + | + | Ctrl+Y Redo + | F5 日時の挿入 + | + | Ctrl+F 検索 + | Ctrl+H 置換 + | Ctrl+J ジャンプ + | F3 次を検索 + | Shift+F3 前を検索 + | Ctrl+G Grep + | + | Ctrl+1 折り返しなし + | Ctrl+2 指定幅折り返し + | Ctrl+3 右端折り返し + | + | Ctrl+↑ 3行単位でカーソル移動 + | Ctrl+↓ 3行単位でカーソル移動 + | + | Shift+変換 選択範囲を再変換 + | Ctrl+BS 確定取消 + + 検索には正規表現がちょびっと使えます。 + 対応している正規表現は以下のとおりです。 + | + | c 普通の字は、その文字自身を意味する + | \t タブ文字 + | \w [0-9a-zA-Z_] と同義。英数字 + | \W [^0-9a-zA-Z_] と同義。英数字以外 + | \d [0-9] と同義。数字 + | \D [^0-9] と同義。数字以外 + | \s [\t ] と同義。空白文字 + | \S [^\t ] と同義。空白文字以外 + | \その他 \\ なら \、\[ なら [ という一文字 + | ^ 行の先頭 + | $ 行の末尾 + | . 任意の一文字 + | [...] [] 内に含まれる任意の1文字。A-Z や あ-ん のような範囲指定も可。 + | [^...] [] 内に含まれない任意の1文字。範囲指定も可。 + | + | r* 正規表現 r を0回以上繰返したもの + | r+ 正規表現 r を1回以上繰返したもの + | r? 正規表現 r の0回または1回 + | r1r2 正規表現 r1 に続いて r2 が現れることを表す + | r1|r2 正規表現 r1 または r2 を表す + | (r) 正規表現 r を表す. + | + | 例 + | 第[1-9]号 "第1号"とか"第2号"とかにマッチ + | <[^>]+> XMLのタグとかにマッチ + | (。|!|?)$ 行末の。か!か?にマッチ + | + | 前方参照とか最短一致とか置換に\1とかそういったことは、 + | 残念ながら一切できません。改行を挟んだ検索などもでき + | ません。ゴメンナサイ。 + + +<> + + ・どうやってフォントとかを変えればよいの? + | 下のほうの*.kwdや*.layの書式に従って、設定ファイルを編集してください。 + | 機会があったら設定変更画面も用意しますので…スミマセン + + ・Unicode対応なはずなのに日本語以外の字が表示できないよ? + | 対応するフォントが必要です。Unicode 2.0をフルに + | 使えるフォントとしては MS Office 付属の "Arial Unicode" や、 + | "BDF UM+" ( http://www.kaoriya.net/#FONT ) など。 + | メニューの「文書タイプ」>「UnicodeText」を選ぶと、 + | Arial Unicode を使ってテキストを表示します。その他のUnicodeフォントは、 + | GreenPad フォルダの types\unicode.lay を書き換えるなどしてご利用下さい。 + + ・右から左に書くタイプの言語がうまく表示できない。 + | ごめんなさいGreenPadでは無理です。 + + ・設定の「外部Grepプログラム」欄には何を入れればよいのでしょう? + | "Grep"用のプログラムがインストールされていると、ここに + | 入力しておくことでGreenPadから起動できます。例として私の環境では + | C:\Software\Getia\getia.exe "%D" + | となっています。外部ツールの起動時に、 + | %D は「現在開いているファイルのあるフォルダ」 + | %F は「現在開いているファイルのフルパス名」 + | %N は「現在開いているファイルの名前部分」 + | に置き換わります。ちなみに作者のおすすめGrepプログラムは + | getia (http://site-clue.statice.jp/soft_getia.php) + | です。 + + ・コマンドラインオプションは? + | -l : 行番号指定 + | -c : 文字コード指定 + | の2つがあります。 + | + | greenpad -l543 -c932 aaaa.txt + | で、強制的にShiftJISで、aaaa.txtを開き、 + | 543行目を一番上にした状態で起動します。 + | + | greenpad aaaa.txt -l543 -c932 + | ではダメです。オプションはファイル名より前に。 + | + | greenpad -l123 aaaa.txt -l456 bbbb.txt + | とやって2つのファイルを開くことは可能 + | + | -cの後ろの数字は、下にあげる"文字コード番号"を指定します。 + + ・文字コード番号? + | WinXPならコントロールパネルの + | 「言語と地域のオプション」の「詳細設定」の「コードページ + | 変換テーブル」の数字を指定するとだいたい上手く行くと思います。 + | ただし、一部のコードにはGreenPad内で特別に負の数を割り振って + | いますので、以下の値を使ってください。 + | + | SJIS = 932, // 日本語1 (Shift_JIS) + | EucJP = -932, // 日本語2 (日本語EUC) + | IsoJP = -933, // 日本語3 (ISO-2022-JP) + | + | Western = -1, // 欧米 (Windows1252 >> ISO-8859-1) + | IsoKR = -950, // 韓国語2 (ISO-2022-KR) + | IsoCN = -936, // 中国語2 (簡体字 ISO-2022-CN) + | HZ = -937, // 中国語3 (簡体字 HZ-GB2312) + | UTF5 = -2, // Unicode (UTF-5) : BOM無し + | UTF8 =-65001,// Unicode (UTF-8) : BOM有り + | UTF16b = -3, // Unicode (UTF-16) : BOM有り BE + | UTF16l = -4, // Unicode (UTF-16) : BOM有り LE + | UTF16BE = -5, // Unicode (UTF-16BE): BOM無し + | UTF16LE = -6, // Unicode (UTF-16LE): BOM無し + | UTF32b = -7, // Unicode (UTF-32) : BOM有り BE + | UTF32l = -8, // Unicode (UTF-32) : BOM有り LE + | UTF32BE = -9, // Unicode (UTF-32BE): BOM無し + | UTF32LE = -10, // Unicode (UTF-32LE): BOM無し + + ・キーワードファイル (*.kwd) の書き方は? + | + | UCS-2をLittleEndianでベタ書きしたファイルでなければならない。 + | 改行コードはなんでもいいけど LF にすると若干高速に読めるかも。 + | BOMはあってもなくても可。つけておくことを推奨。 + | + | 1行目: flag ========================== + | + | 0 or 1 が4つ並ぶ。順に、1ならば + | ・ キーワードは大文字小文字を区別 + | ・ '' の中ではコメント記号は無効 + | ・ "" の中ではコメント記号は無効 + | ・ \ 記号によるエスケープを行う + | を意味する。 + | + | 2行目: ブロックコメント開始記号 ====== + | + | /* とかそんな風に書く。 + | ブロックコメントを使わないなら空行にしておく。 + | + | 3行目: ブロックコメント終了記号 ====== + | + | */ とかそんな風に書く。 + | ブロックコメントを使わないなら空行にしておく。 + | + | 4行目: 行コメント開始記号 ============ + | + | // とかそんな風に書く。 + | 行コメントを使わないなら空行にしておく。 + | + | 5行目以降: キーワード ================ + | + | 強調表示したい単語をひたすら書き連ねる + | + | ※ キーワードとして使えるのは、 + | ※ 数字、アルファベット(大小)、下線 + | ※ からなる単語のみ。border-width とか #include とかはダメ。 + | ※ __int64 などは可。 + | + | ※ コメント記号としては、 + | ※ キーワードに使える文字・空白文字 + | ※ を除いたASCII文字のみが使える。 begin とかはダメ。 + + ・レイアウトファイル (*.lay) の書き方は? + | UCS-2をLittleEndianでベタ書きしたファイルでなければならない。 + | 任意の文字コードを使えるように変更することもすぐに可能なので、 + | 気が向いたら変えるかもしれない。ので、BOMはつけておくことを推奨。 + | + | ct=文字色 + | ck=キーワード色 + | cb=背景色 + | cc=コメント色 + | cn=EOFや改行マークの色 + | cl=行番号の色 + | ft=フォント名 + | sz=フォントサイズ + | tb=タブ幅 + | sc=特殊文字を表示するなら1(左から順に、EOF,改行,タブ,半角空白,全角空白) + | wp=折り返し方式(-1:無し 0:右端 1:指定文字数) + | ww=折り返し文字数(wp=1の時に利用) + | ln=行番号表示(1:ON 0:OFF) + | + | 項目を省略すると、その項目に関しては default.lay の + | 内容が読み込まれる。default.lay も無いときは exe 内に + | 内蔵してあるデフォルトの設定が読み込まれる。 + + ・初期設定ファイル (*.ini) の書き方は? + | 設定ダイアログで設定するとここに書き込まれます。 + | Windowsのごく普通のINIファイル。ANSI-CP (日本ならShift_JIS)+CRLF で + | 書かねばならない。例として、次のような感じになる。 + | + | ------------------------------------------------------------------ + | [ユーザー名] + | UndoLimit= // アンドゥ回数制限。-1 なら制限無し + | TxtFilter= // [開く]ダイアログ用のフィルタ。*.xxx の形で、; で区切ること + | OpenSame= // 同じウインドウで開くなら1、でなければ0。 + | CountUni= // 俗に言う半角全角を区別せず、横の桁数を数える + | GrepExe= // Grep用に使う外部アプリ起動のコマンドを入れる。 + | // %1があればそこをカレントフォルダ名に置き換える。 + | MRU= // [最近開いたファイル] に表示する個数。最大8個 + | MRU#= // [最近開いたファイル] を保存 + | SearchIgnoreCase= // 検索の時大文字小文字の違いを無視するかどうか + | SearchRegExp= // 正規表現検索するかどうか + | NewfileCharset= // 新規作成したファイルの文字コード + | NewfileLB= // 新規作成したファイルの改行コード + | NewfileDoctype= // 新規作成したファイルの文書タイプ + | RememberWindowSize= // ウインドウサイズを再開時に再現するなら1 + | RememberWindowPos= // ウインドウ位置を再開時に再現するなら1 + | WndX= + | WndY= + | WndW= + | WndH= + | WndM= // 記憶されたウインドウサイズや位置 + | + | [DocType] + | 1= + | 2= // 文書タイプの名前を列挙。連番なら幾つまで並べてもOK + | + | [文書タイプ名] + | Pattern= // この文書タイプを自動適用するファイル名を正規表現で + | // (ファイル名全体にマッチするように) + | Keyword= // キーワードファイルの名前 + | Layout= // レイアウトファイルの名前 + | ------------------------------------------------------------------ + | + | ユーザーごとに設定を変えたりしたくない場合は、次のように、 + | SharedConfigというセクションと、Enable=1 という行を書いておく。 + | + | ------------------------------------------------------------------ + | [SharedConfig] + | Enable=1 + | ------------------------------------------------------------------ + | + | こうすると、設定項目はすべてこのSharedConfigセクションから読み書き + | されるようになる。設定済みGreenPadをiniと合わせて色々な環境へ + | 持ち運びたいときに便利。 + + +<<謝辞>> + + ・アイコンはSB氏 ( http://homepage3.nifty.com/scriba/ ) に + 制作していただきました。感謝。 + + +<<更新履歴>> + + 2008/04/29 【ver 1.08】 +  ・再変換しようとすると強制終了するバグを修正 + 2008/04/17 +  ・U版ではメモリ管理をWindowsに完全に任せるように変更 +  ・検索文字列が「見つかりませんでした」ダイアログをモーダルにしました + 2008/04/05 【ver 1.08 beta】 + ・強制終了するバグをいくつか修正 + ・BMP外の文字(サロゲートペアで表現される文字)にごまかしごまかし対応 + ・新たに、UTF-32 の読み書き、Win95 での UTF-8 の読み書きでBMP外の文字をサポート + ・カーソル移動がちゃんとサロゲートペアを1文字として扱うようにしました + ・描画時の文字幅もちゃんとなったはず + ・検索ダイアログのタブ移動順を変更 + ・Home/End キーでは物理行ではなく表示行単位の移動としました + 2006/12/04 【ver 1.07.4】 + ・ISO-8859-1 が標準文字コードの環境で、保存時にファイルが消える致命的なバグ修正 + 2006/11/30 + ・Win95で動作するように修正 + 2006/11/25 【ver 1.07.3】 + ・英語版で Save As... メニューのショートカットが変だったのを修正 + ・時々文字が描画されないことがある問題に対処 + 2006/11/22 【ver 1.07.2】 + ・Win98 でウインドウ位置やサイズを保存できてなかったバグ修正 + 2006/11/17 【ver 1.07.1】 + ・16x16 のアイコンを入れるようにした + ・コンパイル/リンクオプション調整 + 2006/11/13 + ・デフォルト設定の、Javaモード判定用正規表現が間違ってたので修正 + ・内部文字列バッファはWORD整列してメモリ確保するよう修正 + 2006/11/09 【ver 1.07】 + ・Undoと文字入力の組み合わせによって、保存ができないタイミングがあったバグ修正 + ・バージョン情報の「ファイル名」を「GreenPad」に変更 + ・スクロールバーの右クリックのメニューの"最上部"、"最下部"が動くように修正 + ・正規表現検索でスタック食いつぶして落ちることはないように修正 + ・外部Grep起動コマンドに %D(ディレクトリ名), %F(フルパス), %N(ファイル名) + を使えるようにした。従来の%1(ディレクトリ名)も互換性のために残してあります。 + ・改行がないファイルの保存時改行コードは、新規ファイルの場合にそろえるように修正 + ・中国語/韓国語環境では、EUC-JPの自動判定を行わないようにした(GBやUHCの誤判定防止) + ・添付予約語ファイルをいくつか追加 + 2005/11/27 【ver 1.06.2】 + ・キリル語、ギリシャ語、中央ヨーロッパ語として使うコードページを修正 + ・日韓中以外の言語環境でも、ANSIコードページをデフォルトとして使うように変更 + 2005/10/23 【ver 1.06.1】 + ・S版で「開く」「保存」ダイアログが出なくなっていたバグ修正 + ・タイトルバーをクリックしたときシステムメニューが出ないバグ修正 + ・環境によってステータスバーにカーソル位置表示が出ないバグ修正 + 2005/10/21 + ・S版で、外部Grepを呼び出せなくなっていたバグ修正 + 2005/10/20 【ver 1.06】 + ・前回終了時のウインドウの位置とサイズを復元する機能を追加 + ・d.kwdを最新の言語仕様に追従。その他kwdファイルの整理 + 2005/10/18 + ・新規ファイルのデフォルト文字コードや文書タイプの設定を追加 + ・iniファイルに、ユーザ毎に設定を分けないSharedConfigモード追加 + ・Windows x64 版対応 + ・MinGW でリソース以外はコンパイル可能にした + ・DigitalMars C++ でちゃんとコンパイル可能にした + 2005/05/26 + ・HALさんのdelphi/asm/b2e用kwdを添付。 + 2005/03/11 + ・U版でファイルの保存時に、指定より一個親のフォルダに + 保存してしまうことがあったバグを修正。 + 2004/07/29 【ver 1.05】 + ・Windows XP SP2 の Execution Protection 下でも実行できるように修正したつもり。 + ・開いているファイルのあるディレクトリを外から削除できるように修正。 + ・日本語/英語以外のファイル名も「最近のファイル」で扱えるようにした。 + 2004/06/20 + ・全置換をUndoする際にも↓と同じ問題が残っていたので修正。 + ・HALさんによるdelphi用のkwd/layを添付。 + 2004/06/13 + ・改行を削除して複数行を繋げた際に行番号表示がおかしくなるバグ修正 + ・[開く]や[保存]ダイアログで文字コード関係のボックスにTABキーで移動可能にした。 + ・全置換の際にカーソル位置の更新をステータスバーに毎回反映してて + 遅かったバグを修正(つまり、ステータスバーの更新頻度を下げた。) + 2003/09/27 + ・カーソル上下移動時の右寄せを左寄せに変えた + 2003/05/13 【ver 1.04.2】 + ・関係ないメニューでステータスバーON/OFFしてしまうバグ修正…(^^;; + 2003/05/09 【ver 1.04】 + ・ファイル履歴最大を20件にしたつもりが7件だったバグ修正。 + ・横位置を文字数で表示モードでない場合、TABの扱いが不自然だったバグ修正。 + ・ステータスバーON/OFF + ・WM_COPY, WM_CUT, WM_PASTE, WM_UNDO, EM_UNDO, EM_CANUNDO, EM_SETREADONLY + に反応するようにしたつもり。肝心なGETSEL/SETSEL系はチト面倒いので後回し。 + 2003/02/10 + ・BDF UM+ ( http://www.kaoriya.net/#FONT ) というフォントがあるそうです。 + 2003/02/08 【ver 1.03】 + ・ファイル履歴が2件までしか記録出来なくなってたバグ修正。 + ・Arial Unicode が自由にダウンロードできなくなったらしいので、 + Unicodeモードのフォントを Bitstream Cyberbit に変えようとしたところ、 + こちらも有料になってしまったらしい。他にフリーのUnicode2.0フルの + フォントなんて知らないよー。(T_T) + 2003/01/21 + ・UTF-8判別ルーチンの強化 + 2003/01/18 + ・TAB幅調整 + ( これ↓のWin2k以前の仕様に対処する方法が思いつかない。。。 ) + ( http://www.microsoft.com/japan/msdn/windows/windowsxp/FixedPitchFont.asp ) + 2002/12/21 + ・検索ダイアログ開きっぱなしモード(改) + 2002/12/14 【ver 1.02】 + ・検索ダイアログ開きっぱなしモード + ・検索ダイアログの設定保存 + ・選択中文字列を検索ダイアログへ反映 + ・選択範囲再変換機能搭載 + 2002/12/09 + ・英語版ReadMeを書いてみた。 + 2002/12/08 + ・設定項目の中に両端に""がある文字列を正しく保存出来ないバグ修正 + ・GreenPad.iniを開くとGreenPad.iniの末尾にゴミがくっつくことがある問題修正 + >読み込みオープン中のファイルをWritePrivateXXXで弄るとマズかったみたい。 + ・行番号色変更を可能にした。 + ・外部Grepの起動フォルダが正しくないことがあったバグ修正 + 2002/09/22 【ver 1.01】 + ・Win95で通常の検索が出来ないバグ修正 + ・アイコン変更 + ・C++の文書タイプ用パターンの修正 + ・perl/php/css用のキーワードファイルを標準添付 + 2002/07/26 【ver 1.00】 + ・文字数カウントをSJISでのバイト数でも行えるように。 + 2002/07/20 + ・WinNT4でも実行時に変化するメニュー項目が壊滅してたのを修正。 + 2002/07/17 + ・Win95で、実行時に変化するメニュー項目が全滅してたのを修正。 + ・Win95で、UTF-8の読み書き、UTF-7の書きが出来なくなってたのを修正。 + 2002/07/15 + ・Win9x系で全くファイルを開けなくなってたのを修正 + 2002/07/14 + ・排他処理をゆるくした + ・[最近のファイル] メニューをようやく実装 + 2002/07/11 + ・「同じウィンドウで開く」実装 + ・ステータスバーに改行コードを表示するようにした + 2002/07/09 + ・正規表現の\wと\Wが逆だったのを修正 + ・Win9xでのコピー&ペーストが致命的にバグってたのを修正 + ・メニューのチェック表示をラジオボタン的に + ・GreenPad.iniの改行コードがCRのみになっちゃうバグ修正 + 2002/07/06 + ・設定ダイアログ完成。 + ・RC1に向け色々整備開始。 + 2002/07/02 + ・文書タイプ部分の設定ダイアログとかiniへの保存とか。 + 2002/07/01 + ・設定ダイアログの共通項目保存部分完成。あとは文書タイプ。 + 2002/06/30 + ・設定ダイアログ作り始め + ・外部grep呼び出し機能追加。 + 2002/06/29 + ・英語版リソース追加 + 2002/06/24 + ・行頭を表す正規表現^が正しく働いてなかったので修正 + 2002/06/19 + ・layout変更の際にカーソルサイズを変更してなかったバグ修正 + 2002/06/17 + ・Ctrl+Tab / Ctrl+Shift+Tab に対応 + ・ステータスバーにカーソル座標を表示するようにした + 2002/06/05 + ・正規表現検索の方も完成。 + 2002/06/04 + ・文書タイプ選択でのRegExp利用版を完成させる。 + 2002/06/03 + ・正規表現のマッチングルーチン暫定版完成。 + 2002/06/01 + ・「見つかりませんでした」を出すようにした。 + ・上書き保存時に文書タイプが戻ってしまうバグ修正 + ・文書タイプメニューのチェックマーク位置が正しくなるように修正 + ・タイプ選択ルーチンを暫定的に拡張子マッチングにしてみた。 + 2002/05/23 + ・検索メニューがグレーアウトしてたバグ修正 + ・置換機能実装。 + ・全置換も実装。あとは、見つからなかったときのメッセージを出すとかやらないと。 + 2002/05/21 + ・単純な前後検索(CaseIgnoreも可)は実装できた。 + [前を検索]などのUIはも少し考える必要がありそうだ。 + 2002/05/18 + ・[検索]用のフレーム作成。単純検索は、あとはカーソルとの連携のみ。 + ・この版からCVSを導入してみた。 + ・Gp本体部分のccdoc用ソースコメントもpkg化。 + 2002/05/06 + ・[開く][保存] ダイアログの初期ディレクトリが変だったので修正 + ・[表示>文書タイプ] メニュー + 2002/05/05 + ・[開く]メニューまわりのソースの再構成。少しすっきりした。 + ・開き直す、の時は文書タイプの読み直しを行わないようにした。 + ・Undoに回数制限をかけたときのdirty flagの動作が変だったので修正 + ・8.3形式で渡されても名前部分だけは内部でLFNに変換するようにした。 + ・MLU以外の項目を全てiniから読み出すようにした。ただし Pattern は + まだ読み出すだけで使っていない。ここには正規表現を使いたいので。 + ・テスト用にb2e.kwdとini.kwdを作ってみたりする。 + 2002/05/04 + ・grep。-l オプションにて、 + PGrep( http://osaka.cool.ne.jp/parasa/ ) + JGREP( http://www.hi-ho.ne.jp/jun_miura/jgrep.htm ) + GrepJuice( http://hp.vector.co.jp/authors/VA016609/ ) + の外部ビューワとして利用できることを確認。 + ・名前を付けて保存、のタイミングで文書タイプのReloadを行うようにした + ・DigitalMars の smake 用の makefile をまともなものに書き直し + 2002/05/03 + ・*.iniファイルの項目決定&読み込みルーチン作成 + ・検索とかのダイアログだけ作ってみた + ・Insertキーに反応するようにした。 + ・Ctrl+BackSpace で確定取り消し…というのをIMEが勝手に + やってくれるらしい。面白い。(^^) + ・-l## オプション(開くときに指定行番号へジャンプ)実装 + ・ついでに行番号ジャンプダイアログも実装 + ・F5で日時挿入機能実装 + 2002/04/28 + ・*.layファイルのフォーマット決定&読み込みルーチン作成 + ・c.kwd と html.kwd を書き直し + ・ソースにVC.NET用のプロジェクトファイルを追加 + こっちの方がexeのサイズが小さくなることが判明。(^^; + 2002/04/27 + ・4バイト以下のファイルを自動判定で読むとバグるのを修正 + ・未保存を表す * の表示制御を適切に行うようにした。 + ・文書タイプ別設定のフレームワークづくり + ・折り返し方法切り替えのメニューとかつけてみた + ・ウインドウへファイルをD&Dしたら開くようにした + 2002/04/25 + ・BM法による検索ルーチンを作ったけど取り込んでない。 + 2002/04/24: + ・[開く][保存]ダイアログにデフォルトで現在のファイル名が表示されるよーに。 + ・[開く][保存]ダイアログに「テキスト形式のファイル」って項目を。 + ・新規作成した時点では[保存]できなくなってたバグ修正。 + 2002/04/23 + ・[ファイル][編集] メニューのグレーアウト制御 + ・未保存ファイルを破棄しようとしてるときの問い合わせ処理全般 + ・ダブルクリックで単語選択 + ・Undo/Redoの回数制限機能 + 2002/04/22 + ・簡易クリップボード制御ライブラリをGpに統合 + ・Undo/Redo (回数無制限) + ・ファイル保存時のメモリ確保に関する致命的バグ修正 + ・単語が消える問題に、二重TextOutによる応急処置 + 2001/08/05 - 2002/04/21 + ・(記録が残っていません。残念。) + 2001/08/04 + ・開発スタート + + +<<ライセンス>> + + NYSL Version 0.9982 http://www.kmonos.net/nysl/ + + A. 本ソフトウェアは Everyone'sWare です。このソフトを手にした一人一人が、 + ご自分の作ったものを扱うのと同じように、自由に利用することが出来ます。 + + A-1. フリーウェアです。作者からは使用料等を要求しません。 + A-2. 有料無料や媒体の如何を問わず、自由に転載・再配布できます。 + A-3. いかなる種類の 改変・他プログラムでの利用 を行っても構いません。 + A-4. 変更したものや部分的に使用したものは、あなたのものになります。 + 公開する場合は、あなたの名前の下で行って下さい。 + + B. このソフトを利用することによって生じた損害等について、作者は + 責任を負わないものとします。各自の責任においてご利用下さい。 + + C. 著作者人格権は k.inaba に帰属します。著作権は放棄します。 + + D. 以上の3項は、ソース・実行バイナリの双方に適用されます。 + + +--------------------------------------------------------------------------- + by k.inaba( http://www.kmonos.net/ ) ADDED release/type/C#.kwd Index: release/type/C#.kwd ================================================================== --- release/type/C#.kwd +++ release/type/C#.kwd cannot compute difference between binary files ADDED release/type/C.kwd Index: release/type/C.kwd ================================================================== --- release/type/C.kwd +++ release/type/C.kwd cannot compute difference between binary files ADDED release/type/CSS.kwd Index: release/type/CSS.kwd ================================================================== --- release/type/CSS.kwd +++ release/type/CSS.kwd cannot compute difference between binary files ADDED release/type/D.kwd Index: release/type/D.kwd ================================================================== --- release/type/D.kwd +++ release/type/D.kwd cannot compute difference between binary files ADDED release/type/Delphi.kwd Index: release/type/Delphi.kwd ================================================================== --- release/type/Delphi.kwd +++ release/type/Delphi.kwd cannot compute difference between binary files ADDED release/type/Erlang.kwd Index: release/type/Erlang.kwd ================================================================== --- release/type/Erlang.kwd +++ release/type/Erlang.kwd cannot compute difference between binary files ADDED release/type/HTML.kwd Index: release/type/HTML.kwd ================================================================== --- release/type/HTML.kwd +++ release/type/HTML.kwd cannot compute difference between binary files ADDED release/type/Haskell.kwd Index: release/type/Haskell.kwd ================================================================== --- release/type/Haskell.kwd +++ release/type/Haskell.kwd cannot compute difference between binary files ADDED release/type/Java.kwd Index: release/type/Java.kwd ================================================================== --- release/type/Java.kwd +++ release/type/Java.kwd cannot compute difference between binary files ADDED release/type/JavaScript.kwd Index: release/type/JavaScript.kwd ================================================================== --- release/type/JavaScript.kwd +++ release/type/JavaScript.kwd cannot compute difference between binary files ADDED release/type/Lua.kwd Index: release/type/Lua.kwd ================================================================== --- release/type/Lua.kwd +++ release/type/Lua.kwd cannot compute difference between binary files ADDED release/type/OCaml.kwd Index: release/type/OCaml.kwd ================================================================== --- release/type/OCaml.kwd +++ release/type/OCaml.kwd cannot compute difference between binary files ADDED release/type/PHP.kwd Index: release/type/PHP.kwd ================================================================== --- release/type/PHP.kwd +++ release/type/PHP.kwd cannot compute difference between binary files ADDED release/type/Perl.kwd Index: release/type/Perl.kwd ================================================================== --- release/type/Perl.kwd +++ release/type/Perl.kwd cannot compute difference between binary files ADDED release/type/Python.kwd Index: release/type/Python.kwd ================================================================== --- release/type/Python.kwd +++ release/type/Python.kwd cannot compute difference between binary files ADDED release/type/Ruby.kwd Index: release/type/Ruby.kwd ================================================================== --- release/type/Ruby.kwd +++ release/type/Ruby.kwd cannot compute difference between binary files ADDED release/type/asm.kwd Index: release/type/asm.kwd ================================================================== --- release/type/asm.kwd +++ release/type/asm.kwd cannot compute difference between binary files ADDED release/type/b2e.kwd Index: release/type/b2e.kwd ================================================================== --- release/type/b2e.kwd +++ release/type/b2e.kwd cannot compute difference between binary files ADDED release/type/default.lay Index: release/type/default.lay ================================================================== --- release/type/default.lay +++ release/type/default.lay cannot compute difference between binary files ADDED release/type/html.lay Index: release/type/html.lay ================================================================== --- release/type/html.lay +++ release/type/html.lay cannot compute difference between binary files ADDED release/type/ini.kwd Index: release/type/ini.kwd ================================================================== --- release/type/ini.kwd +++ release/type/ini.kwd cannot compute difference between binary files ADDED release/type/program.lay Index: release/type/program.lay ================================================================== --- release/type/program.lay +++ release/type/program.lay cannot compute difference between binary files ADDED release/type/unitext.lay Index: release/type/unitext.lay ================================================================== --- release/type/unitext.lay +++ release/type/unitext.lay cannot compute difference between binary files ADDED rsrc/exefile.ico Index: rsrc/exefile.ico ================================================================== --- rsrc/exefile.ico +++ rsrc/exefile.ico cannot compute difference between binary files ADDED rsrc/gp_rsrc.rc Index: rsrc/gp_rsrc.rc ================================================================== --- rsrc/gp_rsrc.rc +++ rsrc/gp_rsrc.rc @@ -0,0 +1,674 @@ + +#include "resource.h" +#ifndef DS_SETFOREGROUND + #define DS_SETFOREGROUND 0x200L +#endif +#ifndef RT_MANIFEST + #define RT_MANIFEST 24 +#endif + +#include +#include +#define IDC_STATIC (-1) + +///////////////////////////////////////////////////////////////////////////// +// 日本語 resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_JPN) +#ifdef _WIN32 +LANGUAGE LANG_JAPANESE, SUBLANG_DEFAULT +#pragma code_page(932) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// RT_MANIFEST +// + +1 RT_MANIFEST "manifest.xml" + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 0,0,4,0 + PRODUCTVERSION 0,1,8,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "041104b0" + BEGIN + VALUE "CompanyName", "kMonos.NET\0" + VALUE "FileDescription", "GreenPad\0" + VALUE "FileVersion", "#42\0" + VALUE "InternalName", "kilib\0" + VALUE "LegalCopyright", "Written by k.inaba 2002-2008.\0" + VALUE "OriginalFilename", "GreenPad.exe\0" + VALUE "ProductName", "GreenPad\0" + VALUE "ProductVersion", "1.08.0\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x411, 1200 + END +END + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDR_MAIN ICON "exefile.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_MAIN MENU +BEGIN + POPUP "ファイル(&F)" + BEGIN + MENUITEM "新規作成(&N)\tCtrl+N", ID_CMD_NEWFILE + MENUITEM SEPARATOR + MENUITEM "開く(&O)...\tCtrl+O", ID_CMD_OPENFILE + MENUITEM "開き直す(&R)...\tCtrl+R", ID_CMD_REOPENFILE + MENUITEM SEPARATOR + MENUITEM "保存(&S)\tCtrl+S", ID_CMD_SAVEFILE + MENUITEM "別名で保存(&A)...\tCtrl+Shift+S", ID_CMD_SAVEFILEAS + MENUITEM SEPARATOR + POPUP "最近のファイル(&M)" + BEGIN + MENUITEM "(no files)", ID_MENUITEM40029, GRAYED + END + MENUITEM SEPARATOR + MENUITEM "終了(&X)", ID_CMD_EXIT + END + POPUP "編集(&E)" + BEGIN + MENUITEM "元に戻す(&U)\tCtrl+Z", ID_CMD_UNDO + MENUITEM "やり直す(&R)\tCtrl+Y", ID_CMD_REDO + MENUITEM SEPARATOR + MENUITEM "切り取り(&X)\tCtrl+X", ID_CMD_CUT + MENUITEM "コピー(&C)\tCtrl+C", ID_CMD_COPY + MENUITEM "貼り付け(&P)\tCtrl+V", ID_CMD_PASTE + MENUITEM "削除(&D)\tDel", ID_CMD_DELETE + MENUITEM SEPARATOR + MENUITEM "全てを選択(&A)\tCtrl+A", ID_CMD_SELECTALL + MENUITEM SEPARATOR + MENUITEM "日時の挿入(&T)\tF5", ID_CMD_DATETIME + END + POPUP "検索(&S)" + BEGIN + MENUITEM "検索・置換(&F)\tCtrl+F", ID_CMD_FIND + MENUITEM "次を検索(&N)\tF3", ID_CMD_FINDNEXT + MENUITEM "前を検索(&P)\tShift+F3", ID_CMD_FINDPREV + MENUITEM SEPARATOR + MENUITEM "指定行へジャンプ(&J)\tCtrl+J", ID_CMD_JUMP + MENUITEM SEPARATOR + MENUITEM "Grep(&G)...\tCtrl+G", ID_CMD_GREP + END + POPUP "表示(&V)" + BEGIN + MENUITEM "折り返さない(&N)\tCtrl+1", ID_CMD_NOWRAP + MENUITEM "指定幅で折り返し(&W)\tCtrl+2", ID_CMD_WRAPWIDTH + MENUITEM "右端で折り返し(&R)\tCtrl+3", ID_CMD_WRAPWINDOW + MENUITEM SEPARATOR + POPUP "文書タイプ(&T)" + BEGIN + MENUITEM "(なし)", ID_MENUITEM40025, GRAYED + END + MENUITEM "設定(&S)...", ID_CMD_CONFIG + MENUITEM SEPARATOR + MENUITEM "ステータス バー(&B)", ID_CMD_STATUSBAR + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_OPENFILEHOOK DIALOGEX 0, 0, 187, 34 +STYLE DS_SETFONT | DS_3DLOOK | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS +FONT 9, "MS Pゴシック" +BEGIN + LTEXT "",1119,0,0,187,13,SS_SUNKEN | NOT WS_VISIBLE | NOT + WS_GROUP + LTEXT "文字コード(&C):",IDC_STATIC,7,18,42,8 + COMBOBOX IDC_CODELIST,66,16,111,103,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP +END + +IDD_SAVEFILEHOOK DIALOGEX 0, 0, 187, 55 +STYLE DS_SETFONT | DS_3DLOOK | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS +FONT 9, "MS Pゴシック" +BEGIN + LTEXT "",1119,0,0,187,13,SS_SUNKEN | NOT WS_VISIBLE | NOT + WS_GROUP + LTEXT "文字コード(&C):",IDC_STATIC,7,18,42,8 + COMBOBOX IDC_CODELIST,66,16,111,103,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + LTEXT "改行コード(&L):",IDC_STATIC,7,37,41,8 + COMBOBOX IDC_CRLFLIST,66,35,60,103,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP +END + +IDD_REOPENDLG DIALOG 0, 0, 187, 54 +STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_CENTER | WS_VISIBLE | + WS_CLIPSIBLINGS | WS_CAPTION +CAPTION "開き直す" +FONT 9, "MS Pゴシック" +BEGIN + LTEXT "文字コード(&C):",IDC_STATIC,9,13,42,8 + COMBOBOX IDC_CODELIST,59,11,111,103,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + PUSHBUTTON "OK",IDOK,66,32,56,14 + PUSHBUTTON "キャンセル",IDCANCEL,125,32,56,14 +END + +IDD_FINDREPLACE DIALOG 0, 0, 316, 84 +STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | + WS_CAPTION | WS_SYSMENU +CAPTION "検索・置換" +FONT 9, "MS ゴシック" +BEGIN + LTEXT "検索文字列(&T):",IDC_STATIC,5,10,61,8 + EDITTEXT IDC_FINDBOX,68,8,122,13,ES_AUTOHSCROLL + LTEXT "置換文字列(&W):",IDC_STATIC,5,31,61,8 + EDITTEXT IDC_REPLACEBOX,68,28,122,13,ES_AUTOHSCROLL + DEFPUSHBUTTON "次を検索(&F)",ID_FINDNEXT,195,8,56,14 + PUSHBUTTON "置換(&R)",ID_REPLACENEXT,195,27,56,14 + PUSHBUTTON "前を検索(&E)",ID_FINDPREV,256,8,55,14 + PUSHBUTTON "全置換(&A)",ID_REPLACEALL,255,27,56,14 + GROUPBOX "オプション(&O)",IDC_STATIC,12,47,213,27 + CONTROL "大文字小文字を区別しない(&I)",IDC_IGNORECASE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,22,59,124,10 + CONTROL "正規表現(&X)",IDC_REGEXP,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,153,58,60,10 + PUSHBUTTON "閉じる",IDCANCEL,244,59,37,14 +END + +IDD_JUMP DIALOG 0, 0, 131, 43 +STYLE DS_ABSALIGN | DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | + WS_CAPTION | WS_SYSMENU +CAPTION "指定行へジャンプ" +FONT 9, "MS Pゴシック" +BEGIN + DEFPUSHBUTTON "&Go!",IDOK,87,12,25,14 + EDITTEXT IDC_LINEBOX,14,13,40,12,ES_AUTOHSCROLL | ES_NUMBER + LTEXT "行目へ",IDC_STATIC,61,15,22,8 +END + +IDD_CONFIG DIALOGEX 0, 0, 287, 236 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "エディタ設定" +FONT 9, "MS Pゴシック" +BEGIN + GROUPBOX "共通設定",IDC_STATIC,7,11,273,102 + LTEXT "[元に戻す]上限(&U):",IDC_STATIC,24,24,56,8 + CONTROL "無限",IDC_UNDOLIM1,"Button",BS_AUTORADIOBUTTON | + WS_GROUP | WS_TABSTOP,86,23,33,10 + CONTROL "",IDC_UNDOLIM2,"Button",BS_AUTORADIOBUTTON,122,23,11,8 + EDITTEXT IDC_UNDO_CT,134,21,24,12,ES_AUTOHSCROLL | ES_NUMBER + LTEXT "回",IDC_STATIC,162,24,8,8 + LTEXT "横位置の表示(&L):",IDC_STATIC,29,37,51,8 + CONTROL "文字数",IDC_COUNTBYLETTER,"Button",BS_AUTORADIOBUTTON | + WS_GROUP | WS_TABSTOP,86,36,33,10 + CONTROL "表示位置",IDC_COUNTBYLETTER2,"Button", + BS_AUTORADIOBUTTON | WS_TABSTOP,122,35,38,10 + LTEXT "ファイル履歴件数(&H):",IDC_STATIC,19,50,62,8 + EDITTEXT IDC_LATEST_NUM,86,48,32,12,ES_AUTOHSCROLL | ES_NUMBER + LTEXT "ファイル・フィルタ(&F):",IDC_STATIC,21,66,60,8 + EDITTEXT IDC_TXTFILT,86,63,182,12,ES_AUTOHSCROLL + LTEXT "外部Grep(&G):",IDC_STATIC,42,81,39,8 + EDITTEXT IDC_EXTGREP,86,78,182,12,ES_AUTOHSCROLL + LTEXT "新規ファイルのモード(&N):",IDC_STATIC,9,95,73,8 + COMBOBOX IDC_NEWCS,86,92,76,162,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_NEWLB,164,92,39,162,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_NEWDT,204,92,65,164,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + CONTROL "同じウインドウで開く(&R)",IDC_OPENSAME,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,181,25,83,10 + CONTROL "ウインドウサイズを記憶(&S)",IDC_REMSIZE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,181,35,92,10 + CONTROL "ウインドウ位置を記憶(&P)",IDC_REMPLACE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,181,45,86,10 + GROUPBOX "文書タイプ別設定",IDC_STATIC,7,118,274,92 + LISTBOX IDC_DOCTYPELIST,15,133,88,53,LBS_NOINTEGRALHEIGHT | + WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "追加",IDC_NEWDOCTYPE,17,191,29,14 + PUSHBUTTON "削除",IDC_DELDOCTYPE,48,191,29,14 + LTEXT "パターン:",IDC_STATIC,114,138,27,8 + EDITTEXT IDC_DT_PAT,146,135,127,12,ES_AUTOHSCROLL + LTEXT "キーワード:",IDC_STATIC,108,155,34,8 + COMBOBOX IDC_PAT_KWD,146,152,75,172,CBS_DROPDOWNLIST | CBS_SORT | + CBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "編集",IDC_EDITKWD,231,152,19,14 + LTEXT "レイアウト:",IDC_STATIC,111,172,31,8 + COMBOBOX IDC_PAT_LAY,146,169,75,172,CBS_DROPDOWNLIST | CBS_SORT | + CBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "編集",IDC_EDITLAY,231,169,19,14 + DEFPUSHBUTTON "OK",IDOK,175,214,50,14 + PUSHBUTTON "キャンセル",IDCANCEL,230,214,50,14 +END + +IDD_ADDDOCTYPE DIALOG 0, 0, 123, 67 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "文書タイプの追加" +FONT 9, "MS Pゴシック" +BEGIN + LTEXT "名前(&N):",IDC_STATIC,17,11,27,8 + EDITTEXT IDC_NAME,46,8,69,12,ES_AUTOHSCROLL + LTEXT "拡張子(&E):",IDC_STATIC,10,25,34,8 + EDITTEXT IDC_EXT,46,24,69,12,ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDOK,33,43,34,14 + PUSHBUTTON "キャンセル",IDCANCEL,71,43,33,14 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDR_MAIN ACCELERATORS +BEGIN + "1", ID_CMD_NOWRAP, VIRTKEY, CONTROL, NOINVERT + "2", ID_CMD_WRAPWIDTH, VIRTKEY, CONTROL, NOINVERT + "3", ID_CMD_WRAPWINDOW, VIRTKEY, CONTROL, NOINVERT + "A", ID_CMD_SELECTALL, VIRTKEY, CONTROL, NOINVERT + "C", ID_CMD_COPY, VIRTKEY, CONTROL, NOINVERT + "F", ID_CMD_FIND, VIRTKEY, CONTROL, NOINVERT + "G", ID_CMD_GREP, VIRTKEY, CONTROL, NOINVERT + "H", ID_CMD_FIND, VIRTKEY, CONTROL, NOINVERT + "J", ID_CMD_JUMP, VIRTKEY, CONTROL, NOINVERT + "N", ID_CMD_NEWFILE, VIRTKEY, CONTROL, NOINVERT + "O", ID_CMD_OPENFILE, VIRTKEY, CONTROL, NOINVERT + "R", ID_CMD_REOPENFILE, VIRTKEY, CONTROL, NOINVERT + "S", ID_CMD_SAVEFILE, VIRTKEY, CONTROL, NOINVERT + "S", ID_CMD_SAVEFILEAS, VIRTKEY, SHIFT, CONTROL, + NOINVERT + "V", ID_CMD_PASTE, VIRTKEY, CONTROL, NOINVERT + VK_DELETE, ID_CMD_CUT, VIRTKEY, SHIFT, NOINVERT + VK_F3, ID_CMD_FINDNEXT, VIRTKEY, NOINVERT + VK_F3, ID_CMD_FINDPREV, VIRTKEY, SHIFT, NOINVERT + VK_F4, ID_CMD_EXIT, VIRTKEY, CONTROL, NOINVERT + VK_F5, ID_CMD_DATETIME, VIRTKEY, NOINVERT + VK_INSERT, ID_CMD_COPY, VIRTKEY, CONTROL, NOINVERT + VK_INSERT, ID_CMD_PASTE, VIRTKEY, SHIFT, NOINVERT + VK_RETURN, ID_CMD_REOPENFILE, VIRTKEY, ALT, NOINVERT + VK_TAB, ID_CMD_NEXTWINDOW, VIRTKEY, CONTROL, NOINVERT + VK_TAB, ID_CMD_PREVWINDOW, VIRTKEY, SHIFT, CONTROL, + NOINVERT + "X", ID_CMD_CUT, VIRTKEY, CONTROL, NOINVERT + "Y", ID_CMD_REDO, VIRTKEY, CONTROL, NOINVERT + "Z", ID_CMD_UNDO, VIRTKEY, CONTROL, NOINVERT +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_FINDREPLACE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 309 + TOPMARGIN, 7 + BOTTOMMARGIN, 77 + END + + IDD_JUMP, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 124 + TOPMARGIN, 7 + BOTTOMMARGIN, 36 + END + + IDD_CONFIG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 280 + TOPMARGIN, 7 + BOTTOMMARGIN, 228 + END + + IDD_ADDDOCTYPE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 116 + TOPMARGIN, 7 + BOTTOMMARGIN, 60 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_ASKTOSAVE "現在の文書を保存しますか?" + IDS_APPNAME "GreenPad" + IDS_SAVEERROR "ファイルの保存に失敗しました。" + IDS_ALLFILES "全てのファイル(*.*)" + IDS_TXTFILES "テキスト形式のファイル" + IDS_OPENERROR "ファイルを開くのに失敗しました。" + IDS_DEFAULT "(標準)" + IDS_NOTFOUND "見つかりませんでした。" + IDS_REPLACEALLDONE "%d箇所置換しました。" + IDS_OKTODEL "を削除してよろしいですか?" +END + +#endif // 日本語 resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// 英語 (米国) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_MAIN MENU +BEGIN + POPUP "&File" + BEGIN + MENUITEM "&New File\tCtrl+N", ID_CMD_NEWFILE + MENUITEM SEPARATOR + MENUITEM "&Open...\tCtrl+O", ID_CMD_OPENFILE + MENUITEM "&ReOpen...\tCtrl+R", ID_CMD_REOPENFILE + MENUITEM SEPARATOR + MENUITEM "&Save\tCtrl+S", ID_CMD_SAVEFILE + MENUITEM "Save &As...\tCtrl+Shift+S", ID_CMD_SAVEFILEAS + MENUITEM SEPARATOR + POPUP "R&ecent Files" + BEGIN + MENUITEM "(no files)", ID_MENUITEM40030, GRAYED + END + MENUITEM "E&xit", ID_CMD_EXIT + END + POPUP "&Edit" + BEGIN + MENUITEM "&Undo\tCtrl+Z", ID_CMD_UNDO + MENUITEM "&Redo\tCtrl+Y", ID_CMD_REDO + MENUITEM SEPARATOR + MENUITEM "Cut\tCtrl+&X", ID_CMD_CUT + MENUITEM "&Copy\tCtrl+C", ID_CMD_COPY + MENUITEM "&Paste\tCtrl+V", ID_CMD_PASTE + MENUITEM "&Delete\tDel", ID_CMD_DELETE + MENUITEM SEPARATOR + MENUITEM "Select &All\tCtrl+A", ID_CMD_SELECTALL + MENUITEM SEPARATOR + MENUITEM "Insert Date&&&Time\tF5", ID_CMD_DATETIME + END + POPUP "&Search" + BEGIN + MENUITEM "&Find\tCtrl+F", ID_CMD_FIND + MENUITEM "Find &Next\tF3", ID_CMD_FINDNEXT + MENUITEM "Find &Prev\tShift+F3", ID_CMD_FINDPREV + MENUITEM SEPARATOR + MENUITEM "&Jump to Line\tCtrl+J", ID_CMD_JUMP + MENUITEM SEPARATOR + MENUITEM "&Grep...\tCtrl+G", ID_CMD_GREP + END + POPUP "&View" + BEGIN + MENUITEM "&No wrapping\tCtrl+1", ID_CMD_NOWRAP + MENUITEM "&Wrap at #th letter\tCtrl+2", ID_CMD_WRAPWIDTH + MENUITEM "Wrap at &Right Edge\tCtrl+3", ID_CMD_WRAPWINDOW + MENUITEM SEPARATOR + POPUP "&Document Type" + BEGIN + MENUITEM "dummy", ID_MENUITEM40025 + END + MENUITEM "&Settings...", ID_CMD_CONFIG + MENUITEM SEPARATOR + MENUITEM "Status&Bar", ID_CMD_STATUSBAR + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_SAVEFILEHOOK DIALOGEX 0, 0, 187, 55 +STYLE DS_SETFONT | DS_3DLOOK | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS +FONT 9, "MS Sans Serif" +BEGIN + LTEXT "",1119,0,0,187,13,SS_SUNKEN | NOT WS_VISIBLE | NOT + WS_GROUP + LTEXT "&Charactor Encode:",IDC_STATIC,7,18,58,8 + COMBOBOX IDC_CODELIST,69,16,108,103,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + LTEXT "&Line-end:",IDC_STATIC,7,37,41,8 + COMBOBOX IDC_CRLFLIST,69,35,57,103,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP +END + +IDD_REOPENDLG DIALOG 0, 0, 187, 54 +STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_CENTER | WS_VISIBLE | + WS_CLIPSIBLINGS | WS_CAPTION +CAPTION "ReOpen File" +FONT 9, "MS Sans Serif" +BEGIN + LTEXT "&Charactor Encode:",IDC_STATIC,9,13,54,8 + COMBOBOX IDC_CODELIST,72,11,105,103,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + PUSHBUTTON "OK",IDOK,66,32,56,14 + PUSHBUTTON "Cancel",IDCANCEL,125,32,56,14 +END + +IDD_OPENFILEHOOK DIALOGEX 0, 0, 187, 34 +STYLE DS_SETFONT | DS_3DLOOK | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS +FONT 9, "MS Sans Serif" +BEGIN + LTEXT "",1119,0,0,187,13,SS_SUNKEN | NOT WS_VISIBLE | NOT + WS_GROUP + LTEXT "&Charactor Encode:",IDC_STATIC,7,18,54,8 + COMBOBOX IDC_CODELIST,66,16,111,103,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP +END + +IDD_JUMP DIALOG 0, 0, 131, 43 +STYLE DS_ABSALIGN | DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | + WS_CAPTION | WS_SYSMENU +CAPTION "Jump To" +FONT 9, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "&Go!",IDOK,83,13,25,14 + EDITTEXT IDC_LINEBOX,34,14,40,12,ES_AUTOHSCROLL | ES_NUMBER + LTEXT "&Line",IDC_STATIC,17,16,13,8 +END + +IDD_FINDREPLACE DIALOG 0, 0, 282, 84 +STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | + WS_CAPTION | WS_SYSMENU +CAPTION "Find & Replace" +FONT 9, "MS Sans Serif" +BEGIN + LTEXT "&Text to Find:",IDC_STATIC,7,10,38,8 + EDITTEXT IDC_FINDBOX,56,7,123,13,ES_AUTOHSCROLL + DEFPUSHBUTTON "&Find",ID_FINDNEXT,185,8,43,14 + PUSHBUTTON "Find&Prev",ID_FINDPREV,232,8,43,14 + LTEXT "Replace &with:",IDC_STATIC,7,31,40,8 + EDITTEXT IDC_REPLACEBOX,56,28,123,13,ES_AUTOHSCROLL + PUSHBUTTON "&Replace",ID_REPLACENEXT,185,27,43,14 + PUSHBUTTON "Replace&All",ID_REPLACEALL,232,27,43,14 + GROUPBOX "&Options",IDC_STATIC,12,47,181,27 + CONTROL "&Ignore Case",IDC_IGNORECASE,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,22,59,51,10 + CONTROL "Regular E&xpression",IDC_REGEXP,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,114,58,73,10 + PUSHBUTTON "Close",IDCANCEL,244,59,29,14 +END + +IDD_ADDDOCTYPE DIALOG 0, 0, 123, 67 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "New DocType" +FONT 9, "MS Sans Serif" +BEGIN + LTEXT "&Name:",IDC_STATIC,22,11,19,8 + EDITTEXT IDC_NAME,46,8,69,12,ES_AUTOHSCROLL + LTEXT "&Extension:",IDC_STATIC,10,25,34,8 + EDITTEXT IDC_EXT,46,24,69,12,ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDOK,33,43,34,14 + PUSHBUTTON "Cancel",IDCANCEL,71,43,33,14 +END + +IDD_CONFIG DIALOGEX 0, 0, 287, 236 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Config" +FONT 9, "MS Sans Serif" +BEGIN + GROUPBOX "Common",IDC_STATIC,7,7,273,106 + LTEXT "&Undo:",IDC_STATIC,62,21,25,8 + CONTROL "Infinite",IDC_UNDOLIM1,"Button",BS_AUTORADIOBUTTON | + WS_GROUP | WS_TABSTOP,88,20,33,10 + CONTROL "",IDC_UNDOLIM2,"Button",BS_AUTORADIOBUTTON,124,20,11,8 + EDITTEXT IDC_UNDO_CT,135,18,24,12,ES_AUTOHSCROLL | ES_NUMBER + LTEXT "times",IDC_STATIC,163,21,17,8 + LTEXT "&Column by:",IDC_STATIC,46,33,40,8 + CONTROL "Letters",IDC_COUNTBYLETTER,"Button",BS_AUTORADIOBUTTON | + WS_GROUP | WS_TABSTOP,88,32,33,10 + CONTROL "Positions",IDC_COUNTBYLETTER2,"Button", + BS_AUTORADIOBUTTON | WS_TABSTOP,124,32,49,10 + LTEXT "M&RU File Num:",IDC_STATIC,34,45,52,8 + EDITTEXT IDC_LATEST_NUM,88,43,32,12,ES_AUTOHSCROLL | ES_NUMBER + LTEXT "TextFile &Filter:",IDC_STATIC,36,63,44,8 + EDITTEXT IDC_TXTFILT,88,59,176,12,ES_AUTOHSCROLL + LTEXT "Extrenal &Grep Program:",IDC_STATIC,12,77,77,8 + EDITTEXT IDC_EXTGREP,88,75,176,12,ES_AUTOHSCROLL + LTEXT "&New File Mode:",IDC_STATIC,34,93,53,8 + COMBOBOX IDC_NEWCS,88,90,76,167,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_NEWLB,166,90,39,166,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_NEWDT,206,90,65,165,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + CONTROL "Open in Same &Window",IDC_OPENSAME,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,186,17,90,10 + CONTROL "Remember Window &Size",IDC_REMSIZE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,186,27,92,10 + CONTROL "Remember Window &Pos",IDC_REMPLACE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,186,38,90,10 + GROUPBOX "Document Types",IDC_STATIC,6,118,274,92 + LISTBOX IDC_DOCTYPELIST,14,133,89,53,LBS_NOINTEGRALHEIGHT | + WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Add",IDC_NEWDOCTYPE,17,191,29,14 + PUSHBUTTON "Del",IDC_DELDOCTYPE,50,191,29,14 + LTEXT "Pattern:",IDC_STATIC,117,138,23,8 + EDITTEXT IDC_DT_PAT,149,135,123,12,ES_AUTOHSCROLL + LTEXT "Keyword:",IDC_STATIC,114,155,27,8 + COMBOBOX IDC_PAT_KWD,149,152,72,172,CBS_DROPDOWNLIST | CBS_SORT | + CBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "edit",IDC_EDITKWD,229,151,19,14 + LTEXT "Layout: ",IDC_STATIC,118,172,24,8 + COMBOBOX IDC_PAT_LAY,149,169,72,172,CBS_DROPDOWNLIST | CBS_SORT | + CBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "edit",IDC_EDITLAY,229,169,19,14 + DEFPUSHBUTTON "OK",IDOK,175,215,50,14 + PUSHBUTTON "Cancel",IDCANCEL,230,215,50,14 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_JUMP, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 124 + TOPMARGIN, 7 + BOTTOMMARGIN, 36 + END + + IDD_FINDREPLACE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 275 + TOPMARGIN, 7 + BOTTOMMARGIN, 77 + END + + IDD_ADDDOCTYPE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 116 + TOPMARGIN, 7 + BOTTOMMARGIN, 60 + END + + IDD_CONFIG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 280 + TOPMARGIN, 7 + BOTTOMMARGIN, 229 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_ASKTOSAVE "Do you want to Save the current file ?" + IDS_APPNAME "GreenPad" + IDS_SAVEERROR "Could not save the file." + IDS_ALLFILES "All Files(*.*)" + IDS_TXTFILES "Text Files" + IDS_OPENERROR "Could not open the file." + IDS_DEFAULT "(default)" + IDS_NOTFOUND "Not Found." + IDS_REPLACEALLDONE "%d times Replaced." + IDS_OKTODEL " will be removed. OK?" +END + +#endif // 英語 (米国) resources +///////////////////////////////////////////////////////////////////////////// ADDED rsrc/manifest.xml Index: rsrc/manifest.xml ================================================================== --- rsrc/manifest.xml +++ rsrc/manifest.xml @@ -0,0 +1,2 @@ + + ADDED rsrc/resource.h Index: rsrc/resource.h ================================================================== --- rsrc/resource.h +++ rsrc/resource.h @@ -0,0 +1,105 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by kilib.rc +// +#define IDS_ASKTOSAVE 1 +#define IDS_APPNAME 2 +#define IDS_SAVEERROR 3 +#define ID_FINDNEXT 3 +#define IDS_ALLFILES 4 +#define ID_REPLACENEXT 4 +#define IDS_TXTFILES 5 +#define ID_REPLACEALL 5 +#define IDS_OPENERROR 6 +#define IDS_DEFAULT 7 +#define IDS_NOTFOUND 8 +#define IDS_REPLACEALLDONE 9 +#define IDS_OKTODEL 10 +#define IDR_MAIN 103 +#define IDD_OPENFILEHOOK 105 +#define IDD_SAVEFILEHOOK 106 +#define IDD_REOPENDLG 107 +#define IDD_FINDREPLACE 108 +#define IDD_JUMP 109 +#define IDD_CONFIG 110 +#define IDD_ADDDOCTYPE 111 +#define IDC_CODELIST 1002 +#define IDC_CRLFLIST 1003 +#define IDC_FINDBOX 1004 +#define IDC_REPLACE 1005 +#define IDC_REPLACEBOX 1005 +#define IDC_IGNORECASE 1006 +#define IDC_REGEXP 1007 +#define IDC_LINEBOX 1007 +#define IDC_BACKSEARCH 1008 +#define ID_FINDPREV 1008 +#define IDC_LATEST_NUM 1010 +#define IDC_UNDOLIM1 1011 +#define IDC_UNDOLIM2 1012 +#define IDC_UNDO_CT 1013 +#define IDC_TXTFILT 1014 +#define IDC_EXTGREP 1015 +#define IDC_DT_PAT 1018 +#define IDC_PAT_KWD 1019 +#define IDC_DOCTYPELIST 1020 +#define IDC_NEWDOCTYPE 1021 +#define IDC_PAT_LAY 1022 +#define IDC_DELDOCTYPE 1023 +#define IDC_EDITKWD 1024 +#define IDC_EDITLAY 1025 +#define IDC_NAME 1025 +#define IDC_EXT 1026 +#define IDC_OPENSAME 1026 +#define IDC_COUNTBYLETTER 1027 +#define IDC_NEWCS 1028 +#define IDC_NEWLB 1029 +#define IDC_OPENSAME2 1030 +#define IDC_REMSIZE 1030 +#define IDC_OPENSAME3 1031 +#define IDC_REMPLACE 1031 +#define IDC_COUNTBYLETTER2 1032 +#define IDC_NEWLB2 1033 +#define IDC_NEWDT 1033 +#define ID_CMD_REOPENFILE 40002 +#define ID_CMD_PROPERTY 40003 +#define ID_CMD_NEWFILE 40004 +#define ID_CMD_OPENFILE 40005 +#define ID_CMD_SAVEFILE 40006 +#define ID_CMD_SAVEFILEAS 40007 +#define ID_CMD_EXIT 40008 +#define ID_CMD_UNDO 40009 +#define ID_CMD_REDO 40010 +#define ID_CMD_CUT 40011 +#define ID_CMD_COPY 40012 +#define ID_CMD_PASTE 40013 +#define ID_CMD_DELETE 40014 +#define ID_CMD_SELECTALL 40015 +#define ID_CMD_FIND 40016 +#define ID_CMD_FINDNEXT 40017 +#define ID_CMD_FINDPREV 40018 +#define ID_CMD_NOWRAP 40019 +#define ID_CMD_WRAPWIDTH 40020 +#define ID_CMD_WRAPWINDOW 40021 +#define ID_CMD_CONFIG 40022 +#define ID_CMD_JUMP 40023 +#define ID_CMD_DATETIME 40024 +#define ID_MENUITEM40025 40025 +#define ID_CMD_NEXTWINDOW 40026 +#define ID_CMD_PREVWINDOW 40027 +#define ID_CMD_GREP 40028 +#define ID_MENUITEM40029 40029 +#define ID_MENUITEM40030 40030 +#define ID_CMD_STATUSBAR 40032 +#define ID_CMD_MRU 40200 +#define ID_CMD_DOCTYPE 40300 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 113 +#define _APS_NEXT_COMMAND_VALUE 40033 +#define _APS_NEXT_CONTROL_VALUE 1029 +#define _APS_NEXT_SYMED_VALUE 102 +#endif +#endif