This patch changes the semantics of next() and previous() on
VisiblePosition to move a grapheme (character cluster) at a
time. This means that cursor navigation with correctly move
over an entire cluster.
However, the expected behavior for deleting a grapheme is to
delete individual code points, thus decomposing the grapheme
into it constituent parts. That will be addressed in the next
part of the fix.
Reviewed by Ken.
* khtml/editing/visible_position.cpp:
(khtml::VisiblePosition::previousPosition):
(khtml::VisiblePosition::nextPosition):
* khtml/editing/visible_position.h:
* khtml/rendering/render_object.cpp:
(RenderObject::previousOffset):
(RenderObject::nextOffset):
* khtml/rendering/render_object.h:
* khtml/rendering/render_text.cpp:
(RenderText::previousOffset):
(RenderText::nextOffset):
(RenderText::findNextInlineTextBox):
* khtml/rendering/render_text.h:
* khtml/xml/dom_nodeimpl.cpp:
(NodeImpl::previousOffset):
(NodeImpl::nextOffset):
* khtml/xml/dom_nodeimpl.h:
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@8450
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2005-01-25 Richard Williamson <rjw@apple.com>
+
+ Part one of fix for <rdar://problem/3757712> REGRESSION (Mail): WebCore does not allow Devanagari ligature input
+
+ This patch changes the semantics of next() and previous() on
+ VisiblePosition to move a grapheme (character cluster) at a
+ time. This means that cursor navigation with correctly move
+ over an entire cluster.
+
+ However, the expected behavior for deleting a grapheme is to
+ delete individual code points, thus decomposing the grapheme
+ into it constituent parts. That will be addressed in the next
+ part of the fix.
+
+ Reviewed by Ken.
+
+ * khtml/editing/visible_position.cpp:
+ (khtml::VisiblePosition::previousPosition):
+ (khtml::VisiblePosition::nextPosition):
+ * khtml/editing/visible_position.h:
+ * khtml/rendering/render_object.cpp:
+ (RenderObject::previousOffset):
+ (RenderObject::nextOffset):
+ * khtml/rendering/render_object.h:
+ * khtml/rendering/render_text.cpp:
+ (RenderText::previousOffset):
+ (RenderText::nextOffset):
+ (RenderText::findNextInlineTextBox):
+ * khtml/rendering/render_text.h:
+ * khtml/xml/dom_nodeimpl.cpp:
+ (NodeImpl::previousOffset):
+ (NodeImpl::nextOffset):
+ * khtml/xml/dom_nodeimpl.h:
+
2005-01-25 David Harrison <harrison@apple.com>
Reviewed by Maciej.
result = Position(prevNode, prevNode->maxOffset());
}
else {
- result = Position(pos.node(), pos.offset() - 1);
+ NodeImpl *node = pos.node();
+ result = Position(node, node->previousOffset(pos.offset()));
}
return result;
result = Position(nextNode, 0);
}
else {
- result = Position(pos.node(), pos.offset() + 1);
+ NodeImpl *node = pos.node();
+ result = Position(node, node->nextOffset(pos.offset()));
}
return result;
friend bool operator==(const VisiblePosition &a, const VisiblePosition &b);
+ // next() and previous() will increment/decrement by a character cluster.
VisiblePosition next() const;
VisiblePosition previous() const;
return 0;
}
+long RenderObject::previousOffset (long current) const
+{
+ long previousOffset = current - 1;
+ return previousOffset;
+}
+
+long RenderObject::nextOffset (long current) const
+{
+ long nextOffset = current + 1;
+ return nextOffset;
+}
+
InlineBox *RenderObject::inlineBox(long offset, EAffinity affinity)
{
return inlineBoxWrapper();
virtual long caretMaxOffset() const;
virtual unsigned long caretMaxRenderedOffset() const;
+ virtual long previousOffset (long current) const;
+ virtual long nextOffset (long current) const;
+
virtual void setPixmap(const QPixmap&, const QRect&, CachedImage *);
virtual void selectionStartEnd(int& spos, int& epos);
#include <kdebug.h>
#include <assert.h>
+// You may have to turn this to 0 to compile without the headers for ICU installed.
+#define HAVE_ICU_LIBRARY 1
+
+#if HAVE_ICU_LIBRARY
+#include <unicode/ubrk.h>
+#include <unicode/uloc.h>
+#include <unicode/utypes.h>
+#include <unicode/parseerr.h>
+#endif
+
using namespace khtml;
using namespace DOM;
return m_start + m_len;
}
+long RenderText::previousOffset (long current) const
+{
+#if !HAVE_ICU_LIBRARY
+ long previousOffset = current - 1;
+ return previousOffset;
+#else
+ UErrorCode status = U_ZERO_ERROR;
+
+ // The locale is currently ignored when determining character cluster breaks. This may change
+ // in the future (according to Deborah Goldsmith).
+ UBreakIterator* iterator = ubrk_open (UBRK_CHARACTER, "en_us", (const UChar*)str->s, str->l, &status);
+ if (iterator) {
+ // ICU gives us the leading and trailing edges of ligature graphemes, so we determine if
+ // the size of the previous grapheme is larger than 1 (i.e. we have a ligature or some sort),
+ // and if so, move back to the leading edge of the the grapheme, which is the same
+ // as the start of the previous character.
+ long off1 = ubrk_preceding (iterator, current);
+ long off2 = ubrk_preceding (iterator, off1);
+ ubrk_close (iterator);
+
+ if (off2 >= 0 && off1 - off2 > 1)
+ return off2;
+
+ return off1;
+ }
+
+ return current - 1;
+#endif
+}
+
+long RenderText::nextOffset (long current) const
+{
+#if !HAVE_ICU_LIBRARY
+ long nextOffset = current + 1;
+ return nextOffset;
+#else
+ UErrorCode status = U_ZERO_ERROR;
+
+ // The locale is currently ignored when determining character cluster breaks. This may change
+ // in the future (according to Deborah Goldsmith).
+ UBreakIterator* iterator = ubrk_open (UBRK_CHARACTER, "en_us", (const UChar*)str->s, str->l, &status);
+ if (iterator) {
+ // ICU gives us the leading and trailing edges of ligature graphemes, so we determine if
+ // the size of the next grapheme is larger than 1 (i.e. we have a ligature or some sort),
+ // and if so, move forward to the trailing edge of the the grapheme, which is the same
+ // as the start of the next character.
+ long off1 = ubrk_following (iterator, current);
+ long off2 = ubrk_following (iterator, off1);
+ ubrk_close (iterator);
+
+ if (off2 >= 0 && off1 - current > 1)
+ return off2;
+
+ return off1;
+ }
+
+ return current + 1;
+#endif
+}
+
+
#define LOCAL_WIDTH_BUF_SIZE 1024
int InlineTextBox::offsetForPosition(int _x, bool includePartialGlyphs)
box->height()));
}
-InlineTextBox* RenderText::findNextInlineTextBox(int offset, int &pos)
+InlineTextBox* RenderText::findNextInlineTextBox(int offset, int &pos) const
{
// The text runs point to parts of the rendertext's str string
// (they don't include '\n')
virtual long caretMinOffset() const;
virtual long caretMaxOffset() const;
virtual unsigned long caretMaxRenderedOffset() const;
+
+ virtual long previousOffset (long current) const;
+ virtual long nextOffset (long current) const;
#if APPLE_CHANGES
public:
#endif
- InlineTextBox * findNextInlineTextBox( int offset, int &pos );
+ InlineTextBox * findNextInlineTextBox( int offset, int &pos ) const;
protected: // members
DOM::DOMStringImpl *str;
return renderer() ? renderer()->caretMaxRenderedOffset() : 1;
}
+long NodeImpl::previousOffset (long current) const
+{
+ return renderer() ? renderer()->previousOffset(current) : current - 1;
+}
+
+long NodeImpl::nextOffset (long current) const
+{
+ return renderer() ? renderer()->nextOffset(current) : current + 1;
+}
+
bool NodeImpl::isBlockFlow() const
{
return renderer() && renderer()->isBlockFlow();
virtual long caretMaxOffset() const;
virtual unsigned long caretMaxRenderedOffset() const;
+ virtual long previousOffset (long current) const;
+ virtual long nextOffset (long current) const;
+
#ifndef NDEBUG
virtual void dump(QTextStream *stream, QString ind = "") const;
#endif