ページ 2 / 3
返信@yu0627
Posted: 2006年3月22日(水) 17:25
by yu0627
みなさん、ありがとうございます。
やってることは、「スクロールバーを操作しましたよ」とリッチエディットに
うそ情報を与えて表示位置を更新させているだけです。
先にWM_VSCROLLをしてからWM_HSCROLLを行うと微妙にずれますのでこのままの
順番で。
SetKeywordColor関数の内容が手元にあるものとyu0627さんが持っているもので
恐らく違うので、違う動きをするかもしれませんが試す価値はあると思います。
ありがとうございます。
こちらで試したところ、ちらつきはありますが、文字を入力・削除しても大丈夫でした。
毎度毎度すみませんが、タブの文字数(半角何文字分か)を設定する方法はどうすればいいでしょうか。
http://www.geocities.co.jp/SiliconValle ... 幅を変えたいのですが
↑のサイトのコードを参考にやってみましたが、だめでした。
Re: 返信@yu0627
Posted: 2006年3月23日(木) 19:24
by ノッチ
> 毎度毎度すみませんが、タブの文字数(半角何文字分か)を設定する方法はどうすればいいでしょうか。
コード: 全て選択
'タブの間隔を設定
'SendMessage(hEdit, EM_SETTABSTOPS, 1, 4*LOWORD(GetDialogBaseUnits()) \ 2)
以前のコードに上記がありましたが、
EM_SETTABSTOPSの使い方が微妙に違っています。
lParamはタブ間隔を入れた配列のアドレス。
wParamはその配列の数を入れます。
上記のコードを修正すると
コード: 全て選択
Dim a[0] As Long
a[0]=8*LOWORD(GetDialogBaseUnits()) \ 2
SendMessage(hEdit, EM_SETTABSTOPS, 1, VarPtr(a[0]))
こんな感じになります。
ま~無理に配列にする必要もないですが。
lParamが配列な理由は、最初のタブは狭くして、二つ目のタブは広くしたい、
なんて時に使用します。
使うことはほぼないと思いますが。
ちなみに、lParamの配列に入れる値は計算しなくても値を直指定できます。
何ドットという指定はできませんが、細かく指定する場合はDialogBaseUnitsを
調べてみてください。
返信@yu0627
Posted: 2006年3月23日(木) 20:33
by yu0627
ノッチさん、ありがとうございます。
お陰で、タブの文字数を設定することが出来ました。
最後に、アンドゥ機能を付けたいと思っています。
「リッチエディットのアンドゥ機能を使えば良いじゃないか」という人もいるかもしれませんが、
文字色変更がアンドゥに引っかかってしまうので、現在外してあります。
Delphiでよいコードがあったのですが、なんせDelphiなどやったことがないので、
コードの解析に手間取っております。
どなたかDelphiのコードを解析してABのコードに書き直してくれる人はいませんでしょうか。
Re: 返信@yu0627
Posted: 2006年3月24日(金) 19:37
by ノッチ
> 文字色変更がアンドゥに引っかかってしまうので、現在外してあります。
文字色変更がアンドゥに入ってしまうなら、アンドゥする時に色変更だった場合
に色変更が終わるまでアンドゥを繰り返すというのも手です。
ただし、アンドゥ処理内容が色変更か、文字入力かを判断するの方法がわかりません。
(それにあまりスマートな方法ではありませんし)
> Delphiでよいコードがあったのですが、なんせDelphiなどやったことがないので、
> コードの解析に手間取っております。
そのページがわからないので解析のしようがありません。残念です。
で、その他の方法としては操作が色変更の場合にアンドゥに記録しない
というのも思いついたのですが、リッチエディットがどのタイミングで
アンドゥに記録しているのかがわからないので打つ手なしといった感じです。
返信@yu0627
Posted: 2006年3月25日(土) 17:47
by yu0627
そのページがわからないので解析のしようがありません。残念です。
すみません。ページ載せるのをの忘れました(爆)
http://www.psn.ne.jp/~nagayama/program/0024.html
このページの一番下からダウンロードできるRichEditVX.lzhの中にある「RichEditVx.Pas」の
アンドゥ部分のみ解析していただきたいです。
これ、途中で構造体を拡張しているような...。良くは分かりませんが。
何度もすみません。
Re: 返信@yu0627
Posted: 2006年3月27日(月) 00:02
by ノッチ
お久しぶりノッチです。
家にネット環境がないので出張でネット付のホテルに泊まるか会社から(!?)しか
ネットができません。
ということでリンク先を見たのですが、lzhファイルが見つからないですね。
アンドゥ処理は興味があったので残念です。
返信@yu0627
Posted: 2006年3月27日(月) 19:15
by yu0627
お久しぶりノッチです。
家にネット環境がないので出張でネット付のホテルに泊まるか会社から(!?)しか
ネットができません。
ということでリンク先を見たのですが、lzhファイルが見つからないですね。
アンドゥ処理は興味があったので残念です。
どうやら、FTPサイトでファイル要求を送信中にタイムアウトになるようです。
ですので、こちらをご覧ください。
http://www.exfiction.net/~yu0627/temp/RichEditVx.Pas
このコードを見ると、「FUndoMemory: array[0..UndoCount] of TRichUndoMemory;」って
書いてあります。よく分からないのですが、配列を途中で拡大しているようです。
こんなことABで可能でしたっけ...。
Posted: 2006年3月27日(月) 19:57
by hira
このコードを見ると、「FUndoMemory: array[0..UndoCount] of TRichUndoMemory;」って
書いてあります。よく分からないのですが、配列を途中で拡大しているようです。
こんなことABで可能でしたっけ...。
TRichUndoMemoryという名前からすればクラスですよね(多分)。
私の場合、こういうときは無理矢理実装する派です(^^;;
ポインタのポインタ(現在の所うまく処理できませんが)を1つ定義し、要素数分のポインタを格納する領域を用意します(mallocやcallocを使用すること)。
それぞれのポインタは、いったんクラスのポインタの変数にNewで作成したものを各要素に代入して保存します(ポインタのポインタをうまく処理できないため)。
ポインタを使ってクラスを呼び出すときは、クラスのポインタの変数に保存しておいたポインタを代入して呼び出します。
解放するときもやはりクラスのポインタの変数に代入してDeleteです。
※こうなると、
コード: 全て選択
p As *Class->func()
のような書き方ができるか、ポインタのポインタに対応していただけるとありがたいということになります(^^;;
Posted: 2006年3月27日(月) 22:02
by ノッチ
さらっと見ただけですので間違ってるかも知れませんが、内容的には独自で
アンドゥ、リドゥ処理を実装しているようです。
やはり既存のアンドゥ、リドゥは拡張性のないものなのか、ただ情報があまり
出てないだけなのか。
ですので、リッチエディットをサブクラス化してWM_KEYDOWN、WM_COPY、
WM_PASTEあたり(?)を独自で処理すればOKです。
やり方は、WM_KEYDOWN時やペースト時にmallocでメモリを確保。
確保したメモリに入力したどの文字位置から何文字削除した、どの文字位置に
なんという文字列を追加した等を書き込み、その作業が行われるたびに
メモリを確保->メモリに情報を書き込む->メモリに前のメモリのアドレスを追加する
を繰り返し、Ctrl+Zでその作業を巻き戻す、ついでにリドゥバッファに反対の
作業を(削除した場合は追加)を書き込み・・・。
と、書くのは簡単ですが実装は大変そうです。
返信@yu0627
Posted: 2006年3月29日(水) 10:54
by yu0627
hiraさん、ノッチさん、ありがとうございます。
ポインタのポインタ(現在の所うまく処理できませんが)を1つ定義し、要素数分のポインタを格納する領域を用意します(mallocやcallocを使用すること)。
それぞれのポインタは、いったんクラスのポインタの変数にNewで作成したものを各要素に代入して保存します(ポインタのポインタをうまく処理できないため)。
ポインタを使ってクラスを呼び出すときは、クラスのポインタの変数に保存しておいたポインタを代入して呼び出します。
解放するときもやはりクラスのポインタの変数に代入してDeleteです。
ちょっとうまく理解できませんが...。
例えばどうするのでしょうか。
さらっと見ただけですので間違ってるかも知れませんが、内容的には独自で
アンドゥ、リドゥ処理を実装しているようです。
やはり既存のアンドゥ、リドゥは拡張性のないものなのか、ただ情報があまり
出てないだけなのか。
ですので、リッチエディットをサブクラス化してWM_KEYDOWN、WM_COPY、
WM_PASTEあたり(?)を独自で処理すればOKです。
やり方は、WM_KEYDOWN時やペースト時にmallocでメモリを確保。
確保したメモリに入力したどの文字位置から何文字削除した、どの文字位置に
なんという文字列を追加した等を書き込み、その作業が行われるたびに
メモリを確保->メモリに情報を書き込む->メモリに前のメモリのアドレスを追加する
を繰り返し、Ctrl+Zでその作業を巻き戻す、ついでにリドゥバッファに反対の
作業を(削除した場合は追加)を書き込み・・・。
と、書くのは簡単ですが実装は大変そうです。
確かに難しそうですね...。
あと、ストリームをやろうとしたのですが、「dwCookie」の部分に何を代入しようか迷っています。
ファイルの読み込みと保存ならファイルハンドルを指定すればよいのですが、
ただのバッファへの格納にどの値を代入すればよいのでしょうか...。
Re: 返信@yu0627
Posted: 2006年3月29日(水) 19:28
by Tomorrow
ちょっとうまく理解できませんが...。
例えばどうするのでしょうか。
自分なりに解釈してコード化してみました。
あってるでしょうか? >> hiraさん
[ここをクリックすると内容が表示されます] [ここをクリックすると非表示にします]コード: 全て選択
#N88BASIC
Class TRichUndoMemory
Public
Sub TRichUndoMemory()
Print "construct"
End Sub
Sub ~TRichUndoMemory()
Print "destruct"
End Sub
Sub foo() As Long
Print "foo"
End Sub
End Class
'ポインタのポインタ(現在の所うまく処理できませんが)を1つ定義し、
Dim FUndoMemory As **TRichUndoMemory
'要素数分のポインタを格納する領域を用意します(mallocやcallocを使用すること)。
FUndoMemory = malloc(SizeOf(*TRichUndoMemory)*2)
'それぞれのポインタは、いったんクラスのポインタの変数に
Dim temp As *TRichUndoMemory
'Newで作成したものを
temp = New TRichUndoMemory
'各要素に代入して保存します(ポインタのポインタをうまく処理できないため)。
FUndoMemory[0] = temp
'ポインタを使ってクラスを呼び出すときは、クラスのポインタの変数に保存しておいたポインタを代入して
Dim temp2 As *TRichUndoMemory
temp2 = FUndoMemory[0]
'呼び出します。
temp2->foo()
'FUndoMemory[0]->foo() - 文法が間違っています
'解放するときもやはりクラスのポインタの変数に代入してDeleteです。
Dim temp3 As *TRichUndoMemory
temp3 = FUndoMemory[0]
Delete temp3
FUndoMemory[0] = NULL
free(FUndoMemory)
>> yu0627さん
このコードを見ると、「FUndoMemory: array[0..UndoCount] of TRichUndoMemory;」って
書いてあります。よく分からないのですが、配列を途中で拡大しているようです
と書いてらっしゃいますが、"配列を途中で拡大している"とは先のDelphiのソースのどのあたりでしょう?
私には見つかりませんでした。
P.S.
今回の場合だとmallocで確保した配列よりも、リスト構造の方が向いているんじゃないでしょうか?
Posted: 2006年3月29日(水) 20:14
by hira
自分なりに解釈してコード化してみました。
あってるでしょうか?
あのわかりにくい説明を解釈していただいてありがとうございますm(_ _)m
そういう感じです。
デメリットはどうしてもコードが読みにくくなることですね…(^^;
今回の場合だとmallocで確保した配列よりも、リスト構造の方が向いているんじゃないでしょうか?
どうなんでしょう。リスト構造の方が処理が高速な感じはしますが…。
※それ以前に、コードの読みにくさを改善するというメリットが(何
Posted: 2006年3月30日(木) 01:17
by ノッチ
>
今回の場合だとmallocで確保した配列よりも、リスト構造の方が向いているんじゃないでしょうか?
とあるように数日前からリスト構造でのアンドゥバッファ作成をしていました。
まだ作成途中ですが、ABの記述方法がよくわからず(メモリ操作系)
ちょっと挫折気味です。
[ここをクリックすると内容が表示されます] [ここをクリックすると非表示にします]コード: 全て選択
'-----------------------------------------------------------------------------
' イベント プロシージャ
'-----------------------------------------------------------------------------
' このファイルには、ウィンドウ [MainWnd] に関するイベントをコーディングします。
' ウィンドウ ハンドル: hMainWnd
' TODO: この位置にグローバルな変数、構造体、定数、関数を定義します。
Declare Function MoveMemory Lib "kernel32" Alias "RtlMoveMemory" (pDest As VoidPtr,pSrc As VoidPtr,length As Long) As Long'?
Type Undo
pNext As *Undo
nStart As Long
nEnd As Long
szText As String
nCount As Long
End Type
Const UNDO_INPUT=0
Const UNDO_DELETE=1
Dim hEdit As HWND
Dim pOldEditProc As VoidPtr
Dim pUndo As *Undo
Function CreateUndoBuffer() As *Undo
CreateUndoBuffer=malloc(20)
CreateUndoBuffer->pNext=0
End Function
Sub DestroyUndoBuffer(pBuffer As *Undo)
Dim p As *Undo
While pBuffer->pNext
p=pBuffer->pNext
free(pBuffer)
pBuffer=p
Wend
End Sub
Sub AddUndo(undokind As Long,nStart As Long,nEnd As Long,pszText As BytePtr,count As Long) As *Undo
Dim ptmp As *Undo
ptmp=pUndo
pUndo=malloc(20)
pUndo->pNext=ptmp
pUndo->nStart=nStart
pUndo->nEnd=nEnd
pUndo->szText=pszText
pUndo->nCount=count
End Sub
Function EditProc(hWnd As HWND,message As DWord,wParam As WPARAM,lParam As LPARAM) As LRESULT
Dim nStart As Long,nEnd As Long,length As Long
Dim buf As BytePtr,buf2 As BytePtr
Select Case message
Case WM_KEYDOWN
If wParam>=Asc("A") and wParam<=Asc("Z") Then
nStart=LOWORD(SendMessage(hEdit,EM_GETSEL,0,0))
nEnd=HIWORD(SendMessage(hEdit,EM_GETSEL,0,0))
length=1
AddUndo(UNDO_INPUT,nStart,nEnd,"",length)
ElseIf wParam=VK_DELETE Then
nStart=LOWORD(SendMessage(hEdit,EM_GETSEL,0,0))
nEnd=HIWORD(SendMessage(hEdit,EM_GETSEL,0,0))
If nStart=nEnd Then
If GetWindowTextLength(hEdit)=nStart Then goto *EditProcEnd'Selectから抜け出す記述がよくわからない
length=1
Else
length=nEnd-nStart
End If
buf=malloc(GetWindowTextLength(hEdit)+1)
buf2=calloc(length+1)
GetWindowText(hEdit,buf,GetWindowTextLength(hEdit)+1)
MoveMemory(buf2,buf,length)
AddUndo(UNDO_DELETE,nStart,nEnd,buf2,length)
free(buf)
free(buf2)
End If
End Select
*EditProcEnd
EditProc=CallWindowProc(pOldEditProc,hWnd,message,wParam,lParam)
End Function
'-----------------------------------------------------------------------------
' ウィンドウメッセージを処理するためのコールバック関数
Function MainWndProc(hWnd As HWND, dwMsg As DWord, wParam As WPARAM, lParam As LPARAM) As DWord
' TODO: この位置にウィンドウメッセージを処理するためのコードを記述します。
' イベントプロシージャの呼び出しを行います。
MainWndProc=EventCall_MainWnd(hWnd,dwMsg,wParam,lParam)
End Function
'-----------------------------------------------------------------------------
' ここから下は、イベントプロシージャを記述するための領域になります。
Sub MainWnd_Destroy()
DestroyUndoBuffer(pUndo)
Test_DestroyObjects()
PostQuitMessage(0)
End Sub
Sub MainWnd_Create(ByRef CreateStruct As CREATESTRUCT)
pUndo=CreateUndoBuffer()
hEdit=GetDlgItem(hMainWnd,EditBox1)
pOldEditProc=SetWindowLong(hEdit,GWL_WNDPROC,AddressOf(EditProc) As Long) As VoidPtr
End Sub
Sub MainWnd_Resize(SizeType As Long, cx As Integer, cy As Integer)
MoveWindow(hEdit,0,0,cx,cy,1)
End Sub
Sub MainWnd_Activate(state As Integer, minimized As Integer)
If state<>WA_INACTIVE Then SetFocus(hEdit)
End Sub
新規プロジェクトにエディットボックスを配置してMainWnd.sbpに
上記コードをコピペしてみてください。
途中にdebugを入れたりして、こんな方法かというのを感じてもらえればと。
まだA~Zキーを押した時とDeleteキーのみですが。
まだバグもあると思いますし、AddUndoで未知の文字列が開放されます。
&リドゥは未実装です。
ほんとにイメージとして捉えて下さい。
返信@yu0627
Posted: 2006年3月30日(木) 21:42
by yu0627
ノッチさん、ありがとうございます。
しっかりとサブクラス化も成功しています。
コードを見ると、現在はUNDOバッファを追加していくところまでのようですが。
少し僕も改造してみました。追加箇所は、スペースキー・エンターキー・バックスペースキーへの
対応です。少し、追加位置を間違っているかも知りませんが。
[ここをクリックすると内容が表示されます] [ここをクリックすると非表示にします]コード: 全て選択
Function EditProc(hWnd As HWND, message As DWord, wParam As WPARAM, lParam As LPARAM) As LRESULT
Dim nStart As Long,nEnd As Long,length As Long
Dim buf As BytePtr,buf2 As BytePtr
Select Case message
Case WM_KEYDOWN
If (wParam>=Asc("A") and wParam<=Asc("Z")) or wParam=VK_SPACE or wParam=VK_RETURN Then
nStart=LOWORD(SendMessage(hEdit,EM_GETSEL,0,0))
nEnd=HIWORD(SendMessage(hEdit,EM_GETSEL,0,0))
length=1
AddUndo(UNDO_INPUT,nStart,nEnd,"",length)
ElseIf wParam=VK_DELETE Then
nStart=LOWORD(SendMessage(hEdit,EM_GETSEL,0,0))
nEnd=HIWORD(SendMessage(hEdit,EM_GETSEL,0,0))
If nStart=nEnd Then
If GetWindowTextLength(hEdit)=nStart Then goto *EditProcEnd'Selectから抜け出す記述がよくわからない
length=1
Else
length=nEnd-nStart
End If
buf=malloc(GetWindowTextLength(hEdit)+1)
buf2=calloc(length+1)
GetWindowText(hEdit,buf,GetWindowTextLength(hEdit)+1)
MoveMemory(buf2,buf,length)
AddUndo(UNDO_DELETE,nStart,nEnd,buf2,length)
free(buf)
free(buf2)
ElseIf wParam=VK_BACK Then
nStart=LOWORD(SendMessage(hEdit,EM_GETSEL,0,0))
nEnd=HIWORD(SendMessage(hEdit,EM_GETSEL,0,0))
If nStart=nEnd Then
If GetWindowTextLength(hEdit)=nStart Then goto *EditProcEnd'Selectから抜け出す記述がよくわからない
length=1
Else
length=nEnd-nStart
End If
buf=malloc(GetWindowTextLength(hEdit)+1)
buf2=calloc(length+1)
GetWindowText(hEdit,buf,GetWindowTextLength(hEdit)+1)
MoveMemory(buf2,buf,length)
AddUndo(UNDO_DELETE,nStart,nEnd,buf2,length)
free(buf)
free(buf2)
End If
End Select
*EditProcEnd
EditProc=CallWindowProc(pOldEditProc,hWnd,message,wParam,lParam)
End Function
どうやってバッファを呼び出すのか分かりませんが...。
Re: 返信@yu0627
Posted: 2006年3月31日(金) 00:52
by ノッチ
[ここをクリックすると内容が表示されます] [ここをクリックすると非表示にします]コード: 全て選択
ElseIf wParam=VK_DELETE Then
nStart=LOWORD(SendMessage(hEdit,EM_GETSEL,0,0))
nEnd=HIWORD(SendMessage(hEdit,EM_GETSEL,0,0))
If nStart=nEnd Then
If GetWindowTextLength(hEdit)=nStart Then goto *EditProcEnd'Selectから抜け出す記述がよくわからない
length=1
Else
length=nEnd-nStart
End If
buf=malloc(GetWindowTextLength(hEdit)+1)
buf2=calloc(length+1)
GetWindowText(hEdit,buf,GetWindowTextLength(hEdit)+1)
MoveMemory(buf2,buf,length)
AddUndo(UNDO_DELETE,nStart,nEnd,buf2,length)
free(buf)
free(buf2)
この部分で
If GetWindowTextLength(hEdit)=nStart Then
としてるのはカーソルがエディットの最後にある時はDeleteを押しても
なにもしないよ。ということです。
ですので、BackSpaceの場合はカーソルが先頭にあって何も選択してない時に
何もしない、となります。
で、投げっぱなしのようになってしまうのですが、僕の家にはネット環境が
ありません。今は出張でネット付のホテルに泊まっていたのでコメントできたの
ですが、出張が終わるので時々会社から覗く(それと簡単なコメント)に
なってしまいます。
とりあえず、やり方はCtrl+Zが押されたことを検出(アクセラレータ使ってるか
WM_KEYDOWN wParam=Zの時にAsyncKeyState(VK_CONTROL)してるか)し、
それを奪い取って(CallWindowProcに渡さずExit Functionで抜ける)、
バッファから文字を追加、削除する。といった感じです。
削除をアンドゥする時はnStart~nEndの文字列をszTextに変換する。
文字追加をアンドゥする時は追加された文字をnStartの位置から削除する。
追加の場合、前のコードは考慮されていませんでしたが、文字選択状態で
"A"なんかを押すと選択部分を削除して文字追加になるのでその部分も
考えなければいけませんでした。
あまり役に立たずにいなくなります。すみません。
どなたかサポートをお願いします。