> 3. ScintillaWin::DragEnter() now immediately reads the dragged text and caches it (see 1). This code is moved here from ScintillaWin::Drop().
The client’s drag checking shouldn’t be creating replacement text since this will be thrown away. It should just be saying OK/Not until the drop which may then be transformed.
A deletion-veto API seems safer.
Client code that requires more complex transformations should queue up a complex deletion command packet with all the information from the check and then process that in idle time, transforming the packet as needed for any other modifications that occur before it is acted on.
> One way to resolve both of these issues would be to abandon SC_MOD_DELETECHECK/SCI_CHANGEDELETION and instead propose a new API that simply allows vetoing of any change - e.g. SC_MOD_CHANGECHECK/SCI_VETOCHANGE. I don't think this is as tidy though.
It has a much more limited scope which I see as less likely to cause problems.
I've started implementing SCN_CHECKMODIFY and SCI_MODIFYVETO but as hinted by something you wrote earlier, I'm running into problems with compound actions. My supposition is that if you have a rectangular selection (for example) you want the entire clear/cut/paste operation to fail if any of the selections has a veto on it. So we need checks not only in the gateway functions but also in some of the Editor functions that call these. There are two problems here: first that you can end up with multiple SCN_CHECKMODIFY notifications for the same basic operation, and secondly it starts to look very much like the implementation of protected regions.
You've written previously of your dissatisfaction with protected regions. Do you think it would be better if I concentrated on improving the implementation and functionality of protected regions, rather than implementing this new veto API? One essential modification that I would need would be the ability to select and copy text in a protected region. I'd also like to make the InsertString and DeleteChars gateway functions check if the text is protected as belt and braces in case checks are missed elsewhere. Can you see any obvious pitfalls? Which approach would you prefer?
Cheers,
Ian
I'm submitting a new patch for vetoing of document modifications. I know that the release of 3.5.0 is imminent and I was going to wait until after that but a couple of people here have expressed interest, so consider this a preview. This patch is against version 3.4.4.
There are three new messages and one new notification:
SCI_VETOMODIFICATION
This may be called only from a SCN_MODIFYCHECK notification handler and will veto the action, preventing the document from being modified.
SCI_SETVETOMODIFICATIONFLAGS(int flags)
Lots of methods in Editor have had a ModifyVetoChecker added. PasteRectangular() needed extensive re-working to incorporate ModifyVetoChecker in a sane way. The external behaviour should be unaffected if there are no vetoes.
(I commented out Editor::DelChar() because it is dead code.)
Scope for improvement:
The fact that the checking and making the modifications are independent is a potential source of bugs and complexity in the code. It would be better to replace the current internal interface for low-level document modifications with a transaction-based approach, which would allow arbitrarily complex modifications to the document to be carried out atomically. But this would be a major piece of work and is not something I can contemplate taking on at the moment (though I do have an idea of how it might be implemented).
Not sure if a ModifyVetoChecker is needed for implementing APIs that will always be called by the container and are not the result of Scintilla handling user actions. For example AddStyledText isn’t called except to implement SCI_ADDSTYLEDTEXT. ReplaceTarget and SCI_SETTEXT and others are similar.
Also unsure about ModifyVetoChecker being an object instead of a set of calls as it (currently) only yields a boolean result.
Its good to simplify major patches by breaking off pieces that can be applied independently. This looks like it can be done before the rest of the patch. Editor::DelChar looks like it became dead code with change set 2957 in 2009 which implemented discontiguous selection and virtual space.(I commented out Editor::DelChar() because it is dead code.)
Some sort of container of insertions/deletions may be a useful tool but it would be more work. It is complicated by actions changing the selection, particularly for multiple and rectangular selection.
Part of my reason for the reply on this patch was to make sure you were aware of the line-associated state and the need to maintain this as far as possible since the post contained “Applying this patch will have no effect on the operation of Scintilla” when it would be more accurate to say that it has no effect on the document content.
The line ‘*ptr++’ does nothing with the dereferenced value. The “lines.rbegin()->size()’ expression can fail if the data is empty (len == 0).
64-bit builds produce several size_t to int conversion warnings.
Backwards loops to 0 with an unsigned counter are easy to get wrong. If not now, then when changed by maintenance. Placing the mutation in the test clause adds to the fragility.
The results are not always exactly the same as before with proportional fonts although the new results are mostly better.
The anchor fields in the ranges are never used so I expect it would be clearer to use a vector of SelectionPosition.
I’m not seeing how the ‘veto the complete action’ concept works with pastes: the deletions occur in ScintillaWin::Paste which can be vetoed but if they succeed and then the insertion is vetoed, the deletions aren’t reversed.
At a more general level, the veto mechanism seems to have an implicit model where allowing a deletion at a position implies allowing insertion at that position.
However, this isn’t really reflected in the API so applications may try to implement features counter to this model (delete-only or insert-only regions for example) but which produce different results depending on which action the user performs.
At a more general level, the veto mechanism seems to have an implicit model where allowing a deletion at a position implies allowing insertion at that position.
Correct. That was the intention.
In particular, the callback must not try to second-guess whether the modification is an insertion or deletion by looking at the length parameter, because Scintilla assumes that if a particular range can be deleted then an insertion can also be made anywhere within that range.
Does this also imply that inserting immediately before a protected range is not permitted?
Can we avoid this by careful documentation? I know developers will ultimately do what they want to do, but we can at least warn that the callback must provide consistent results during a particular operation (much as a search comparator must do).
Allowing separate rules for deletions and insertions opens a whole new can of worms and isn't a route I'd like to go down. I'd be open to changing to the API to make it impossible to distinguish deletions from insertions in the callback, but I can't see how this could be achieved.
I’m not seeing how the ‘veto the complete action’ concept works with pastes: the deletions occur in ScintillaWin::Paste which can be vetoed but if they succeed and then the insertion is vetoed, the deletions aren’t reversed.
You are right – there is a bug if you paste a rectangular selection over a shorter rectangular selection and the bottom of the pasted rectangle would be vetoed. In this case as you say the original selection is deleted, but the paste gets vetoed. I'm afraid I can't see a way to resolve this that isn't insanely complicated.