Discussion:
riched20 - save and load cursor position (Newyana2)
(too old to reply)
Newyana2
2023-06-13 13:10:17 UTC
Permalink
"R.Wieser" <***@is.invalid> wrote

| As you have mentioned to have done quite a bit with the RichEdit control,
| maybe you can help me understand a problem I had a while ago.
|
| I loaded an RTF file (containing multiple font sizes) into a RichEd20
| control, Than Itried to save the cursor position using EM_GETSCROLLPOS and
| later restore the position using EM_SETSCROLLPOS.
|
| The problem ? Even when I executed those two commands directly after the
| other* the cursor jumped up a number of lines.
|

I sandwich operations between LockWindowUpdate
on the parent window. I also sometimes add EnableWindow
on the RichEdit itself. The former blocks painting. The
latter blocks input from mouse and keyboard.

DoEvents
LockWindowUpdate ParentWindow.hWnd
EnableWindow hRE.hWnd, False

' [Here I might do something like read out the
RE content and run the string through a tokenizing
routine to update formatting, colors, or whatever,
then put back the new RTF text.]

EnableWindow hRE.hWnd, True
LockWindowUpdate 0

At some point MS decided LockWindowUpdate should not be
used and recommended a WM_SETREDRAW message instead.
If I remember correctly, LWU wraps that message along with
other operations. I never understood the hubbub. Raymond
Chen goes on about how all hell will break loose if you start
running LWU on multiple windows because there's no window
handle for unlocking. But I don't see a problem with that. I'm
not doing it with multiple windows and I only do it for a fraction
of a second. It may be possible for another program to make
the same call during that fraction of a second while someone is
using my editor, but that's farfetched. At any rate, you can try
WM_SETREDRAW. Both block painting but keep track of changes
for painting after unlock. EnableWindow, by contrast, is used to
block input to the window entirely while a locking operation is
going on.

I find that if I can do all operations in less than 1/4 second
then the window display is smooth and the content just updates
in appearance. I don't if that covers the problem you're having.
I Guess I typically record SelStart before any operation and
then return it to that position. EM_GETSEL and EM_SETSEL.

I don't get how you're doing any of this with script, but I'll
leave you to it. :) However, getting, setting SelStart requires
a long pointer to a CHARRANGE STRUCT. The STRUCT is two
longs, which specify the starting point and ending point of
a specified set of characters. You dson't have to care about that
if you just return the CHARRANGE from GET to the SET operation,
but you will need to handle a CHARRANGE.


| P.s.
| I posted the question here as you frequent this newsgroup. But I've set
| the follow-up to "comp.os.ms-windows.programmer.win32". If thats not a
| newsgroup your newsgroup provider carries than please remove the
| redirection.
|

I'll find out, I guess. I'm setting it for both, since
it's possible someone here would be curious. I used
to visit maybe 6-7 programming groups, including
Win32 API. I didn't know there were any still left.
The only one I still go to is vb.general.discussion,
which still gets regular traffic.
R.Wieser
2023-06-13 15:45:00 UTC
Permalink
Newyana2,
I sandwich operations between LockWindowUpdate on the parent
window. I also sometimes add EnableWindow on the RichEdit itself.
I just tried either and both LockWindowUpdate and EnableWindow but alas, I
got the same jumping result.

Thanks for the suggestion though.
I'll find out, I guess. I'm setting it for both, since
it's possible someone here would be curious.
That indeed has happened. I was hoping they would move to this
(comp.os.ms-windows.programmer.win32) newsgroup too.
I used to visit maybe 6-7 programming groups
Most of the programming newsgroups I visit are of little usage to me, as my
questions are seldom about the language, but instead about the underlaying
controls, DLL functions, etc.

Regards,
Rudy Wieser
Newyana2
2023-06-13 19:14:51 UTC
Permalink
"R.Wieser" <***@is.invalid> wrote

| I just tried either and both LockWindowUpdate and EnableWindow but alas, I
| got the same jumping result.
|

I don't see how that's possible. If you do a LWU, then
save the cursor pos, then do operations, then reset
the cursor, then unlock window, it's not possible for
the window to move while you're doing all that.

Did you also save and reset the SelStart position?
If you do as above but end up with SelStart (cursor pos)
several lines away, then it will jump. You have to put
everything back as it was before you unlock.
R.Wieser
2023-06-13 20:27:29 UTC
Permalink
Newyana2,
Post by Newyana2
I don't see how that's possible.
I don't either, but it still does.

I just retried it, and it behaves the same. To double-check I removed the
unlocking, and afterwards, as expected, wasn't able to interact with the
RichEdit control anymore.

And something I did not mention before : the jumps seem to get smaller the
closer the cursor gets to the top.
Post by Newyana2
Did you also save and reset the SelStart position?
No. Why should I ?


Ehhrrmmm... I said I saved the /cursor/ position, but as you probably have
noticed fromk the used EM_xxx messages, I was trying to save and restore the
position of the "view". IOW, I'm not even touching the cursor (anymore).

For reference, here is the code thats under the button :

;-- Get the current scrollpos location
call SendMessageA,[@@hWnd],EM_GETSCROLLPOS, 0, ebx

