Previously it found the start or end by traversing the linked lists of
run, rows, paragraphs, and cells from the current position of the
cursors. Clearly it is better to get the start or end directly to make
it a constant time operation.
Wrapping is needed to be done even when repainting isn't done since
later messages expect line breaks to reflect the current text. Some
message can specify not to paint the sceen, but this should prevent
wrapping from being done.
- A HWND can be safely marshaled over a LONG as its payload is not
a pointer but a user handle.
- Use GetWindowLongPtr instead of GetWindowLong to retrieve a pointer.
When finding an adjacent paragraph, the next_para and prev_para pointers
should be used because they are direct pointers, a constant time
operation. Instead I found some places in the code that searched through
the general linked list to get to an adjacent paragraph, which is a linear
time operation, depending on the number of rows and runs in between
paragraphs.
The fixme comment is suggesting wrapping a paragraph within a function
that is for moving the selection cursor up or down a line when the up
or down keys are pressed. The contents fo paragraph aren't being
changed, so there is no need to wrap the paragraph.
More case of searching for the paragraph through the linked list when
is was already previously available. Since each wrap context is used
for wrapping each paragraph, I decided to add the reference to the
paragarph in the structure.
Rather than get the paragraph from the run, the function allows the
caller to provide the paragraph, since it is already available. This
reduces unnecessary traversals of lists that take longer as more runs
and rows are in the paragraph.
The ME_RunOfsFromCharOfs function finds the paragraph before finding the
run and offset within the run, so the function may as well be able to
return this paragraph to the caller. Many callers to the function
instead find the paragraph from the run, which ends up unnecessarily
traversing a linked list of runs within the paragraph.
Whenever ME_InitContext is called, ME_DestroyContext should be used to
clean it up. This way the context can be extended easily by modifying
those two functions. Instead, these two places of code just released
the DC, without using ME_DestroyContext, so the created brush for the
margin was not deleted.
These calls to ME_WrapMarkedParagraphs never do anything, and don't make
sense to be called in these places. These places are for ME_MoveCaret,
and ME_ArrowHome, which both don't involve any text being modified, and
all (direct and indirect) calls to these functions are done after the
text has already been wrapped.
There was a bug in ME_FindText which would cause the final caracter
offset to be incorrect when a paragraph was crossed while matching
characters. The problem was the character offset of the wrong
paragraph was used in the calculation of the start offset of the
match.
The text mode is already stored, and EM_SETTEXTMODE already exists.
There was however a bug in EM_MakeEditor that could cause TM_PLAINTEXT
and TM_RICHEDIT to be set at the same time. This was corrected to ensure
EM_GETTEXTMODE returned the proper mode being used.
The width for EM_SETTARGETDEVICE is used by some applications to set the
wrapping width to a certain distance in twips. This can be used even
though the target device is ignored.
The internal style flags are used to determine whether to show or hide
the scrollbar when ME_UpdateScrollBar is called. EM_SHOWSCROLLBAR seems
to update this state in native richedit controls.
If the scrollbar style isn't initially used, then the scrollbar should
be shown. Otherwise this can be a problem when the horizontal scrollbar
is shown for a single line richedit control, since it will cover all the
text (See bug 12088).
Previously a count of the carraige returns and line feeds were stored
for end of paragraph runs, and a paragraph sign was stored as the actual
string. This was causing many special cases where the length of the
run needed to be determined differently if the run was or wasn't an
end of paragraph run.
There wasn't any use for storing the paragraph sign unless some drawing
code gets commented out to allow the end paragraphs to be shown,
therefore I changed the code to store the actual string that gets
retrieved by WM_GETTEXT.
The two functions ME_FindItemAtOffset and ME_RunOfsFromCharOfs were almost
identically used, since ME_FindItemAtOffset was always used to find a run.
The only difference was how they returned the offset within the run for an
end of paragraph run.
For ME_FindItemAtOffset it would return the next run if it was in between \r
and \n. ME_RunOfsFromCharOfs would instead return an nOffset of 0 for end
paragraph runs. This subtle difference introduced bugs, so I decided to
avoid having special case in this function when creating this patch, and
instead let the caller handle this case.
EM_GETTEXTRANGE allows the start character offset and end characters
offset to be used to specify the range of text to retrieve. If the
start offset is in the middle of an end of paragraph run (i.e. \r\n),
then it should only retrieve the characters after the specified
character offset.
I found that ME_FindItemAtOffset and ME_CursorFromCharOfs are used
almost identically, except for how they handle a character offset that
is between a carriage return and line feed. In this case
ME_CursorFromCharOfs sets the cursor's run offset to 0, but
ME_FindItemAtOffset instead returns the next run which is what was
causing ME_LINELENGTH to incorrectly return the length of the next
line.
riched32.dll does preserve the carriage returns and line feeds unlike
later versions of the richedit control, however the tests previously
missed the fact that a sequence of carriage returns followed by a line
feed (e.g. \r\r\r\n) can actually cause multiple paragraph breaks.
I noticed a while ago that on Windows XP richedit controls ignored
characters typed while the mouse is captured (e.g. from holding the left
or middle button down). Arrow keys, delete, and backspace, copying,
cutting, pasting, and everything else handled on WM_CHAR and WM_KEYDOWN
messages are also ignored.
Certain operations will simply not be done for windowless richedit
controls, such as WM_PAINT which isn't done for windowless richedit
controls since ITextServices provides a TxDraw method.
The methods in ITextHost are mostly thin wrappers around functions that
take a handle to a window as their first parameter. This patch just
uses the wrapper functions provided by ITextHost instead of using the
functions that require a handle to a window that the editor might now
have (for windowless richedit controls).
EM_GETPARAFORMAT previously would overwrite the cbSize field with the
size of PARAFORMAT2, would read past the end of the struct, and might
indicate that PARAFORMAT2 fields are valid using the mask regardless
of the value of cbSize.
If a EM_SETPARAFORMAT message is sent to the richedit control with bits
in the dwMask field that correspond to PARAFORMAT2 fields, then these
fields should be ignored. Instead data was copied from outside of the
structure.
Using the WS_VSCROLL style causes the ES_AUTOVSCROLL option to be set,
and using the WS_HSCROLL style causes the ES_AUTOHSCROLL flag to be
set (except with richedit v1.0).
The contents of the text can be zoomed in with EM_SETZOOM, or with the
mouse wheel. EM_SETZOOM is implemented, but these tests show bugs in
the implementation, and zooming using the mouse wheel isn't
implemented at all yet.
Windowless richedit control will not be able to call GetCapture without
a handle to the host window (and there is no ITextHost_TxGetCapture
method), but there is a ITextHost_TxSetCapture method available for
setting and releasing the capture on the mouse. This means that the
richedit control will need to keep track of whether it has captured the
mouse or not to implement windowless richedit controls.
Previously the WM_NCCREATE was handled by the as if it was always for
later versions, then the window proc for version 1.0 would make
appropriate changes afterwards. Instead both versions should call the
same function (e.g. ME_MakeEditor) and provide the value for
bEmulateVersion10 to make the code clearer.
This fixes inconsistencies shown in the tests I added for the
WM_GETDLGCODE. The tests covered different cases handled by the
current implementation in order to show that the native implementation
is simpler for all these cases.
When the character or paragraph format is changed the paragraph that
is changed is already marked to be rewrapped, so ME_MarkAllForWrapping
shouldn't be called. Since ME_RewrapRepaint uses this function, it
shouldn't be called in these circumstances, since rewrapping all the
text can cause noticable delays when working with a lot of text.
A common case for richedit controls are that a large amount of text is
set initially with word wrap enabled. This causes the initially
wrapping of the text, which also calculates the text length. After
this the vertical scrollbar will be shown, which causes the text to be
rewrapped again. After this there are two redundant rewraps that are
done which this patch eliminates.
On WM_DESTROY the editor was getting freed, then it was used to obtain
a handle to the editor. This patch moves it just before the editor is
freed within ME_DestroyEditor.
In order to make the message handling available to windowless richedit
controls, the message handling must be in a function that can be
called from the ITextServices_TxSendMessage method. This method will
never have a handle to a window to pass to RichEditWndProc_common in
order to get the editor with GetWindowLongPtrW, but passing the editor
will work (even if hWnd is NULL).
When the text is wrapped, the positions for all the runs, paragraphs,
and cells are already calculated and stored. The only thing left to do
for painting is to offset them by the formatting rectangle and the
scroll position.
During wrapping there were three different heights that were being
stored, with only one of them being done correctly. The other ones
failed to incorporate the height of the paragraph or row, so ended up
being incorrect.
The formatting rectangle is set with EM_SETRECT, and retrieved with
EM_GETRECT, so it corresponds to rcFormat in the code. This defines the
area that the richedit control should draw the text so that it is
offset by the top-left corner of the formatting rectangle, and clipped
so that it doesn't draw past the bottom or right hand side. Thus this
is important for implementing windowless richedit controls to not
interfere with the rest of the window.
There were several methods that do not have a HRESULT for a return
value, so returning E_NOTIMPL is not appropriate. For all the BOOL
return values FALSE was returned to indicate the operation was not
performed.
The RichEditWndProc_common function is big enough already by handling
all the window messages, so moving code to handle a message to its own
function makes the code more readable.
There is no reason for the rich text format parser to need a handle to
the window, and even if there were it has a handle to the editor which
contains a handle to the window. It is better to remove this
considering we need to cut down on the use of window handles to
implement windowless richedit controls.
The vertical scrollbar state is stored internally within the control,
so should be used when possible. This will become more necessary when
windowless richedit controls are implemented, and there will no hWnd
to pass to GetScrollInfo.
Comparing the editor as apposed to the handle to the window will work
just as well right now, but will also work when there is no window
handle to make a comparison with, which will be the case with
windowless richedit controls.
The code for the ME_EnsureVisible function does exactly what
EM_SCROLLCARET does, yet this code is duplicated in order to handle
this message. It is simpler to just use the existing function to
implement the message, and avoid internally sending the EM_SCROLLCARET
when this function is available.
EditWordBreakProc documented the third parameter as being the number
of unicode characters in the string for richedit 2.0 and up. It turns
out that it should actually be the number of bytes in the string.
For some reason EM_POSFROMCHAR was returning 0 when the position was
equal to the end of the text, or beyond the end of the text. Instead
it should use the position at the end of the text for both these
cases. The x value was also seen to be offset by 1 according to the
tests.
Previously the shortcuts for cut, copy, paste, undo, redo, and select
all were being handled during the WM_CHAR message. These tests show
that these shortcuts should be handled with the WM_KEYDOWN message
instead.
There were some notifications that weren't sent in ME_UpdateRepaint
while redraw was disabled, so this verifies that they are not sent
with redraw disabled.
The assertion was not valid, because it neglected to take into account
the situation where a line break is forced with a MERF_ENDROW run
(caused by \line control word or pressing Shift-Enter). This means
that spaces can cause a line wrap after a forced line break as well as
after a paragraph break, so we cannot assert that it is the first row
in the paragraph.
The test for EM_GETLINE was testing to make sure the null terminating
character was written at the end of the text as long as the buffer was
long enough, and also tested to make sure that no other bytes were
written after this null terminating character. This is consistent with
Windows 2000 and up, but not for previous versions of Windows.
The table properties are streamed out at the start of the table for
non-nested tables, and at the end of the table for nested tables. The
assertion caught the fact that I didn't get the start of the table row
for nested tables before trying to stream out the properties.
The call to ME_GetTableRowStart will handle both of these cases by
getting the table row start paragraph and asserting that it is found.
This call was also the reason for removing the const qualifier on one
of the parameters.
Previously the paragraph and cell border properties were lost when
deleting the text, then undoing the deletion. This would cause tables
to lose the colour and width of the table border.
Previously the control words in skipped groups were being processed by
the read hook on the RTF parser. By moving this code into the class
callbacks for the parser, the skipped groups actually remain skipped.
For simple tables cells are represented with tabs, and a table row is
ended at the end of the paragraph, so native richedit controls
substitute spaces for \tab and \par rich text format control words.
The values returned by EM_SETPARAFORMAT and EM_GETPARAFORMAT previously
indicated an error, and the included tests shows that Windows behaves as
documented.
After selection a word, line, or paragraph with multi click selection or using
the selection bar, then shift can be held and the arrows can be used to move
one of the ends of the selection.
The problem was that the paragraph format was being retrieved,
slightly modified and then used to set the paragraph format, without
limiting the mask to what was being set. The PFM_OFFSETINDENT mask flag
being valid meant that dxStartIndent specifies a relative offset, thus
dxStartIndent was doubled.
This is a minimal model of what happens in Corman Lisp 3.0 -
subclassed window class that unconditionally calls ShowScrollBar() to
force scrollbar visibility.
Some applications have never heard of ES_DISABLENOSCROLL and attempt
to force scrollbars to be always shown (with ShowScrollBar() or
similar) when otherwise richedit would hide them. If richedit attempts
to wrestle control back, a recursive loop of requests can result if
app overrides WM_SIZE behavior. Apparently native never reads the
scrollbar state, and operates from some sort of internal state, so
that scrollbars can be modified externally without native trying to
wrestle back control. This is confirmed by attached tests. An
exception: EM_SCROLL will restore visibility to a scrollbar that was
forcibly hidden.
EM_AUTOURLDETECT tests are taking too much time, so this patch tests
just one URL and one non-URL for all messages but WM_SETTEXT. Also,
remove one trace that spams the output needlessly.
This also reverts commit 2b52dd845097f16076c0185b02a003f63898dcab:
wordpad: Empty the richedit undo buffer on creation.
The reverted commit I created to fix an issue that only applied to Wine,
but it just masked the issue which was in richedit controls. The
default character format was set in two places while wordpad was
starting up, and caused wordpad to have two undo items at startup.
Trying to set the font size to a value larger than 1638
in points (yHeightCharPtsMost) using EM_SETCHARFORMAT will cause it to be
set to actually set to the maximum.
In version 1.0 of the richedit controls highlighting is done by
inverting the colours. Version 2.0 and up highlight instead draw
the text using system colours for the background and the text.
The uncommon case that this patch handles is enough whitespace being
on the first line of a paragraph to cause it to wrap. In this case the
first non-space character will be wrapped onto the next line.
When the cursor is moved over the selection bar, without holding any
mouse buttons down, the cursor would be repeatably set between the
normal cursor, set by DefWindowProc for the WM_SETCURSOR message, and
the reversed cursor, set by ME_MouseMove.
I created a function to set the default paragraph format to ensure
consistency when this is done. This initial paragraph format is also
now more consistent with native richedit controls. The dwMask value
always appears to have the same value when retrieved from the native
richedit controls, so all the mask values are now initialized when the
PARAFORMAT2 structure is created.
The PARAFORMAT structure has a bit in wEffects to indicate whether the
paragraph is a table or not, so this should be used instead of a private
bTable value, since this structure can be retrieved with EM_GETPARAFORMAT.
The cursor should only be shown when there is no selection, since this
is how it is done in Windows. This patch avoids showing the cursor when
there is a selection, and destroys the cursor when a selection is made.
The error was a memory access of a freed object. In ME_AddUndoItem I
checked the top of the undo stack to end a coalescing undo transaction,
assuming that this should be either a valid undo item, or NULL, instead
it was already freed.
The caret's x position is stored in ME_TextEditor.nUDArrowX so that when
the caret is moved up or down, it will stay along the same horizontal
position, or at the end of a line. Unfortunately, the value stored in
nUDArrowX was being unconditionally discarded in ME_ArrowKey, preventing
it from serving its purpose.
Before the end of the text was selected when clicking below the end of
the text, rather than using the x position to find the appropriate
character on the last row that is closest to the pixel position.
When the caret is at the start of a run, it uses the font of the
previous run for inserting characters. The caret size previously was
the wrong height for the characters being inserted when the caret was at
the start of a line, but not the start of a paragraph so this patch
fixes this bug.
The check to see if the dwOutputBuffer is full was performed incorrectly
in RTFPutUnicodeString, however, this mistake was actually harmless
since it would just cause an extra loop that does nothing, then the
check would work properly with the variable fit equal to 0.
Checks were added for hexadecimal values that did not have valid
characters, and for EOF received before the final closing brace of the
rich text stream. The error values were tested on richedit versions 1,
2, 3 & 4.1, and they were all the same for these cases.
The function ME_GetInsertStyle already checks for the case where there
is a selection, so there is no need to duplicate this code for
ME_GetSelectionInsertStyle.