You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
koffice/lib/kformula/textelement.cpp

568 lines
18 KiB

/* This file is part of the KDE project
Copyright (C) 2001 Andrea Rizzi <rizzi@kde.org>
Ulrich Kuettler <ulrich.kuettler@mailbox.tu-dresden.de>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <tqfontmetrics.h>
#include <tqpainter.h>
#include <kdebug.h>
#include "basicelement.h"
#include "contextstyle.h"
#include "elementtype.h"
#include "elementvisitor.h"
#include "fontstyle.h"
#include "formulaelement.h"
#include "kformulacommand.h"
#include "sequenceelement.h"
#include "symboltable.h"
#include "textelement.h"
KFORMULA_NAMESPACE_BEGIN
TextElement::TextElement(TQChar ch, bool beSymbol, BasicElement* parent)
: BasicElement(parent), character(ch), symbol(beSymbol)
{
charStyle( anyChar );
charFamily( anyFamily );
}
TextElement::TextElement( const TextElement& other )
: BasicElement( other ),
character( other.character ),
symbol( other.symbol ),
m_format( other.m_format )
{
}
bool TextElement::accept( ElementVisitor* visitor )
{
return visitor->visit( this );
}
TokenType TextElement::getTokenType() const
{
if ( isSymbol() ) {
return getSymbolTable().charClass( character );
}
switch ( character.unicode() ) {
case '+':
case '-':
case '*':
//case '/': because it counts as text -- no extra spaces
return BINOP;
case '=':
case '<':
case '>':
return RELATION;
case ',':
case ';':
case ':':
return PUNCTUATION;
case '\\':
return SEPARATOR;
case '\0':
return ELEMENT;
default:
if ( character.isNumber() ) {
return NUMBER;
}
else {
return ORDINARY;
}
}
}
bool TextElement::isInvisible() const
{
if (getElementType() != 0) {
return getElementType()->isInvisible(*this);
}
return false;
}
/**
* Calculates our width and height and
* our children's parentPosition.
*/
void TextElement::calcSizes( const ContextStyle& context,
ContextStyle::TextStyle tstyle,
ContextStyle::IndexStyle /*istyle*/,
StyleAttributes& style )
{
double factor = style.sizeFactor();
luPt mySize = context.getAdjustedSize( tstyle, factor );
setCharStyle( style.charStyle() );
setCharFamily( style.charFamily() );
TQFont font = getFont( context, style );
double fontsize = context.layoutUnitPtToPt( mySize );
double scriptsize = pow( style.scriptSizeMultiplier(), style.scriptLevel() );
double size = fontsize * scriptsize;
font.setPointSizeFloat( size < style.scriptMinSize() ? style.scriptMinSize() : size );
TQFontMetrics fm( font );
if ( character == applyFunctionChar || character == invisibleTimes || character == invisibleComma ) {
setWidth( 0 );
setHeight( 0 );
setBaseline( getHeight() );
}
else {
TQChar ch = getRealCharacter(context);
if ( ch == TQChar::null ) {
setWidth( tqRound( context.getEmptyRectWidth( factor ) * 2./3. ) );
setHeight( tqRound( context.getEmptyRectHeight( factor ) * 2./3. ) );
setBaseline( getHeight() );
}
else {
TQRect bound = fm.boundingRect( ch );
setWidth( context.ptToLayoutUnitPt( fm.width( ch ) ) );
setHeight( context.ptToLayoutUnitPt( bound.height() ) );
setBaseline( context.ptToLayoutUnitPt( -bound.top() ) );
// There are some glyphs in TeX that have
// baseline==0. (\int, \sum, \prod)
if ( getBaseline() == 0 ) {
//setBaseline( getHeight()/2 + context.axisHeight( tstyle ) );
setBaseline( -1 );
}
}
}
}
/**
* Draws the whole element including its children.
* The `parentOrigin' is the point this element's parent starts.
* We can use our parentPosition to get our own origin then.
*/
void TextElement::draw( TQPainter& painter, const LuPixelRect& /*r*/,
const ContextStyle& context,
ContextStyle::TextStyle tstyle,
ContextStyle::IndexStyle /*istyle*/,
StyleAttributes& style,
const LuPixelPoint& parentOrigin )
{
if ( character == applyFunctionChar || character == invisibleTimes || character == invisibleComma ) {
return;
}
LuPixelPoint myPos( parentOrigin.x()+getX(), parentOrigin.y()+getY() );
//if ( !LuPixelRect( myPos.x(), myPos.y(), getWidth(), getHeight() ).intersects( r ) )
// return;
// Let container set the color, instead of elementType
//setUpPainter( context, painter );
painter.setPen( style.color() );
setCharStyle( style.charStyle() );
setCharFamily( style.charFamily() );
double factor = style.sizeFactor();
luPt mySize = context.getAdjustedSize( tstyle, factor );
TQFont font = getFont( context, style );
double fontsize = context.layoutUnitPtToPt( mySize );
double scriptsize = pow( style.scriptSizeMultiplier(), style.scriptLevel() );
double size = fontsize * scriptsize;
font.setPointSizeFloat( size < style.scriptMinSize() ? style.scriptMinSize() : size );
painter.setFont( font );
//kdDebug( DEBUGID ) << "TextElement::draw font=" << font.rawName() << endl;
//kdDebug( DEBUGID ) << "TextElement::draw size=" << mySize << endl;
//kdDebug( DEBUGID ) << "TextElement::draw size=" << context.layoutUnitToFontSize( mySize, false ) << endl;
//kdDebug( DEBUGID ) << "TextElement::draw height: " << getHeight() << endl;
//kdDebug( DEBUGID ) << "TextElement::draw width: " << getWidth() << endl;
//kdDebug( DEBUGID ) << endl;
// Each starting element draws the whole token
/*
ElementType* token = getElementType();
if ( ( token != 0 ) && !symbol ) {
TQString text = token->text( static_cast<SequenceElement*>( getParent() ) );
// kdDebug() << "draw text: " << text[0].unicode()
// << " " << painter.font().family().latin1()
// << endl;
painter.fillRect( context.layoutUnitToPixelX( parentOrigin.x() ),
context.layoutUnitToPixelY( parentOrigin.y() ),
context.layoutUnitToPixelX( getParent()->getWidth() ),
context.layoutUnitToPixelY( getParent()->getHeight() ),
style.background() );
painter.drawText( context.layoutUnitToPixelX( myPos.x() ),
context.layoutUnitToPixelY( myPos.y()+getBaseline() ),
text );
}
else {
*/
TQChar ch = getRealCharacter(context);
if ( ch != TQChar::null ) {
luPixel bl = getBaseline();
if ( bl == -1 ) {
// That's quite hacky and actually not the way it's
// meant to be. You shouldn't calculate a lot in
// draw. But I don't see how else to deal with
// baseline==0 glyphs without yet another flag.
bl = -( getHeight()/2 + context.axisHeight( tstyle, factor ) );
}
painter.drawText( context.layoutUnitToPixelX( myPos.x() ),
context.layoutUnitToPixelY( myPos.y()+bl ),
TQString(ch) );
}
else {
painter.setPen( TQPen( context.getErrorColor(),
context.layoutUnitToPixelX( context.getLineWidth( factor ) ) ) );
painter.drawRect( context.layoutUnitToPixelX( myPos.x() ),
context.layoutUnitToPixelY( myPos.y() ),
context.layoutUnitToPixelX( getWidth() ),
context.layoutUnitToPixelY( getHeight() ) );
}
// }
// Debug
//painter.setBrush(TQt::NoBrush);
// if ( isSymbol() ) {
// painter.setPen( TQt::red );
// painter.drawRect( context.layoutUnitToPixelX( myPos.x() ),
// context.layoutUnitToPixelX( myPos.y() ),
// context.layoutUnitToPixelX( getWidth() ),
// context.layoutUnitToPixelX( getHeight() ) );
// painter.setPen(TQt::green);
// painter.drawLine(myPos.x(), myPos.y()+axis( context, tstyle ),
// myPos.x()+getWidth(), myPos.y()+axis( context, tstyle ));
// }
}
void TextElement::dispatchFontCommand( FontCommand* cmd )
{
cmd->addTextElement( this );
}
void TextElement::setCharStyle( CharStyle cs )
{
charStyle( cs );
formula()->changed();
}
void TextElement::setCharFamily( CharFamily cf )
{
charFamily( cf );
formula()->changed();
}
TQChar TextElement::getRealCharacter(const ContextStyle& context)
{
return character;
/*
if ( !isSymbol() ) {
const FontStyle& fontStyle = context.fontStyle();
const AlphaTable* alphaTable = fontStyle.alphaTable();
if ( alphaTable != 0 ) {
AlphaTableEntry ate = alphaTable->entry( character,
charFamily(),
charStyle() );
if ( ate.valid() ) {
return ate.pos;
}
}
return character;
}
else {
return getSymbolTable().character(character, charStyle());
}
*/
}
TQFont TextElement::getFont(const ContextStyle& context, const StyleAttributes& style)
{
const FontStyle& fontStyle = context.fontStyle();
TQFont font;
if ( style.customFont() ) {
font = style.font();
}
else if (getElementType() != 0) {
font = getElementType()->getFont(context);
}
else {
font = context.getDefaultFont();
}
switch ( charStyle() ) {
case anyChar:
font.setItalic( false );
font.setBold( false );
break;
case normalChar:
font.setItalic( false );
font.setBold( false );
break;
case boldChar:
font.setItalic( false );
font.setBold( true );
break;
case italicChar:
font.setItalic( true );
font.setBold( false );
break;
case boldItalicChar:
font.setItalic( true );
font.setBold( true );
break;
}
return fontStyle.symbolTable()->font( character, font );
}
void TextElement::setUpPainter(const ContextStyle& context, TQPainter& painter)
{
if (getElementType() != 0) {
getElementType()->setUpPainter(context, painter);
}
else {
painter.setPen(TQt::red);
}
}
const SymbolTable& TextElement::getSymbolTable() const
{
return formula()->getSymbolTable();
}
void TextElement::writeMathML( TQDomDocument& doc, TQDomNode& parent, bool ) const
{
parent.appendChild( doc.createTextNode( getCharacter() ) );
}
/**
* Appends our attributes to the dom element.
*/
void TextElement::writeDom(TQDomElement element)
{
BasicElement::writeDom(element);
element.setAttribute("CHAR", TQString(character));
//TQString s;
//element.setAttribute("CHAR", s.sprintf( "#x%05X", character ) );
if (symbol) element.setAttribute("SYMBOL", "3");
switch ( charStyle() ) {
case anyChar: break;
case normalChar: element.setAttribute("STYLE", "normal"); break;
case boldChar: element.setAttribute("STYLE", "bold"); break;
case italicChar: element.setAttribute("STYLE", "italic"); break;
case boldItalicChar: element.setAttribute("STYLE", "bolditalic"); break;
}
switch ( charFamily() ) {
case normalFamily: element.setAttribute("FAMILY", "normal"); break;
case scriptFamily: element.setAttribute("FAMILY", "script"); break;
case frakturFamily: element.setAttribute("FAMILY", "fraktur"); break;
case doubleStruckFamily: element.setAttribute("FAMILY", "doublestruck"); break;
case anyFamily: break;
}
}
/**
* Reads our attributes from the element.
* Returns false if it failed.
*/
bool TextElement::readAttributesFromDom(TQDomElement element)
{
if (!BasicElement::readAttributesFromDom(element)) {
return false;
}
TQString charStr = element.attribute("CHAR");
if(!charStr.isNull()) {
character = charStr.at(0);
}
TQString symbolStr = element.attribute("SYMBOL");
if(!symbolStr.isNull()) {
int symbolInt = symbolStr.toInt();
if ( symbolInt == 1 ) {
character = getSymbolTable().unicodeFromSymbolFont(character);
}
if ( symbolInt == 2 ) {
switch ( character.unicode() ) {
case 0x03D5: character = 0x03C6; break;
case 0x03C6: character = 0x03D5; break;
case 0x03Ba: character = 0x03BA; break;
case 0x00B4: character = 0x2032; break;
case 0x2215: character = 0x2244; break;
case 0x00B7: character = 0x2022; break;
case 0x1D574: character = 0x2111; break;
case 0x1D579: character = 0x211C; break;
case 0x2219: character = 0x22C5; break;
case 0x2662: character = 0x26C4; break;
case 0x220B: character = 0x220D; break;
case 0x224C: character = 0x2245; break;
case 0x03DB: character = 0x03C2; break;
}
}
symbol = symbolInt != 0;
}
TQString styleStr = element.attribute("STYLE");
if ( styleStr == "normal" ) {
charStyle( normalChar );
}
else if ( styleStr == "bold" ) {
charStyle( boldChar );
}
else if ( styleStr == "italic" ) {
charStyle( italicChar );
}
else if ( styleStr == "bolditalic" ) {
charStyle( boldItalicChar );
}
else {
charStyle( anyChar );
}
TQString familyStr = element.attribute( "FAMILY" );
if ( familyStr == "normal" ) {
charFamily( normalFamily );
}
else if ( familyStr == "script" ) {
charFamily( scriptFamily );
}
else if ( familyStr == "fraktur" ) {
charFamily( frakturFamily );
}
else if ( familyStr == "doublestruck" ) {
charFamily( doubleStruckFamily );
}
else {
charFamily( anyFamily );
}
// kdDebug() << "charStyle=" << charStyle()
// << " charFamily=" << charFamily()
// << " format=" << int( format() ) << endl;
return true;
}
/**
* Reads our content from the node. Sets the node to the next node
* that needs to be read.
* Returns false if it failed.
*/
bool TextElement::readContentFromDom(TQDomNode& node)
{
return BasicElement::readContentFromDom(node);
}
TQString TextElement::toLatex()
{
if ( isSymbol() ) {
TQString texName = getSymbolTable().name( character );
if ( !texName.isNull() )
return " \\" + texName + " ";
return " ? ";
}
else {
return TQString(character);
}
}
TQString TextElement::formulaString()
{
if ( isSymbol() ) {
TQString texName = getSymbolTable().name( character );
if ( !texName.isNull() )
return " " + texName + " ";
return " ? ";
}
else {
return character;
}
}
EmptyElement::EmptyElement( BasicElement* parent )
: BasicElement( parent )
{
}
EmptyElement::EmptyElement( const EmptyElement& other )
: BasicElement( other )
{
}
bool EmptyElement::accept( ElementVisitor* visitor )
{
return visitor->visit( this );
}
void EmptyElement::calcSizes( const ContextStyle& context,
ContextStyle::TextStyle tstyle,
ContextStyle::IndexStyle /*istyle*/,
StyleAttributes& style )
{
luPt mySize = context.getAdjustedSize( tstyle, style.sizeFactor() );
//kdDebug( DEBUGID ) << "TextElement::calcSizes size=" << mySize << endl;
TQFont font = context.getDefaultFont();
font.setPointSizeFloat( context.layoutUnitPtToPt( mySize ) );
TQFontMetrics fm( font );
TQChar ch = 'A';
TQRect bound = fm.boundingRect( ch );
setWidth( 0 );
setHeight( context.ptToLayoutUnitPt( bound.height() ) );
setBaseline( context.ptToLayoutUnitPt( -bound.top() ) );
}
void EmptyElement::draw( TQPainter& painter, const LuPixelRect& /*r*/,
const ContextStyle& context,
ContextStyle::TextStyle /*tstyle*/,
ContextStyle::IndexStyle /*istyle*/,
StyleAttributes& /*style*/ ,
const LuPixelPoint& parentOrigin )
{
LuPixelPoint myPos( parentOrigin.x()+getX(), parentOrigin.y()+getY() );
/*
if ( !LuPixelRect( myPos.x(), myPos.y(), getWidth(), getHeight() ).intersects( r ) )
return;
*/
if ( context.edit() ) {
painter.setPen( context.getHelpColor() );
painter.drawLine( context.layoutUnitToPixelX( myPos.x() ),
context.layoutUnitToPixelY( myPos.y() ),
context.layoutUnitToPixelX( myPos.x() ),
context.layoutUnitToPixelY( myPos.y()+getHeight() ) );
}
}
TQString EmptyElement::toLatex()
{
return "{}";
}
KFORMULA_NAMESPACE_END