;-- go to that same location
call SendMessageA,[@@hWnd],EM_SETSCROLLPOS, 0, ebx

(some debugging-output snipped)
Post by Newyana2
If you do as above but end up with SelStart (cursor pos)
several lines away, then it will jump.
I start with the cursor in the view. When I than click the button the view
jumps /away/ from it, in the direction of the start of the file.

Looking at the vertical scrollbar it jumps to a position halfway between the
current position an the top of the text. Scrolling the view using that
scrollbar works normally.

I have absolutily no idea what is happening. :-(

Regards,
Rudy Wieser
Newyana2
2023-06-13 21:31:15 UTC
Permalink
"R.Wieser" <***@is.invalid> wrote
|
| > Did you also save and reset the SelStart position?
|
| No. Why should I ?
|
|
| Ehhrrmmm... I said I saved the /cursor/ position, but as you probably
have
| noticed fromk the used EM_xxx messages, I was trying to save and restore
the
| position of the "view". IOW, I'm not even touching the cursor (anymore).
|
| For reference, here is the code thats under the button :
|
| ;-- Get the current scrollpos location
| call SendMessageA,[@@hWnd],EM_GETSCROLLPOS, 0, ebx
|
| ;-- go to that same location
| call SendMessageA,[@@hWnd],EM_SETSCROLLPOS, 0, ebx
|
| (some debugging-output snipped)
|
| > If you do as above but end up with SelStart (cursor pos)
| > several lines away, then it will jump.

The button? You click a button to run the test?
I don't know whether that might affect it, since it takes
focus from that window. But I'm not really understanding
what you're doing, or why you need a button.

GETSCROLLPOS is not the same as cursor position.
Cursor position is EM_EXGETSEL and EM_EX_SETSEL.
It's a CHARRANGE that describes the selection offset in
characters and the selection length. So, for example,
if you send EX_SETSEL with 100, 100 then you'll put the
cursor at the 100th character with no selection length.

SCROLLPOS is the scrolled position of the RichEdit window
in relation to the upper left corner.

How about if you just do the operation you want without
the button? In other words, do what you actually want to
do with the text. Say, for example, that you're going to
change the font of some words and the color of others. Then
you get your cursor point and scrollpoint, you lock the window,
you do your changes, you reset scroll and cursor, and finally
you unlock. Not via a button by via code. If you have to
use the button then you might need to always track cursor
and scroll positions, so that you know where they were when
the RE loses focus.

If you change the actual text then of course you'll need to
account for that. For example, if the cursor is at char 542 and
you remove the first 100 characters, then 542 will be a
different position in the text and a different line will be the line
where you were before.
R.Wieser
2023-06-14 06:16:57 UTC
Permalink
Newyana2,
Post by Newyana2
The button? You click a button to run the test?
Yes. Exactly because its a test.
Post by Newyana2
I don't know whether that might affect it, since
it takes focus from that window.
I moved it from the WM_INITDIALOG code to in a WM_COMMAND bit of code (for
the buttons event) - as a control might not be fully initialized in
WM_INITDIALOG, skewing the result.
Post by Newyana2
But I'm not really understanding what you're doing,
Currently ? Trying to figure out what causes that jump.

Later on ? Use it to save the current "view" position when I close the
program, so that it can be restored next time I open it.
Post by Newyana2
or why you need a button.
I needed a way to get those two lines of code executed somehow and I wanted
to remove as much as non-essential code as possible (the actual saving on
close and loading on opening the program) so it could not interfere (read:
cause the problem).
Post by Newyana2
GETSCROLLPOS is not the same as cursor position.
Cursor position is EM_EXGETSEL and EM_EX_SETSEL.
Yes, that was my mistake in naming the problem. Its about saving/restoring
the views position, not the cursor.
Post by Newyana2
SCROLLPOS is the scrolled position of the RichEdit window
in relation to the upper left corner.
I would have thought so too (reading the MSDN documentation), but here I am,
with two lines of code which do not do what thay are supposed to do. :-(
Post by Newyana2
How about if you just do the operation you want without
the button? In other words, do what you actually want to
do with the text.
That is what I started with and where I noticed the problem. From that I
went into "bug hunting mode", stripping as much irrelevant-to-the-problem
code as I could while keeping the problem itself. Its how I ended up with
the two lines I posted.
Post by Newyana2
If you change the actual text then of course you'll need to
account for that.
:-) I'm aware of that, thank you.

Again, my code is now retrieving the views current location and than telling
it to move *to that same position*. IOW, the view should not move at all.
Yet, it does. :-(



Extra info:
I mentioned that the view jumps to a position roughly halfway the distance
between its current position and the top of the text. Subsequent presses
of the button repeated that effect, roughly halving the distance every time.
IOW, its not an offset problem.

I also turned the whole thing upside-down by setting the views location to a
specified one, and than ask where it thought it was.

set get
----- ----
500 -> 159
12000 -> 3811
21000 -> 6669

The funny thing is that when I divide the latter by the former the factor is
the same every time : 0.31757.

Holdup, let me try something ....

I just "compiled" the program again, but removed the (dialog definition)
line specifying the dialogs default font and size. The "get" result
changed. Why ? Beats me.

I than also told the RichEdit control to load plain text (instead of RTF
text, which uses several font sizes). The "get" result again changed.

IOW, *something* is messing with the "get" result, and it ain't me. <phew!>


To be absolutily clear about it, the RichEdit control gets created and
filled with text /way/ before I press the "test" button. And the code
behind that button only contains (besides some debugging output) the
EM_GETSCROLLPOS and EM_SETSCROLLPOS commands. No "funny stuff" of any
kind.

Regards,
Rudy Wieser
R.Wieser
2023-06-14 09:47:41 UTC
Permalink
Newyana2,
... I wanted to remove as much as non-essential code as possible (the
actual saving on close and loading on opening the program) so it could
not interfere (read: cause the problem).
Just now I realized that I applied some extra settings to the RichEdit
control, and disabled all of it. As a result the "set" and "get" started to
match. Victory ! (even though not all of the RTF file gets loaded)

... well, not quite : what's causing it ?

It turns out that when use EM_EXLIMITTEXT (so I can load more than 64K of
RTF) it starts to happen. When I set it to something like 0x50000 the "set"
and "get" values start to get different, with the difference becoming larger
the higer that the EM_EXLIMITTEXT value is.

Do I have any idea why that its happening ? Nope. Is it in any way
mentioned on the MSDN webpage explaining EM_EXLIMITTEXT ? Also nope.

Regards,
Rudy Wieser
Newyana2
2023-06-14 12:13:11 UTC
Permalink
"R.Wieser" <***@is.invalid> wrote

| It turns out that when use EM_EXLIMITTEXT (so I can load more than 64K of
| RTF) it starts to happen. When I set it to something like 0x50000 the
"set"
| and "get" values start to get different, with the difference becoming
larger
| the higer that the EM_EXLIMITTEXT value is.
|
| Do I have any idea why that its happening ? Nope. Is it in any way
| mentioned on the MSDN webpage explaining EM_EXLIMITTEXT ? Also nope.
|

I use 8000000 (decimal). I don't remember how I came up
with that number. But I doubt it's doing anything but changing
the theoretical height of the window. Something else seems
to be affecting the jumping. As my father used to say when no
one would admit to breaking something, "I guess a ghost must
have done it." :)

If it were me I'd lock the window, load the file, set the
cursor position, then unlock. The cursor is automatically
scrolled into view, so you don't need the scroll position.
And scroll position could change, as you say, with font
changes. But cursor position won't change because it's
a character offset rather than a window display setting.

If you want to keep doing it as you're doing it then maybe
start eliminating factors more. For instance, you could load
the file from within code. I'm not clear about the dialogue.
You're making your own window? If you're calling GetOpenFileName
then the dialogue window is gone when you get the file path,
so it shouldn't be a factor. But if you're making your own window
or sub-frame I suppose it could affect where the focus goes.
R.Wieser
2023-06-14 12:52:41 UTC
Permalink
Newyana2,
Post by Newyana2
But I doubt it's doing anything but changing
the theoretical height of the window
I used different numbers to EM_EXLIMITTEXT, set a constant position using
EM_SETSCROLLPOS and saw the EM_GETSCROLLPOS return different values. If its
not /the/ cause than it still influences it - even though, as you remarked,
it should not have to do anything with it.
Post by Newyana2
If it were me I'd lock the window, load the file, set the
cursor position, then unlock.
Its currently not about restoring the views position (I used another approch
which seems to work alright, even if a bit clumsy), but about determining if
I should remove the EM_GET- and EM_SETSCROLLPOS from the list of usable
messages - so I won't run into something similar in the future.

By the way: I found, while googeling, a few other messages about having the
same problem. None of them mentioned a solution to it.
Post by Newyana2
If you want to keep doing it as you're doing it then maybe
start eliminating factors more.
Such as ?
Post by Newyana2
For instance, you could load the file from within code.
I'm already doing that. Starting a program after each small edit (trying to
locate the problem) and than having to load the same file over-and-over
again quickly becomes tiresome.

Besides, bughunting should not be done on something you're (unknowingly,
mistakingly) changing the parameters to. IOW, for/during my testing the
filename (and (thus) the loaded text) is a constant.
Post by Newyana2
I'm not clear about the dialogue. You're making your own window?
You always do. :-) In my case I describe a dialog in a resource file, and
than load that resource using "call DialogBoxParamA,0, IDC_DIALOG,0,offset
DialogCB,0"

