Reviewed by Darin.
<rdar://problem/
3758033> REGRESSION (Mail): Support attributes in marked text (International input)
* khtml/rendering/render_text.cpp:
(InlineTextBox::paint): Support painting custom underline markers for
marked text in place of generic yellow.
(InlineTextBox::paintMarkedTextUnderline): New method that handles this.
* khtml/rendering/render_text.h:
* kwq/KWQKHTMLPart.h: Declare new methods and structs.
* kwq/KWQKHTMLPart.mm:
(KWQKHTMLPart::clear): Clear marked test underlines.
(KWQKHTMLPart::setMarkedTextRange): Takes attributes and ranges now.
(convertAttributesToUnderlines): Converts NSAttributedString attributes
to simplified and C++-friendly form.
(KWQKHTMLPart::markedTextUsesUnderlines): New method.
(KWQKHTMLPart::markedTextUnderlines): New method.
* kwq/KWQPainter.mm:
(QPainter::drawLineForText): Handle pen width.
* kwq/WebCoreBridge.h:
* kwq/WebCoreBridge.mm:
(-[WebCoreBridge setMarkedTextDOMRange:customAttributes:ranges:]): Take attributes
and ranges.
* kwq/WebCoreTextRenderer.h:
WebKit:
Reviewed by Darin.
<rdar://problem/
3758033> REGRESSION (Mail): Support attributes in marked text (International input)
* WebCoreSupport.subproj/WebTextRenderer.m:
(-[WebTextRenderer drawLineForCharacters:yOffset:width:color:thickness:]): Changed to support
underline thickness. Also added a bit of a hack here to move thickness 2 underlines down by
.5 pixels, since the rendering engine can't give a fractional pixel offset.
* WebView.subproj/WebHTMLView.m:
(-[WebHTMLView validAttributesForMarkedText]): Support underline, underline color and marked
clause attributes. Others that NSText supports are unimplemented for now.
(-[WebHTMLView firstRectForCharacterRange:]): Remove needless logging.
(-[WebHTMLView unmarkText]): Updated for new WebCore SPI.
(-[WebHTMLView _extractAttributes:ranges:fromAttributedString:]): New method to pull the attributes
and ranges out of an attributed string.
(-[WebHTMLView setMarkedText:selectedRange:]): Extract attributes and pass to WebCore.
(-[WebHTMLView insertText:]): Add comment noting that we don't really handle attributed strings
here.
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@8360
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2005-01-10 Maciej Stachowiak <mjs@apple.com>
+
+ Reviewed by Darin.
+
+ <rdar://problem/3758033> REGRESSION (Mail): Support attributes in marked text (International input)
+
+ * khtml/rendering/render_text.cpp:
+ (InlineTextBox::paint): Support painting custom underline markers for
+ marked text in place of generic yellow.
+ (InlineTextBox::paintMarkedTextUnderline): New method that handles this.
+ * khtml/rendering/render_text.h:
+ * kwq/KWQKHTMLPart.h: Declare new methods and structs.
+ * kwq/KWQKHTMLPart.mm:
+ (KWQKHTMLPart::clear): Clear marked test underlines.
+ (KWQKHTMLPart::setMarkedTextRange): Takes attributes and ranges now.
+ (convertAttributesToUnderlines): Converts NSAttributedString attributes
+ to simplified and C++-friendly form.
+ (KWQKHTMLPart::markedTextUsesUnderlines): New method.
+ (KWQKHTMLPart::markedTextUnderlines): New method.
+ * kwq/KWQPainter.mm:
+ (QPainter::drawLineForText): Handle pen width.
+ * kwq/WebCoreBridge.h:
+ * kwq/WebCoreBridge.mm:
+ (-[WebCoreBridge setMarkedTextDOMRange:customAttributes:ranges:]): Take attributes
+ and ranges.
+ * kwq/WebCoreTextRenderer.h:
+
2005-01-12 David Harrison <harrison@apple.com>
Reviewed by Dave Hyatt.
// Determine whether or not we have marked text.
Range markedTextRange = KWQ(object()->document()->part())->markedTextRange();
bool haveMarkedText = markedTextRange.handle() != 0 && markedTextRange.startContainer() == object()->node();
+ bool markedTextUsesUnderlines = KWQ(object()->document()->part())->markedTextUsesUnderlines();
+
// Set our font.
RenderStyle* styleToUse = object()->style(m_firstLine);
// 1. Paint backgrounds behind text if needed. Examples of such backgrounds include selection
// and marked text.
- if ((haveSelection || haveMarkedText) && i.phase != PaintActionSelection && !isPrinting) {
+ if ((haveSelection || haveMarkedText) && !markedTextUsesUnderlines && i.phase != PaintActionSelection && !isPrinting) {
if (haveMarkedText)
paintMarkedTextBackground(i.p, tx, ty, styleToUse, font, markedTextRange.startOffset(), markedTextRange.endOffset());
QValueList<DocumentMarker> markers = object()->document()->markersForNode(object()->node());
QValueListIterator <DocumentMarker> markerIt = markers.begin();
+ QValueList<KWQKHTMLPart::MarkedTextUnderline> underlines;
+ if (haveMarkedText && markedTextUsesUnderlines) {
+ underlines = KWQ(object()->document()->part())->markedTextUnderlines();
+ }
+ QValueListIterator<KWQKHTMLPart::MarkedTextUnderline> underlineIt = underlines.begin();
+
QColor textColor = styleToUse->color();
if (styleToUse->shouldCorrectTextColor())
textColor = correctedTextColor(textColor, styleToUse->backgroundColor());
// marker is completely after this run, bail. A later run will paint it.
break;
}
+
+
+ for ( ; underlineIt != underlines.end(); underlineIt++) {
+ KWQKHTMLPart::MarkedTextUnderline underline = *underlineIt;
+
+ if (underline.endOffset <= start())
+ // underline is completely before this run. This might be an underlinethat sits
+ // before the first run we draw, or underlines that were within runs we skipped
+ // due to truncation.
+ continue;
+
+ if (underline.startOffset <= end()) {
+ // underline intersects this run. Paint it.
+ paintMarkedTextUnderline(i.p, tx, ty, underline);
+ if (underline.endOffset > end() + 1)
+ // underline also runs into the next run. Bail now, no more marker advancement.
+ break;
+ } else
+ // underline is completely after this run, bail. A later run will paint it.
+ break;
+ }
+
+
+
}
if (setShadow)
pt->drawLineForMisspelling(_tx + start, _ty + underlineOffset, width);
}
+void InlineTextBox::paintMarkedTextUnderline(QPainter *pt, int _tx, int _ty, KWQKHTMLPart::MarkedTextUnderline underline)
+{
+ _tx += m_x;
+ _ty += m_y;
+
+ if (m_truncation == cFullTruncation)
+ return;
+
+ int start = 0; // start of line to draw, relative to _tx
+ int width = m_width; // how much line to draw
+ bool useWholeWidth = true;
+ ulong paintStart = m_start;
+ ulong paintEnd = end()+1; // end points at the last char, not past it
+ if (paintStart <= underline.startOffset) {
+ paintStart = underline.startOffset;
+ useWholeWidth = false;
+ start = static_cast<RenderText*>(m_object)->width(m_start, paintStart - m_start, m_firstLine);
+ }
+ if (paintEnd != underline.endOffset) { // end points at the last char, not past it
+ paintEnd = kMin(paintEnd, (ulong)underline.endOffset);
+ useWholeWidth = false;
+ }
+ if (m_truncation != cNoTruncation) {
+ paintEnd = kMin(paintEnd, (ulong)m_truncation);
+ useWholeWidth = false;
+ }
+ if (!useWholeWidth) {
+ width = static_cast<RenderText*>(m_object)->width(paintStart, paintEnd - paintStart, m_firstLine);
+ }
+
+ int underlineOffset = m_height - 3;
+ pt->setPen(QPen(underline.color, underline.thick ? 2 : 0));
+ pt->drawLineForText(_tx + start, _ty, underlineOffset, width);
+}
+
long InlineTextBox::caretMinOffset() const
{
return m_start;
#include <qptrvector.h>
#include <assert.h>
+#include "KWQKHTMLPart.h"
class QPainter;
class QFontMetrics;
void paintSelection(QPainter* p, int tx, int ty, RenderStyle* style, const Font* font);
void paintMarkedTextBackground(QPainter* p, int tx, int ty, RenderStyle* style, const Font* font, int startPos, int endPos);
void paintMarker(QPainter* p, int _tx, int _ty, DOM::DocumentMarker marker);
-
+ void paintMarkedTextUnderline(QPainter *pt, int _tx, int _ty, KWQKHTMLPart::MarkedTextUnderline underline);
virtual long caretMinOffset() const;
virtual long caretMaxOffset() const;
virtual unsigned long caretMaxRenderedOffset() const;
// Implementation of CSS property -khtml-user-drag == auto
bool shouldDragAutoNode(DOM::NodeImpl*, int x, int y) const;
+ struct MarkedTextUnderline {
+ MarkedTextUnderline(unsigned _startOffset, unsigned _endOffset, const QColor &_color, bool _thick)
+ : startOffset(_startOffset)
+ , endOffset(_endOffset)
+ , color(_color)
+ , thick(_thick)
+ {}
+ unsigned startOffset;
+ unsigned endOffset;
+ QColor color;
+ bool thick;
+ };
+
+ void setMarkedTextRange(const DOM::Range &, NSArray *attributes, NSArray *ranges);
DOM::Range markedTextRange() const;
- void setMarkedTextRange(const DOM::Range &);
+ bool markedTextUsesUnderlines() const;
+ QValueList<MarkedTextUnderline> markedTextUnderlines() const;
bool canGoBackOrForward(int distance) const;
mutable DOM::Node _elementToDraw;
DOM::Range m_markedTextRange;
+ bool m_markedTextUsesUnderlines;
+ QValueList<MarkedTextUnderline> m_markedTextUnderlines;
};
inline KWQKHTMLPart *KWQ(KHTMLPart *part) { return static_cast<KWQKHTMLPart *>(part); }
void KWQKHTMLPart::clear()
{
urlsBridgeKnowsAbout.clear();
- setMarkedTextRange(0);
+ setMarkedTextRange(0, nil, nil);
KHTMLPart::clear();
}
return m_markedTextRange;
}
-void KWQKHTMLPart::setMarkedTextRange(const DOM::Range &range)
+static QValueList<KWQKHTMLPart::MarkedTextUnderline> convertAttributesToUnderlines(const DOM::Range &markedTextRange, NSArray *attributes, NSArray *ranges)
+{
+ QValueList<KWQKHTMLPart::MarkedTextUnderline> result;
+
+ int baseOffset = markedTextRange.startOffset();
+
+ unsigned length = [attributes count];
+ ASSERT([ranges count] == length);
+
+ for (unsigned i = 0; i < length; i++) {
+ NSNumber *style = [[attributes objectAtIndex:i] objectForKey:NSUnderlineStyleAttributeName];
+ if (!style)
+ continue;
+ NSRange range = [[ranges objectAtIndex:i] rangeValue];
+ NSColor *color = [[attributes objectAtIndex:i] objectForKey:NSUnderlineColorAttributeName];
+ QColor qColor = Qt::black;
+ if (color) {
+ NSColor* deviceColor = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace];
+ qColor = QColor(qRgba((int)(255 * [deviceColor redComponent]),
+ (int)(255 * [deviceColor blueComponent]),
+ (int)(255 * [deviceColor greenComponent]),
+ (int)(255 * [deviceColor alphaComponent])));
+ }
+
+ result.append(KWQKHTMLPart::MarkedTextUnderline(range.location + baseOffset,
+ range.location + baseOffset + range.length,
+ qColor,
+ [style intValue] > 1));
+ }
+
+ return result;
+}
+
+void KWQKHTMLPart::setMarkedTextRange(const DOM::Range &range, NSArray *attributes, NSArray *ranges)
{
ASSERT(!range.handle() || range.startContainer() == range.endContainer());
ASSERT(!range.handle() || range.collapsed() || range.startContainer().nodeType() == Node::TEXT_NODE);
+ if (attributes == nil) {
+ m_markedTextUsesUnderlines = false;
+ m_markedTextUnderlines.clear();
+ } else {
+ m_markedTextUsesUnderlines = true;
+ m_markedTextUnderlines = convertAttributesToUnderlines(range, attributes, ranges);
+ }
+
if (m_markedTextRange.handle() && xmlDocImpl()
&& m_markedTextRange.startContainer().handle()->renderer()) {
m_markedTextRange.startContainer().handle()->renderer()->repaint();
}
}
+bool KWQKHTMLPart::markedTextUsesUnderlines() const
+{
+ return m_markedTextUsesUnderlines;
+}
+
+QValueList<KWQKHTMLPart::MarkedTextUnderline> KWQKHTMLPart::markedTextUnderlines() const
+{
+ return m_markedTextUnderlines;
+}
+
bool KWQKHTMLPart::canGoBackOrForward(int distance) const
{
return [_bridge canGoBackOrForward:distance];
[data->textRenderer
drawLineForCharacters: NSMakePoint(x, y)
yOffset:(float)yOffset
- withWidth: width
- withColor:data->state.pen.color().getNSColor()];
+ width: width
+ color:data->state.pen.color().getNSColor()
+ thickness:data->state.pen.width()];
}
void QPainter::drawLineForMisspelling(int x, int y, int width)
- (void)setMarkDOMRange:(DOMRange *)range;
- (DOMRange *)markDOMRange;
-// spelling-checking "marked text"
-- (void)setMarkedTextDOMRange:(DOMRange *)range;
+// international text input "marked text"
+- (void)setMarkedTextDOMRange:(DOMRange *)range customAttributes:(NSArray *)attributes ranges:(NSArray *)ranges;
- (DOMRange *)markedTextDOMRange;
- (NSAttributedString *)attributedStringFrom:(DOMNode *)startNode startOffset:(int)startOffset to:(DOMNode *)endNode endOffset:(int)endOffset;
return [DOMRange _rangeWithImpl:_part->mark().toRange().handle()];
}
-- (void)setMarkedTextDOMRange:(DOMRange *)range
+- (void)setMarkedTextDOMRange:(DOMRange *)range customAttributes:(NSArray *)attributes ranges:(NSArray *)ranges
{
- _part->setMarkedTextRange([range _rangeImpl]);
+ _part->setMarkedTextRange([range _rangeImpl], attributes, ranges);
}
- (DOMRange *)markedTextDOMRange
// drawing
- (void)drawRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style geometry:(const WebCoreTextGeometry *)geometry;
- (void)drawHighlightForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style geometry:(const WebCoreTextGeometry *)geometry;
-- (void)drawLineForCharacters:(NSPoint)point yOffset:(float)yOffset withWidth:(int)width withColor:(NSColor *)color;
+- (void)drawLineForCharacters:(NSPoint)point yOffset:(float)yOffset width: (int)width color:(NSColor *)color thickness:(float)thickness;
- (void)drawLineForMisspelling:(NSPoint)point withWidth:(int)width;
// selection point check
+2005-01-10 Maciej Stachowiak <mjs@apple.com>
+
+ Reviewed by Darin.
+
+ <rdar://problem/3758033> REGRESSION (Mail): Support attributes in marked text (International input)
+
+ * WebCoreSupport.subproj/WebTextRenderer.m:
+ (-[WebTextRenderer drawLineForCharacters:yOffset:width:color:thickness:]): Changed to support
+ underline thickness. Also added a bit of a hack here to move thickness 2 underlines down by
+ .5 pixels, since the rendering engine can't give a fractional pixel offset.
+ * WebView.subproj/WebHTMLView.m:
+ (-[WebHTMLView validAttributesForMarkedText]): Support underline, underline color and marked
+ clause attributes. Others that NSText supports are unimplemented for now.
+ (-[WebHTMLView firstRectForCharacterRange:]): Remove needless logging.
+ (-[WebHTMLView unmarkText]): Updated for new WebCore SPI.
+ (-[WebHTMLView _extractAttributes:ranges:fromAttributedString:]): New method to pull the attributes
+ and ranges out of an attributed string.
+ (-[WebHTMLView setMarkedText:selectedRange:]): Extract attributes and pass to WebCore.
+ (-[WebHTMLView insertText:]): Add comment noting that we don't really handle attributed strings
+ here.
+
2005-01-12 Darin Adler <darin@apple.com>
Reviewed by Ken.
return [self _floatWidthForRun:run style:style widths:widthBuffer fonts:nil glyphs:nil startPosition:nil numGlyphs:nil];
}
-- (void)drawLineForCharacters:(NSPoint)point yOffset:(float)yOffset withWidth: (int)width withColor:(NSColor *)color
+- (void)drawLineForCharacters:(NSPoint)point yOffset:(float)yOffset width: (int)width color:(NSColor *)color thickness:(float)thickness
{
- // XXX MJS
-
NSGraphicsContext *graphicsContext = [NSGraphicsContext currentContext];
CGContextRef cgContext;
BOOL flag = [graphicsContext shouldAntialias];
+ [graphicsContext setShouldAntialias: NO];
+
// We don't want antialiased lines on screen, but we do when printing (else they are too thick)
if ([graphicsContext isDrawingToScreen]) {
[graphicsContext setShouldAntialias:NO];
[color set];
cgContext = (CGContextRef)[graphicsContext graphicsPort];
- CGSize size = CGSizeApplyAffineTransform(CGSizeMake(1.0, 1.0), CGAffineTransformInvert(CGContextGetCTM(cgContext)));
- CGContextSetLineWidth(cgContext, size.width);
+
+ // hack to make thickness 2 underlines for internation text input look right
+ if (thickness > 1.5 && thickness < 2.5) {
+ yOffset += .5;
+ }
+
+ if (thickness == 0.0) {
+ CGSize size = CGSizeApplyAffineTransform(CGSizeMake(1.0, 1.0), CGAffineTransformInvert(CGContextGetCTM(cgContext)));
+ CGContextSetLineWidth(cgContext, size.width);
+ } else {
+ CGContextSetLineWidth(cgContext, thickness);
+ }
+
#if BUILDING_ON_PANTHER
CGContextMoveToPoint(cgContext, point.x, point.y + [self lineSpacing] + 1.5 - [self descent] + yOffset);
// <rdar://problem/3630640>: "Calling interpretKeyEvents: in a custom text view can fail to process keys right after app startup"
#import <AppKit/NSKeyBindingManager.h>
+// need to declare this because AppKit does not make it available as API or SPI
+extern NSString *NSMarkedClauseSegmentAttributeName;
+
// Kill ring calls. Would be better to use NSKillRing.h, but that's not available in SPI.
void _NSInitializeKillRing(void);
void _NSAppendToKillRing(NSString *);
@implementation WebHTMLView (WebNSTextInputSupport)
+static NSArray *validAttributes = nil;
+
- (NSArray *)validAttributesForMarkedText
{
- // FIXME: TEXTINPUT: validAttributesForMarkedText not yet implemented
- return [NSArray array];
+ if (!validAttributes) {
+ validAttributes = [[NSArray allocWithZone:[self zone]] initWithObjects:NSUnderlineStyleAttributeName, NSUnderlineColorAttributeName, NSMarkedClauseSegmentAttributeName, nil];
+ // NSText also supports the following attributes, but it's
+ // hard to tell which are really required for text input to
+ // work well; I have not seen any input method make use of them yet.
+ //
+ // NSFontAttributeName, NSForegroundColorAttributeName,
+ // NSBackgroundColorAttributeName, NSLanguageAttributeName,
+ // NSTextInputReplacementRangeAttributeName
+ }
+
+ return validAttributes;
}
- (unsigned int)characterIndexForPoint:(NSPoint)thePoint
- (NSRect)firstRectForCharacterRange:(NSRange)theRange
{
- NSLog(@"location: %d length: %d\n", theRange.location, theRange.length);
-
if (![self hasMarkedText]) {
return NSMakeRect(0,0,0,0);
}
NSRect resultRect = [self convertRect:[bridge firstRectForDOMRange:rectRange] toView:nil];
resultRect.origin = [[self window] convertBaseToScreen:resultRect.origin];
- NSLog(@"(%d,%d) %dx%d\n", (int)resultRect.origin.x, (int)resultRect.origin.y, (int)resultRect.size.width, (int)resultRect.size.height);
-
return resultRect;
}
- (void)unmarkText
{
- [[self _bridge] setMarkedTextDOMRange:nil];
+ [[self _bridge] setMarkedTextDOMRange:nil customAttributes:nil ranges:nil];
}
- (void)_selectMarkedText
[bridge setSelectedDOMRange:selectedRange affinity:NSSelectionAffinityUpstream];
}
+- (void)_extractAttributes:(NSArray **)a ranges:(NSArray **)r fromAttributedString:(NSAttributedString *)string
+{
+ int length = [[string string] length];
+ int i = 0;
+ NSMutableArray *attributes = [NSMutableArray array];
+ NSMutableArray *ranges = [NSMutableArray array];
+ while (i < length) {
+ NSRange effectiveRange;
+ NSDictionary *attrs = [string attributesAtIndex:i longestEffectiveRange:&effectiveRange inRange:NSMakeRange(i,length - i)];
+ [attributes addObject:attrs];
+ [ranges addObject:[NSValue valueWithRange:effectiveRange]];
+ i = effectiveRange.location + effectiveRange.length;
+ }
+ *a = attributes;
+ *r = ranges;
+}
+
- (void)setMarkedText:(id)string selectedRange:(NSRange)newSelRange
{
WebBridge *bridge = [self _bridge];
[self _selectMarkedText];
NSString *text;
+ NSArray *attributes = nil;
+ NSArray *ranges = nil;
if ([string isKindOfClass:[NSAttributedString class]]) {
- ERROR("TEXTINPUT: requested set marked text with attributed string");
text = [string string];
+ [self _extractAttributes:&attributes ranges:&ranges fromAttributedString:string];
} else {
text = string;
}
[bridge replaceSelectionWithText:text selectReplacement:YES smartReplace:NO];
- [bridge setMarkedTextDOMRange:[self _selectedRange]];
+ [bridge setMarkedTextDOMRange:[self _selectedRange] customAttributes:attributes ranges:ranges];
if ([self hasMarkedText]) {
[self _selectRangeInMarkedText:newSelRange];
}
{
NSString *text;
if ([string isKindOfClass:[NSAttributedString class]]) {
- ERROR("TEXTINPUT: requested insert of attributed string");
text = [string string];
- int length = [text length];
- int i = 0;
- while (i < length) {
- NSRange effectiveRange;
- NSDictionary *attrs = [string attributesAtIndex:i longestEffectiveRange:&effectiveRange inRange:NSMakeRange(i,length - i)];
- i = effectiveRange.location + effectiveRange.length;
- NSLog(@"attribute chunk: %@ from %d length %d\n", attrs, effectiveRange.location, effectiveRange.length);
- }
+ // we don't yet support inserting an attributed string but input methods
+ // don't appear to require this.
} else {
text = string;
}
+
[self _insertText:text selectInsertedText:NO];
}