{------------------------------- EmEditorのプラグインDLLの 共通関数ユニット 履歴 2002/08/17 usesにStringRecordListを追加して SetSelectTextを実装 2002/09/01 選択状態の時にキャレット位置に前後があることに気が付いたので それを保持・復帰できるようにSetSelectCaretPos/GetSelectCaretPosを実装して SetSelectTextとRowSelectionに組み込んだ 2002/09/29 SetSelectTextに['']を指定したときに 文字列Insertがおきて変更されてしまう動作を停止 2002/10/03 SetSelectTextで一部WideString化されていない 所があったので修正 2003/05/25 RowSelectionの時、最終行は改行コードを含まなかったものを 含むようにした。 2004/02/22 Ver3からVer4へ移行では#0終端文字列もメモリ確保していないと 正しい文字列を返してくれなかったので修正した。 2007/08/19 XPtestユニットが容量を食っていたのでコメントアウトした --------------------------------} unit PluginLibrary; interface uses Types, Windows, StringUnitLight, WideStringRecordList, plugin, XPtest, uses_end; type TSelectCaretPoint = (scpStart, scpEnd); function Editor_GetSelTextW_Delphi(hwnd: HWND): WideString; function Editor_GetLineW_Delphi(hwnd: HWND; Line: Integer): WideString; procedure Editor_ReplaceW_Delphi(hwnd: HWND; const OldPattern, NewPattern: WideString); procedure Editor_SetSelView_Delphi(hwnd: HWND; SelStartPoint, SelEndPoint: TPoint); procedure RowSelection(hwnd: HWND); procedure StartSelection(hwnd: HWND); procedure EndSelection(hwnd: HWND); procedure Editor_InsertStringW_Delphi(hwnd: HWND; const S: WideString); procedure SetSelectText(hwnd: HWND; const SetStr: WideString); //procedure testSetSelectText(hwnd: HWND); procedure SetSelectCaretPos(hwnd: HWND; CaretPoint: TSelectCaretPoint); function GetSelectCaretPos(hwnd: HWND): TSelectCaretPoint; function CaretFrontText(hwnd: HWND): WideString; function CaretBackText(hwnd: HWND): WideString; function GetSelStartIndex(hwnd: HWND): Integer; procedure SetSelStartIndex(hwnd: HWND; Value: Integer); function GetSelLength(hwnd: HWND): Integer; procedure SetSelLength(hwnd: HWND; Value: Integer); //function LengthW(const S: WideString): Integer; function Editor_Info_EI_GET_FILE_NAMEW_Delphi(hwnd: HWND): WideString; function MenuOption(CheckValue, EnabledValue: Boolean): Integer; implementation (*----------------------------------- //Delphi文字列用Editor_GetSelTextW function Editor_GetSelTextW_Delphi(hwnd: HWND): WideString; var SelectLen: Integer; begin Result := ''; {↓選択文字列をDelphiのWideStringに確保する Editor_GetSelTextWは文字数+1を返す} SelectLen := Editor_GetSelTextW(hwnd,0,nil) - 1; {↓文字列の容量を確保 SetLengthはWideStringに対して文字数を指定するとよい} SetLength(Result, SelectLen); {↓文字列を取得(WideStrに選択した文字列が入る)} Editor_GetSelTextW(hwnd, SelectLen, @Result[1]); end; //Delphi文字列用Editor_GetLineW function Editor_GetLineW_Delphi(hwnd: HWND; Line: Integer): WideString; var LineLength: Integer; GetLineInfo: TGET_LINE_INFO; begin Result := ''; {↓文字列を取得} with GetLineInfo do begin cb := 0; flags := FLAG_LOGICAL; yLine := Line; end; LineLength := Editor_GetLineW( hwnd, @GetLineInfo, nil) - 1; SetLength(Result, LineLength); with GetLineInfo do begin cb := LineLength + 1; flags := FLAG_LOGICAL; yLine := Line; end; Editor_GetLineW(hwnd, @GetLineInfo, @Result[1]); end; //-----------------------------------*) //(*----------------------------------- {------------------------------- // Delphi文字列用Editor_GetSelTextW 備考: 履歴: 2004/02/22 修正 文字列終端の#0の部分もResultに確保するようにした そうしないとEmEditorV4では動かない //------------------------------} function Editor_GetSelTextW_Delphi(hwnd: HWND): WideString; var SelectLen: Integer; begin Result := ''; {↓選択文字列をDelphiのWideStringに確保する Editor_GetSelTextWは終端に#0を含む文字数+1を返す} SelectLen := Editor_GetSelTextW(hwnd,0,nil); {↓文字列の容量を確保 SetLengthはWideStringに対して(バイト数ではなく)文字数指定で容量を確保できる 終端の#0も1文字として容量を確保しておく} SetLength(Result, SelectLen); {↓文字列を取得(WideStrに選択した文字列が入る)} Editor_GetSelTextW(hwnd, SelectLen, @Result[1]); {↓文字列から終端の#0を削除する} Result := WideString(PWideChar(Result)); // DebugPrintNotepad( Result ); end; //------------------------------ {------------------------------- // Delphi文字列用Editor_GetLineW 備考: 履歴: 2004/02/22 修正 API呼び出しの部分で 文字列終端の#0の部分も含めた長さで Resultに確保するようにした そうしないとEmEditorV4では動かない //------------------------------} function Editor_GetLineW_Delphi(hwnd: HWND; Line: Integer): WideString; var LineLength: Integer; GetLineInfo: TGetLineInfo;//TGET_LINE_INFO; begin Result := ''; {↓文字列を取得} with GetLineInfo do begin cb := 0; flags := FLAG_LOGICAL; yLine := Line; end; LineLength := Editor_GetLineW( hwnd, @GetLineInfo, nil); SetLength(Result, LineLength); with GetLineInfo do begin cb := LineLength; flags := FLAG_LOGICAL; yLine := Line; end; Editor_GetLineW(hwnd, @GetLineInfo, @Result[1]); Result := WideString(PWideChar(Result)); end; //------------------------------ //Delphi文字列用Editor_ReplaceW procedure Editor_ReplaceW_Delphi(hwnd: HWND; const OldPattern, NewPattern: WideString); var WideStr: WideString; begin WideStr := OldPattern + #0 + NewPattern + #0#0; Editor_ReplaceW(hwnd, FLAG_REPLACE_ALL+FLAG_REPLACE_SEL_ONLY, PWideChar(WideStr)); { ----------------------------------- Ansi文字列の場合は以下のようにする var S: String; Editor_ReplaceA(hwnd, FLAG_REPLACE_ALL+FLAG_REPLACE_SEL_ONLY, PChar(S)); //----------------------------------- } {↓検索強調文字の解除} Editor_ExecCommand(hwnd,EEID_ERASE_FIND_HILITE); end; //Delphi用SetSelView procedure Editor_SetSelView_Delphi(hwnd: HWND; SelStartPoint, SelEndPoint: TPoint); begin if (SelStartPoint.X = SelEndPoint.X) and (SelStartPoint.Y = SelEndPoint.Y) then begin Editor_SetCaretPos(hwnd, POS_VIEW, @SelStartPoint); end else begin Editor_SetSelView(hwnd, @SelStartPoint, @SelEndPoint); end; { ----------------------------------- ↑選択位置のStartとEndが同一の時に Editor_SetSelViewで選択文字を指定すると 選択文字列がないにもかかわらず 選択文字列があるように振舞ってしまう (キー入力のワンストローク分が変になる) ので気をつけること 上記ソースのように SelStartPointとSelEndPointのX,Yが 同一かどうかで調べて SetCaretPosかSetSelViewを使い分けてください。 //----------------------------------- } end; //EmEditorのテキストを行選択状態にする (*----------------------------------- procedure RowSelection(hwnd: HWND); var SelStartPoint, SelEndPoint: TPoint; LineStr: WideString; CaretPos: TSelectCaretPoint; begin {↓選択位置の始点終点を保持する} Editor_GetSelStart( hwnd, POS_LOGICAL_W, @SelStartPoint ); Editor_GetSelEnd( hwnd, POS_LOGICAL_W, @SelEndPoint ); CaretPos := GetSelectCaretPos(hwnd); {↓始点のX位置を0にする} SelStartPoint.X := 0; {↓行の最後の改行コードを選択しているかどうかを調べる} if (SelStartPoint.Y <> SelEndPoint.Y) and (SelEndPoint.X = 0) then begin //行の最後の改行コードを選択しているなら何もしない end else begin {↓終点のX位置を文字列最後にあわせる} LineStr := Editor_GetLineW_Delphi(hwnd, SelEndPoint.Y); SelEndPoint.X := Length(LineStr); end; {↓論理座標を表示座標に変更してSetSelViewを使って文字列を選択} Editor_LogicalToView( hwnd, @SelStartPoint, @SelStartPoint); Editor_LogicalToView( hwnd, @SelEndPoint, @SelEndPoint); Editor_SetSelView( hwnd, @SelStartPoint, @SelEndPoint); SetSelectCaretPos( hwnd, CaretPos); end; //-----------------------------------*) procedure RowSelection(hwnd: HWND); var SelStartPoint, SelEndPoint: TPoint; CaretPos: TSelectCaretPoint; LineStr: WideString; begin {↓選択位置の始点終点を保持する} Editor_GetSelStart( hwnd, POS_LOGICAL_W, @SelStartPoint ); Editor_GetSelEnd( hwnd, POS_LOGICAL_W, @SelEndPoint ); CaretPos := GetSelectCaretPos(hwnd); {↓始点のX位置を0にする} SelStartPoint.X := 0; {↓行の最後の改行コードを選択しているかどうかを調べる} if (SelStartPoint.Y <> SelEndPoint.Y) and (SelEndPoint.X = 0) then begin //行の最後の改行コードを選択しているなら何もしない end else begin if SelEndPoint.Y = Editor_GetLines(hwnd, POS_LOGICAL_W)-1 then begin {↓終点のX位置を文字列最後にあわせる} LineStr := Editor_GetLineW_Delphi(hwnd, SelEndPoint.Y); SelEndPoint.X := Length(LineStr); end else begin {↓終点を次の行の先頭にする} SelEndPoint.Y := SelEndPoint.Y + 1; SelEndPoint.X := 0; end; end; {↓論理座標を表示座標に変更してSetSelViewを使って文字列を選択} Editor_LogicalToView( hwnd, @SelStartPoint, @SelStartPoint); Editor_LogicalToView( hwnd, @SelEndPoint, @SelEndPoint); Editor_SetSelView( hwnd, @SelStartPoint, @SelEndPoint); SetSelectCaretPos( hwnd, CaretPos); end; //キャレットより前方を選択状態にする procedure StartSelection(hwnd: HWND); var CaretPoint: TPoint; SelStartPoint: TPoint; begin Editor_GetCaretPos(hwnd, POS_VIEW, @CaretPoint); SelStartPoint := Point(0,0); Editor_SetSelView_Delphi(hwnd, SelStartPoint, CaretPoint); end; //キャレットより後方を選択状態にする procedure EndSelection(hwnd: HWND); var CaretPoint: TPoint; SelEndPoint: TPoint; LastLine: Integer; begin Editor_GetCaretPos(hwnd, POS_LOGICAL_W, @CaretPoint); LastLine := Editor_GetLines(hwnd, POS_LOGICAL_W); SelEndPoint := Point(Length(Editor_GetLineW_Delphi(hwnd, LastLine-1)), LastLine-1); {↓論理座標を表示座標に変更してSetSelViewを使って文字列を選択} Editor_LogicalToView( hwnd, @CaretPoint, @CaretPoint); Editor_LogicalToView( hwnd, @SelEndPoint, @SelEndPoint); Editor_SetSelView_Delphi(hwnd, CaretPoint, SelEndPoint); SetSelectCaretPos(hwnd, scpStart); end; //単に選択位置の文字列を置き換えるだけ //SetSelectTextがあるので使わない //(このやり方では行ごとの更新情報がなくなるから不便) procedure Editor_InsertStringW_Delphi(hwnd: HWND; const S: WideString); var WideStr: WideString; SelStartPos: TPoint; begin WideStr := S; {↓SelStartの位置を保持} Editor_GetSelStart(hwnd, POS_LOGICAL_W, @SelStartPos); {↓文字列を挿入} Editor_InsertStringW(hwnd, @WideStr[1]); //SendMessage(hwnd, EE_INSERT_STRINGW, 0, Integer(@WideStr[1])); {↓SelStartの位置を復帰} Editor_SetCaretPos(hwnd, POS_LOGICAL_W, @SelStartPos); Editor_SetSelLength(hwnd, Length(S)); {↑置き換えた文字列を選択しておく} end; {------------------------------- // EmEditorに文字列をセットする手続き 機能: EmEditorで更新された行だけを セットするようにした 引数説明: SetStr: セットしたい文字列 備考: WideString対応 WideStringをAnsiStringに変更するだけで ShiftJIS版が出来る 履歴: 2002/10/03 この説明を記述 一部WideString化が抜けていたので修正  //------------------------------} procedure SetSelectText(hwnd: HWND; const SetStr: WideString); function Min(A, B: Integer): Integer; begin if A <= B then Result := A else Result := B; end; var SelectStrList, SetStrList: TWideStringRecordList; i, j: Integer; SelStartPos, SelEndPos: TPoint; LineSelectPos: TPoint; SelectStr, SelectLineStr, SetLineStr, InsertStr: WideString; DeleteLength: Integer; CaretPoint: TSelectCaretPoint; begin SelectStrList := TWideStringRecordList.Create; try SetStrList := TWideStringRecordList.Create; try {↓StringListに選択文字列と置換え文字列をセット} SelectStr := Editor_GetSelTextW_Delphi(hwnd); SelectStrList.Text := SelectStr; SetStrList.Text := SetStr; {↓SelStart SelEndの位置を保持} Editor_GetSelStart(hwnd, POS_LOGICAL_W, @SelStartPos); Editor_GetSelEnd(hwnd, POS_LOGICAL_W, @SelEndPos); CaretPoint := GetSelectCaretPos(hwnd); {↑キャレットの位置を保持} for i := 0 to Min(SelectStrList.Count, SetStrList.Count)-1 do begin LineSelectPos := Point(0,0); {↓行の文字列を取得} SelectLineStr := SelectStrList.Items[i]; SetLineStr := SetStrList.Items[i]; if i=0 then LineSelectPos.X := SelStartPos.X else LineSelectPos.X := 0; LineSelectPos.Y := i + SelStartPos.Y; if SelectLineStr = SetLineStr then begin {↓行の文字列が一致しているなら 次の行にキャレットを移動} Inc(LineSelectPos.Y); LineSelectPos.X := 0; Editor_SetCaretPos(hwnd, POS_LOGICAL_W, @LineSelectPos); end else begin {↓行の文字列が一致していないなら} Editor_SetCaretPos(hwnd, POS_LOGICAL_W, @LineSelectPos); if (LastLineBreakStyle(SelectLineStr)=lbsCRLF) and (LastLineBreakStyle(SetLineStr)=lbsCRLF) then begin {改行コードが両方に含まれている場合削除して挿入する} Delete(SetLineStr, Length(SetLineStr)-1, 2); Editor_SetSelLength(hwnd, Length( SelectLineStr )-2 ); Editor_InsertStringW(hwnd, @SetLineStr[1] ); {※改行コード分はキャレットを移動} //Editor_GetSelStart(hwnd, POS_LOGICAL_W, @LineSelectPos); {↓次の行にキャレットを移動} Inc(LineSelectPos.Y); LineSelectPos.X := 0; Editor_SetCaretPos(hwnd, POS_LOGICAL_W, @LineSelectPos); end else begin Editor_SetSelLength(hwnd, Length(SelectLineStr) ); Editor_InsertStringW(hwnd, @SetLineStr[1] ); end; end; end; if SelectStrList.Count < SetStrList.Count then begin //選択文字列行が少ない場合は挿入処理 InsertStr := ''; for j := SelectStrList.Count to SetStrList.Count-1 do begin InsertStr := InsertStr + SetStrList.Items[j]; end; Editor_InsertStringW(hwnd, @InsertStr[1]); end else if SetStrList.Count < SelectStrList.Count then begin //セット文字列行が少ない場合は削除処理 DeleteLength := 0; for j := SetStrList.Count to SelectStrList.Count-1 do begin Inc(DeleteLength, Length(SelectStrList.Items[j]) ); end; Editor_SetSelLength(hwnd, DeleteLength); Editor_InsertStringW(hwnd, @WideString( '' )[1]); end; finally SetStrList.Free; end; finally SelectStrList.Free; end; Editor_SetCaretPos(hwnd, POS_LOGICAL_W, @SelStartPos); Editor_SetSelLength(hwnd, Length(SetStr)); SetSelectCaretPos(hwnd, CaretPoint); end; //procedure testSetSelectText(hwnd: HWND); // procedure SetSelectTextRun(SelStart, SelEnd: TPoint; S: WideString); // begin // Editor_SetSelView(hwnd, @SelStart, @SelEnd); // SetSelectText(hwnd, S); // end; // // procedure CheckSetSelectText(S: WideString); // begin // Editor_ExecCommand(hwnd ,EEID_EDIT_SELECT_ALL); // Check(S, Editor_GetSelTextW_Delphi(hwnd)); // end; //var // ws :WideString; //begin // Editor_ExecCommand(hwnd ,EEID_EDIT_SELECT_ALL); // {↑全選択する} // // ws := // '  0001' + #13#10 + // ' 0002' + #13#10 + // #9 +'0003' + #13#10; // // Editor_InsertStringW(hwnd, @ws[1]); // SetSelectTextRun(Point(0, 1), Point(0, 2), 'あいうえお'#13#10); // CheckSetSelectText( // '  0001' + #13#10 + // 'あいうえお' + #13#10 + // #9 +'0003' + #13#10 // ); // // Editor_InsertStringW(hwnd, @ws[1]); // SetSelectTextRun(Point(0, 1), Point(0, 2), 'あいうえお'); // CheckSetSelectText( // '  0001' + #13#10 + // 'あいうえお' + // #9 +'0003' + #13#10 // ); // // Editor_InsertStringW(hwnd, @ws[1]); // SetSelectTextRun(Point(8, 0), Point(0, 2), 'あいうえお'); // CheckSetSelectText( // '  0001' + // 'あいうえお' + // #9 +'0003' + #13#10 // ); // // Editor_InsertStringW(hwnd, @ws[1]); // SetSelectTextRun(Point(8, 0), Point(0, 2), #13#10'あいうえお'#13#10'かきくけこ'#13#10); // CheckSetSelectText( // '  0001' + #13#10 + // 'あいうえお' + #13#10 + // 'かきくけこ' + #13#10 + // #9 +'0003' + #13#10 // ); //end; //------------------------------ {------------------------------- // 選択しているキャレット位置を先頭か終端かに変更する 備考: GetSelStartとGetSelEndで選択範囲の先頭終端が わかるのでSetSelViewでCaret位置を指定している。 履歴: 2002/09/01 作成 2003/05/26 コメント記述 //------------------------------} procedure SetSelectCaretPos(hwnd: HWND; CaretPoint: TSelectCaretPoint); var SelStartPos, SelEndPos: TPoint; begin Editor_GetSelStart(hwnd, POS_VIEW, @SelStartPos); Editor_GetSelEnd(hwnd, POS_VIEW, @SelEndPos); case CaretPoint of scpStart: //キャレットは前 begin Editor_SetSelView_Delphi(hwnd, SelEndPos, SelStartPos); end; scpEnd: //キャレットは後ろ begin Editor_SetSelView_Delphi(hwnd, SelStartPos, SelEndPos); end; end; end; //------------------------------ {------------------------------- // 選択しているキャレットの位置が先頭か終端か求める 備考: 履歴: 2002/09/01 作成 2003/05/26 コメント記述 //------------------------------} function GetSelectCaretPos(hwnd: HWND): TSelectCaretPoint; function IsSamePoint(A, B: TPoint): Boolean; begin if (A.X = B.X) and (A.Y = B.Y) then begin Result := True; end else begin Result := False; end; end; var SelStartPos, SelEndPos, CaretPos: TPoint; begin Editor_GetSelStart(hwnd, POS_VIEW, @SelStartPos); Editor_GetSelEnd(hwnd, POS_VIEW, @SelEndPos); Editor_GetCaretPos(hwnd, POS_VIEW, @CaretPos); if IsSamePoint(SelStartPos, CaretPos) then begin Result := scpStart end else if IsSamePoint(SelEndPos, CaretPos) then begin Result := scpEnd end else begin Result := scpStart //全選択状態の時はここにくるが便宜上Startにしておく end; end; //------------------------------ //キャレットより前方の文字列を取得 function CaretFrontText(hwnd: HWND): WideString; var SelStartPos, SelEndPos: TPoint; CaretPoint: TSelectCaretPoint; begin Editor_GetSelStart(hwnd, POS_VIEW, @SelStartPos); Editor_GetSelEnd(hwnd, POS_VIEW, @SelEndPos); CaretPoint := GetSelectCaretPos(hwnd); StartSelection(hwnd); Result := Editor_GetSelTextW_Delphi(hwnd); Editor_SetSelView_Delphi(hwnd, SelStartPos, SelEndPos); SetSelectCaretPos(hwnd, CaretPoint); end; //キャレットより後方の文字列を取得 function CaretBackText(hwnd: HWND): WideString; var SelStartPos, SelEndPos: TPoint; CaretPoint: TSelectCaretPoint; begin Editor_GetSelStart(hwnd, POS_VIEW, @SelStartPos); Editor_GetSelEnd(hwnd, POS_VIEW, @SelEndPos); CaretPoint := GetSelectCaretPos(hwnd); EndSelection(hwnd); Result := Editor_GetSelTextW_Delphi(hwnd); Editor_SetSelView_Delphi(hwnd, SelStartPos, SelEndPos); SetSelectCaretPos(hwnd, CaretPoint); end; //Delphi-SelStartのEmEditor版 function GetSelStartIndex(hwnd: HWND): Integer; var ReturnWStr: WideString; CaretPosBuf: TSelectCaretPoint; begin CaretPosBuf := GetSelectCaretPos(hwnd); SetSelectCaretPos(hwnd, scpStart); {↑キャレット位置を選択範囲の前方に設定している} ReturnWStr := CaretFrontText(hwnd); Result := Length(ReturnWStr); SetSelectCaretPos(hwnd, CaretPosBuf); end; //Delphi-SelStartのEmEditor版 //改行コードはCRLFのみに対応 procedure SetSelStartIndex(hwnd: HWND; Value: Integer); var i, LineCount, LineLength, BufferIndex: Integer; SelStartPoint: TPoint; begin LineCount := Editor_GetLines(hwnd, POS_LOGICAL_W); BufferIndex := 0; for i := 0 to LineCount-1 do begin LineLength := Length(Editor_GetLineW_Delphi(hwnd, i)); LineLength := LineLength + Length(#13#10); BufferIndex := BufferIndex + LineLength; if Value < BufferIndex then {2004/05/16 <= から < に変更した} begin BufferIndex := BufferIndex - LineLength; break; end; end; SelStartPoint.Y := i; SelStartPoint.X := Value - BufferIndex; Editor_SetCaretPos(hwnd, POS_LOGICAL_W, @SelStartPoint); end; //Delphi-SelLengthのEmEditor版 function GetSelLength(hwnd: HWND): Integer; var ReturnWStr: WideString; begin ReturnWStr := Editor_GetSelTextW_Delphi(hwnd); Result := Length(ReturnWStr); end; procedure SetSelLength(hwnd: HWND; Value: Integer); begin Editor_SetSelLength(hwnd, Value); end; ////Delphi-LengthのEmEditor版 //function LengthW(const S: WideString): Integer; //begin // Result := Length(S) - StrCount(CR+LF, S); // {↑EmEditorでの文字列長ははCRLFを1として数える} //end; //ファイル名を取得する関数 //無題の場合EmptyStringが返る function Editor_Info_EI_GET_FILE_NAMEW_Delphi(hwnd: HWND): WideString; begin SetLength(Result, (MAX_PATH+1)); //MAX_PATH+ヌル文字分のWideStringバッファ確保 Editor_Info(hwnd, EI_GET_FILE_NAMEW, Integer(@Result[1])); //↑Integer(@Result[1])はInteger(PWideChar(Result))でもいいかもしれない Result := WideString(PWideChar(Result)); end; //Menuの属性を返す関数 function MenuOption(CheckValue, EnabledValue: Boolean): Integer; begin Result := MF_STRING; if CheckValue then begin Result := Result or MF_CHECKED; end; if not EnabledValue then begin Result := Result or MF_GRAYED; end; end; end.