Also, as mentioned, the reason why I found EM_EXLIMITTEXT to be involved is
because I effectivily disabled /everytyhing/. And that was enough to make
the problem disappear - on the cost of only seeing a partially-loaded RTF
file.


If you want to see the full sourcecode (or certain parts thereof) you only
have to ask. I have no problem with posting it (here and/or to an email
addres).

Remark: Its in Assembly (should not be too hard to read, but you never
know).

Regards,
Rudy Wieser
Newyana2
2023-06-14 16:51:41 UTC
Permalink
"R.Wieser" <***@is.invalid> wrote

| > I'm not clear about the dialogue. You're making your own window?

| You always do. :-) In my case I describe a dialog in a resource file, and
| than load that resource using "call DialogBoxParamA,0, IDC_DIALOG,0,offset
| DialogCB,0"

I've never used that. I use GetOpenFileName, to
give me a system file-open window with all the fixings.

|
| If you want to see the full sourcecode (or certain parts thereof) you only
| have to ask. I have no problem with posting it (here and/or to an email
| addres).
|
That might be good. I'm having trouble following. I thought
you said the problem was with loading a file, and now you say
it isn't. So I don't really understand what you're doing. If I
can make out the code then I might be able to run my own test.
R.Wieser
2023-06-14 18:06:58 UTC
Permalink
Newyana2,
| In my case I describe a dialog in a resource file, and than load
| that resource using "call DialogBoxParamA,0, IDC_DIALOG,0,offset
| DialogCB,0"
I've never used that. I use GetOpenFileName, to
give me a system file-open window with all the fixings.
Were talking about two very different things here : you're talking about the
dialog which allows you to select a file to be opened, I on the other hand
was describing how I create a programs window with all the controls on it.

I could use that "GetOpenFileName" dialog to select the file to load into my
programs RichEdit control, but I normally pass the filename as a commandline
argument. Like when you click a datafile and the associated program gets
executed, which than "automatically" opens the datafile.
That might be good. I'm having trouble following.
I thought you said the problem was with loading a file,
I can't remember I did. I did however mention that I loaded an RTF file
into a RichEdit control - as a base to test against.

My whole-and-only problem consists manipulating the position of the current
"view", using those EM_GETSCROLLPOS and EM_SETSCROLLPOS messages. The rest
is just fluff to be able to see what happens.

Here are the two files (.RC and .ASM). I've removed any-and-everything not
related to the problem.

Regards,
Rudy Wieser


.RC (resource) file :

- - - - - - - - - - - - - - - - - - - - - - - - - - - -

#include "RichEd20.rci"

;================================================

#define IDD_BASEWIN 1000
#define IDC_EXECUTE 1002
#define IDC_RICHEDIT 1003

IDD_BASEWIN DIALOG DISCARDABLE 300, 0, 450, 200

;-- Create a sizable window
STYLE DS_3DLOOK | WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX |
WS_MINIMIZEBOX ;| DS_CENTER

CAPTION "RICHED20 20220411"
FONT 8, "MS Sans Serif"
BEGIN

CONTROL "",IDC_RICHEDIT,"RichEdit20A"
,WS_TABSTOP | WS_BORDER ; | ES_SAVESEL | ES_NOHIDESEL
| ES_MULTILINE | WS_VSCROLL ; | ES_WANTRETURN
,5,5,445,175

PUSHBUTTON "&Execute",IDC_EXECUTE,5,185,50,11

END

;================================================

- - - - - - - - - - - - - - - - - - - - - - - - - - - -


The .ASM (Assembly) file :
- - - - - - - - - - - - - - - - - - - - - - - - - - - -

;=======================
.Target equ GUI

.486
.model flat,STDCALL
stackalign
locals

;-----------------------
.nolist

INCLUDELIB KERNEL32.LIB
INCLUDE KERNEL32.INC

INCLUDELIB USER32.LIB
INCLUDE USER32.INC

INCLUDELIB RichEd20.LIB
INCLUDE RichEd20.INC

.list
;-----------------------

;-- Load the control-ID constants (IDD_xxx and IDC_xxx, extracted from the
resource file)

% include @FileName.rhi

;=======================









;=======================
.CODE
;-----------------------

Start proc
;-- Initialize RichEdit component
call LoadLibraryA,offset @@TXT_RichEdDLL

;-- Create the programs window
call DialogBoxParamA,0,IDD_BASEWIN,0,offset DialogCB,0

call ExitProcess,eax

;=======================
.DATA
;-----------------------

@@TXT_RichEdDLL db 'RichEd20.dll',0

;-----------------------
Start endp
;=======================









;=======================
.CODE
;-----------------------

DialogCB proc
arg @@hWnd:DWORD,@@wMsg:DWORD,@@wParam:DWORD,@@lParam:DWORD
uses ebx,ecx,edx,esi,edi

movzx eax,word ptr [@@wMsg]

cmp eax,WM_INITDIALOG
je @@Init

cmp eax,WM_CLOSE
je @@Close

cmp eax,WM_COMMAND
je @@Command

@@DialogCB_Cont:
xor eax,eax
@@DialogCB9:
ret

;-----------------------

@@Init:
call SendDlgItemMessageA,[@@hWnd],IDC_RICHEDIT,EM_EXLIMITTEXT,0,10000000
;10M chars

call LoadDocument,[@@hWnd],IDC_RICHEDIT,offset [@@TXT_File]

jmp @@DialogCB_Cont

;-----------------------

@@Close:
call EndDialog,[@@hWnd],0
jmp @@DialogCB_Cont

;-----------------------

@@Command:
movzx eax,word ptr [@@wParam]

cmp eax,IDC_EXECUTE
je @@Execute

jmp @@DialogCB_Cont

;---------------

@@Execute:
call GetDlgItem,[@@hWnd],IDC_RICHEDIT
call Fail1Test,eax

jmp @@DialogCB_Cont

;=======================
.DATA
;-----------------------

;-- Make sure the 'TestRTF.doc' file is large enough (over 250 KByte
; on my machine), otherwise the problem won't show

@@TXT_File label byte
db 'TestRTF.doc',0

;-----------------------
DialogCB endp
;=======================









;=======================
.CODE
;-----------------------
LoadDocument PROCDESC hWnd:DWORD,lCtlID:DWORD,sFile:DWORD
; Load either an RTF or plain text document

LoadDocument proc
arg @@hWnd:DWORD,@@lCtlID:DWORD,@@sFile:DWORD
uses ebx
local @@rES:EDITSTREAM

cmp [@@lCtlID],0
je @@LoadDocument1

call GetDlgItem,[@@hWnd],[@@lCtlID]
mov [@@hWnd],eax
@@LoadDocument1:

lea ebx,[@@rES]
call RtlZeroMemory,ebx,size EDITSTREAM
mov [ebx].ES_pCallBack,offset @@LoadFileCB

;-- Needs to be set, as EM_SETTEXTMODE can cause an RTF file to
; be rejected, but EM_STREAMIN than *doesn't* set an error ..
mov [ebx].ES_lError,0FFFFFFF0h ;Needs to be given a real error code

call CreateFileA,[@@sFile],GENERIC_READ, FILE_SHARE_READ, 0 \
,OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0
mov [ebx].ES_lCookie,eax
cmp eax,INVALID_HANDLE_VALUE
je @@LoadDocument_E

;-- Try to load the file as RTF. Might fail.
call SendMessageA,[@@hWnd],EM_STREAMIN,SF_RTF,ebx
; and eax,eax ;# of chars read. Zero doesn't need to be an error ...
; jz @@LoadDocument_E1

cmp [ebx].ES_lError,0FFFFFFF0h ; -16 (no idea what this error means...)
jne @@LoadDocument2

;-- Try to load the file as plain text
call SetFilePointer,[ebx].ES_lCookie,0,0,FILE_BEGIN

call SendMessageA,[@@hWnd],EM_STREAMIN,SF_TEXT,ebx

@@LoadDocument2:
cmp [ebx].ES_lError,0 ;Any error?
jne @@LoadDocument_E1 ;Yep

call CloseHandle,[ebx].ES_lCookie

;-- OK --
clc
@@LoadDocument9:
ret

;---------------

@@LoadDocument_E1:
call CloseHandle,[ebx].ES_lCookie
@@LoadDocument_E:
;-- Error --
stc
jmp @@LoadDocument9

;-----------------------

@@LoadFileCB proc
arg @@lCookie:DWORD,@@pBufDat:DWORD,@@lBufSiz:DWORD,@@plReadCnt:DWORD

call ReadFile,[@@lCookie],[@@pBufDat],[@@lBufSiz],[@@plReadCnt],0

cmp eax,1
sbb eax,eax
ret
endp

;-----------------------
LoadDocument endp
;=======================









;=======================
.CODE
;-----------------------
Fail1Test PROCDESC hWnd:DWORD

Fail1Test proc
arg @@hWnd:DWORD
uses ebx
local @@rPnt:POINT

lea ebx,[@@rPnt]

;-- Get the current scrollpos location
call SendMessageA,[@@hWnd],EM_GETSCROLLPOS, 0, ebx

;-- go to that same location
call SendMessageA,[@@hWnd],EM_SETSCROLLPOS, 0, ebx

ret

;-----------------------
Fail1Test endp
;=======================









;=======================

end Start
ends

;-----------------------

- - - - - - - - - - - - - - - - - - - - - - - - - - - -
Newyana2
2023-06-14 20:48:05 UTC
Permalink
"R.Wieser" <***@is.invalid> wrote...

|code

That code doesn't really tell me much, but I tried setting up
a quickie with VB code and a VB RichTextBox, which wraps
RichEdit. I did find that it acts as you say. I don't know why.
When I get and set scrollpos it jumps, and each additional
operation moves the scrollpos up. However, if I get and set
the caret position it works fine, with no jumping:

Private Const WM_USER = &H400&
Private Const EM_GETSCROLLPOS = (WM_USER + 221)
Private Const EM_SETSCROLLPOS = (WM_USER + 222)
Private Const EM_EXGETSEL = (WM_USER + 52)
Private Const EM_EXSETSEL = (WM_USER + 55)

Private Type POINTAPI
X As Long
Y As Long
End Type

Private Type CHARRANGE
cpMin As Long
cpMax As Long
End Type


Private Declare Function SendMessageAnyW Lib "user32" Alias "SendMessageW"
(ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam
As Long) As Long


Private Sub Command1_Click() '-- load big file.
RTB.LoadFile "C:\Windows\Desktop\bible.txt", 1 '--should be over 250 KB. :)
End Sub

Private Sub Command2_Click() '-- get and set scroll position.
Dim LRet As Long, PT2 As POINTAPI

LRet = SendMessageAnyW(RTB.hWnd, EM_GETSCROLLPOS, 0&, VarPtr(PT2))
Debug.Print PT2.Y
LRet = SendMessageAnyW(RTB.hWnd, EM_SETSCROLLPOS, 0&, VarPtr(PT2))
Debug.Print PT2.Y
End Sub

Private Sub Command3_Click() '-- get and set cursor position.
Dim CR1 As CHARRANGE
On Error Resume Next
SendMessageAnyW RTB.hWnd, EM_EXGETSEL, 0, VarPtr(CR1)
Debug.Print CR1.cpMin
SendMessageAnyW RTB.hWnd, EM_EXSETSEL, 0, VarPtr(CR1)
Debug.Print CR1.cpMin
End Sub
R.Wieser
2023-06-15 06:56:48 UTC
Permalink
Newyana2,
Post by Newyana2
I did find that it acts as you say. I don't know why.
When I get and set scrollpos it jumps, and each
additional operation moves the scrollpos up.
Thank you for confirming it.

Now all I need to do is figure out how to fix it - if at all possible (have
seen it mentioned as a being bug).
Post by Newyana2
However, if I get and set the caret position it works fine,
That was never a problem. EM_SETSEL has always worked for me.

Regards,
Rudy Wieser
Apd
2023-06-15 09:43:02 UTC
Permalink
Post by R.Wieser
Newyana2,
Post by Newyana2
I did find that it acts as you say. I don't know why.
When I get and set scrollpos it jumps, and each
additional operation moves the scrollpos up.
Thank you for confirming it.
Now all I need to do is figure out how to fix it - if at all possible
(have seen it mentioned as a being bug).
I played with the VB code and found it seems to be a function of font
size and window width. If the font size is not too large and the rich
text box is wide enough, the jumping doesn't occur. I don't know
exactly what that relationship is. Very strange!
R.Wieser
2023-06-15 10:37:36 UTC
Permalink
Apd,
I played with the VB code and found it seems to be a function of font size
and window width. If the font size is not too large and the rich text box
is wide enough, the jumping doesn't occur. I don't know exactly what that
relationship is. Very strange!
Thanks for looking into it. :-)

When you made the problem disappear by making the font smaller and the
control wider, have you also tried to increase the ammount of loaded text
(double the document) ?

The problem might be a function of the total height of the document. Both
of your changes lessen it.

My own guess that EM_EXLIMITTEXT is involved does also match the (updated,
still guessed) "total height" possible cause : Limit the ammount of text
that can be loaded and you limit the hight of the document.

Hmm... I just tried to remove all line-ends within the paragraphs, but still
got the/my problem.

Regards,
Rudy Wieser
Apd
2023-06-15 11:52:46 UTC
Permalink
Post by R.Wieser
Apd,
Post by Apd
I played with the VB code and found it seems to be a function of font
size and window width. If the font size is not too large and the rich
text box is wide enough, the jumping doesn't occur. I don't know exactly
what that relationship is. Very strange!
Thanks for looking into it. :-)
When you made the problem disappear by making the font smaller and the
control wider, have you also tried to increase the ammount of loaded text
(double the document) ?
The larger the byte count, the worse it is. I already had 500K of text.
With a larger amount, it's not possible to make the window wide enough
unless you have a very wide screen. With 100K or less, the window can
be quite narrow before the effect is seen. BTW. I'm using Tahoma 10pt,
throughout.
Post by R.Wieser
The problem might be a function of the total height of the document. Both
of your changes lessen it.
I also varied the window height but it made no difference.
Post by R.Wieser
My own guess that EM_EXLIMITTEXT is involved does also match the (updated,
still guessed) "total height" possible cause : Limit the ammount of text
that can be loaded and you limit the hight of the document.
Maybe with a really tall screen it would be seen!
Post by R.Wieser
Hmm... I just tried to remove all line-ends within the paragraphs, but
still got the/my problem.
I'm using many copies of the same paragraph, 356 characters long which
wraps to the window.
R.Wieser
2023-06-16 08:23:29 UTC
Permalink
Apd,
Post by Apd
The larger the byte count, the worse it is.
I've been doing some more testing, varying the size of the file and the # of
characters on a line but could not pinpoint what caused the problem.

But something raised my suspicion : when I changed the size of the file I
noticed that the "no problem" vs "a problem" file always had near to or over
4000 lines.

So, I disabled RichEd20's wordwrap (so that the number of lines in RichEd20
matched the sourcefile, regardless of how long the lines where), and just
added and removed empty lines.

To make sure the size of the loaded file wasn't a problem the first time I
reached the "no problem" -> "problem" point again I replaced some long lines
with empty ones (the file became smaller, even though it contained the same
# of lines)

The end result ? The "no problem" -> "problem" point was when I changed
the number of lines in the file from 4096 to 4097. Yep, from 0x1000 to
0x1001. :-)

I just generated a file in which I only had the line number printed (just
over 30 KByte) and the problem occurred on the same line.

IOW, it looks like a magical number is involved, but MS "forgot" to mention
it.

Regards,
Rudy Wieser

P.s.
It looks like its influencing the EM_GETSCROLLPOS. With 4095 lines the
"view"s Y position is 0xFEFE, with twice the ammount of lines its 0xFF8F.
Not really twice as much height, wouldn't you say ? :-)
Apd
2023-06-16 12:34:57 UTC
Permalink
"R.Wieser" wrote:
[...]
Post by R.Wieser
The end result ? The "no problem" -> "problem" point was when I changed
the number of lines in the file from 4096 to 4097. Yep, from 0x1000 to
0x1001. :-)
In a test I did, the jumping happened on exactly 4096 lines but only
when EM_GETSCROLLPOS > 32768. Below 32768, the jumping stopped. This
used short lines with no wrap in 10pt Courier New.
Post by R.Wieser
IOW, it looks like a magical number is involved, but MS "forgot" to
mention it.
Yes, magic numbers. The same problem from 2005:
"RichTextBox scrolling incorrect"
<https://groups.google.com/g/microsoft.public.vb.controls/c/o1NiVb526NE>

"... problem occurs if fontheight in pixels * line count > 64k"

I tried more than 4096 lines with a smaller font (8pt Courier New) and
there was no jumping.
Post by R.Wieser
It looks like its influencing the EM_GETSCROLLPOS. With 4095 lines the
"view"s Y position is 0xFEFE, with twice the ammount of lines its 0xFF8F.
Not really twice as much height, wouldn't you say ? :-)
See also this:

<https://learn.microsoft.com/en-us/windows/win32/controls/em-getscrollpos>

"The values returned in the POINT structure are 16-bit values (even in
the 32-bit wide fields)".

What a mess!
R.Wieser
2023-06-16 13:51:27 UTC
Permalink
Apd,
In a test I did, the jumping happened on exactly 4096 lines but only when
EM_GETSCROLLPOS > 32768. Below 32768, the jumping stopped. This used short
lines with no wrap in 10pt Courier New.
I did the test using a flat textfile. To get a 4096 lines with a height
less than 32768 the font size needs to be 8 units or less.

Though in my case I had 4096 lines and (as mentioned) EM_GETSCROLLPOS
returned a value of just under 0xFF00 when the jumping stopped. Thats quite
a bit higher as what you found ...

I think I also found your first link (I did quite bit of googeling trying to
find out what caused the problem), but as it was mostly guesses I put it
aside. Ah yes, april last year.
"The values returned in the POINT structure are 16-bit values (even in the
32-bit wide fields)".
Damn! Thats something I missed, even though I suspected something like it.

Though assuming that is clipped to 16 bits it doesn't explain why doubling
my file resulted in a larger value (from 0xFEFE to 0xFF8F) : any value below
65536 doubled but modulo 65536 should become less than the doubled value,
not more. Odd.
What a mess!
It certainly is. Even just by the absense of a clear warning.


By the way, I just realized that the 4096 line limit could well be just a
side-effect of that 16-bit clipping and the size of the plain-text font.

... testing ...

I just changed the font to 120 twips (6 points) and used a plain-text file
with lines showing the numbers 0 ... 4100. The EM_GETSCROLLPOS returned the
value 0xDF58 and no jumping occured.

IOW, its not a "number of lines" related, but, as you suggested earlier,
just the height of the document. Strange though that it keeps jumping, even
when the location of the view becomes lower than 65536.

And again doubling the number of lines caused the result become larger, not
smaller (0xDF58 -> FF6F).

Another fine mess I/we find myself/ourselves in ! :-( :-)

Regards,
Rudy Wieser
Newyana2
2023-06-16 16:29:00 UTC
Permalink
"R.Wieser" <***@is.invalid> wrote

| > What a mess!
|
| It certainly is. Even just by the absense of a clear warning.
|

Not all that bad. It leaves me room for about 60 -70
window heights worth of text. On the one hand, there
seems to be no excuse for limiting 32-bit values to 16-bit.
On the other hand, who needs 4000 lines of text with
formatting? The real tragedy is that you're not finding
a way to get paid for your uncanny ability to find bugs. :)
R.Wieser
2023-06-16 17:01:22 UTC
Permalink
Newyana2,
Post by Newyana2
Not all that bad. It leaves me room for about 60 -70
window heights worth of text. On the one hand, there
seems to be no excuse for limiting 32-bit values to
16-bit.
I could imagine something like that for earlier versions of the OS (win 3.x,
perhaps W95 and W98 too), but not for later ones. /Especially/ not for
versions Vista and up.
Post by Newyana2
On the other hand, who needs 4000 lines of text with
formatting?
I dunno, anyone who reads documents or stories ? :-p Like I've got RFC's
that are have a 4000+ linecount (RFC 883 (domain names) to name one).
Post by Newyana2
The real tragedy is that you're not finding a way to
get paid for your uncanny ability to find bugs. :)
I don't think I'm good at finding bugs. I just come across them while I'm
trying to create something. It just might be my urge to know what is going
on that I do not just forget about the bug when I find a work-around.

As for getting payed for them ? The last time I tried to tell a company
that their product had a problem and asked how they fixed it they claimed
that it wasn't in their database, so nothing they could do for me. IOW, I
would gladly take a fixed product as payment, but I'm not even getting that.

Regards,
Rudy Wieser
R.Wieser
2023-06-16 17:11:00 UTC
Permalink
I just come across them while I'm trying to create something.
One thing that could be of influence : I'm writing Assembly (rather low
level) and as such need to write /all/ the glue myself. So if there is a
problem it can't be hidden from me like a compiler could do (either by
applying a fix in the high(er) level function call, or simply by not giving
access to the malfunctioning DLL function).

Regards,
Rudy Wieser
R.Wieser
2023-06-17 06:01:25 UTC
Permalink
Post by R.Wieser
And again doubling the number of lines caused the result become larger,
not smaller (0xDF58 -> FF6F).
This morning I realized the cause for that : the value is for the *top* of
the view, not the total height of the displayed content. When I doubled the
files contents the view does not go twice the previous distance, but twice
/plus the height of the view itself/.

IOW, the answer was staring me in the eye. Quite Literally.

Regards,
Rudy Wieser

Newyana2
2023-06-15 12:06:14 UTC
Permalink
"R.Wieser" <***@is.invalid> wrote

|
| Now all I need to do is figure out how to fix it - if at all possible
(have
| seen it mentioned as a being bug).
|
It may very well be a bug. I've come across other bugs.
Especially when things are not well documented. Sometimes
I think MS assigns people to break things that are not officially
documented. For example, when I added UTF-8 support for
richedit I came across listings for the flags needed, but when
I actually did it, it was all mixed up. Some EM messages work
with it. Some don't. Special handling was needed for ANSI vs
unicode strings. It was all trial and error.

A similar thing once happened in Win98. People were using
an icon choosing system window and MS changed the functionality
in ME. It wasn't official, so no one could complain. Then, if I
remember correctly, they broke it altogether in XP. The dialogue
window was still there. MS just reworked it in arbitrary ways so
that it wouldn't work unless you had the secret initiation. Then there's
also the infamous Bags keys in the Registry to save window
coordinates for Explorer windows. MS broke the whole system
with every Windows version and as far as I know they never
had it working properly. The Registry keys have to be edited
to make it work. Yet MS went to great lengths to make that difficult.
It's like a supermarket where they shuffle the products around
every night, just to be a wiseguy.

The scrollpos issue never showed up for me because I don't
use it to save a position. I only use it for line numbers. I have
a second RE window along the edge of the main RE, which I use
to provide line numbers. When the main RE moves, the line
numbers RE must match it. That functionality works fine. When
I do operations with lockwindowupdate I just return it to the
original caret position before unlocking. With paste I need
to calculate the caret position to put it at the end of the paste.
There are little details like that. But I don't think I've ever needed
anything but caret position saved before an operation and reset
at the end.

With multiple files I save the top line position and then set
it to that position when switching between loaded files. For
that I use EM_GETFIRSTVISIBLELINE and EM_LINESCROLL. I
guess I probably also save the caret position. It's not all fresh in
my mind, as I haven't worked with this for some time.
R.Wieser
2023-06-16 08:12:44 UTC
Permalink
Newyana2,
Post by Newyana2
It may very well be a bug. I've come across other bugs.
It very well might be. But it also might be an oversight when creating the
documentation (which is also a bug, but of a different kind :- )

As I just wrote to Apd, I just found out that the problem starts to occur
when RichEdit shows 4096 or more /displayed/ lines (a word wrap counts as
another line).

Regards,
Rudy Wieser
Paul N
2023-06-14 14:29:01 UTC
Permalink
Post by Newyana2
GETSCROLLPOS is not the same as cursor position.
Cursor position is EM_EXGETSEL and EM_EX_SETSEL.
Yes, that was my mistake in naming the problem. Its about saving/restoring
the views position, not the cursor.
You're still not using quite the right terminology, if you say the problem right it might be easier to solve :-)

The "cursor" is the mouse pointer which moves around when you move the mouse. The "carat" is the flashing line which indicates where new text will go if you type any, and which moves when you press the arrow keys.

If you try to set the scroll position to a place where the carat won't actually be visible in the window, my understanding is that it will scroll to a different place instead where the carat will be visible. So you want to set the carat position in the text before you try adjusting the scroll to show the part of the text you desire. This may be the source of your difficulties.

Hope this is in some way helpful!
Paul.
R.Wieser
2023-06-14 14:54:33 UTC
Permalink
Paul,
Post by Paul N
You're still not using quite the right terminology, if you say the problem
right it
might be easier to solve :-)
That might have been true when I posted my first message about it (mea
culpa), but not not this far in the thread. If by now you have no idea
what I'm talking about you never will. :-)
Post by Paul N
If you try to set the scroll position to a place where the carat won't
actually be visible
in the window, my understanding is that it will scroll to a different
place instead where
the carat will be visible.
If you have ever used the a scrollwheel in an edit control (or just the
vertical, or even horizontal slider) you know that is false.

Though when you type something or even use the cursor keys to move the caret
around the view will snap to a location which includes the caret.
Post by Paul N
So you want to set the carat position in the text before you try adjusting
the scroll to
show the part of the text you desire.
In the parent to your message I wrote that each press of the button (and
executing the EM_GETSCROLLPOS, EM_SETSCROLLPOS combination) the "view" would
jump further "up"wards, towards the start of the text. That would not be
possible if what you say in the above is true, 'cause the "view" would than
stay leashed to the caret, keeping it in view..
Post by Paul N
This may be the source of your difficulties.
So no, it isn't (alas).

Regards,
Rudy Wieser
Loading...