lib

textelement.cc

00001 /* This file is part of the KDE project
00002    Copyright (C) 2001 Andrea Rizzi <rizzi@kde.org>
00003                   Ulrich Kuettler <ulrich.kuettler@mailbox.tu-dresden.de>
00004 
00005    This library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Library General Public
00007    License as published by the Free Software Foundation; either
00008    version 2 of the License, or (at your option) any later version.
00009 
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this library; see the file COPYING.LIB.  If not, write to
00017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018  * Boston, MA 02110-1301, USA.
00019 */
00020 
00021 #include <qfontmetrics.h>
00022 #include <qpainter.h>
00023 
00024 #include <kdebug.h>
00025 
00026 #include "basicelement.h"
00027 #include "contextstyle.h"
00028 #include "elementtype.h"
00029 #include "elementvisitor.h"
00030 #include "fontstyle.h"
00031 #include "formulaelement.h"
00032 #include "kformulacommand.h"
00033 #include "sequenceelement.h"
00034 #include "symboltable.h"
00035 #include "textelement.h"
00036 
00037 
00038 KFORMULA_NAMESPACE_BEGIN
00039 
00040 TextElement::TextElement(QChar ch, bool beSymbol, BasicElement* parent)
00041         : BasicElement(parent), character(ch), symbol(beSymbol)
00042 {
00043     charStyle( anyChar );
00044     charFamily( anyFamily );
00045 }
00046 
00047 
00048 TextElement::TextElement( const TextElement& other )
00049     : BasicElement( other ),
00050       character( other.character ),
00051       symbol( other.symbol ),
00052       m_format( other.m_format )
00053 {
00054 }
00055 
00056 
00057 bool TextElement::accept( ElementVisitor* visitor )
00058 {
00059     return visitor->visit( this );
00060 }
00061 
00062 
00063 TokenType TextElement::getTokenType() const
00064 {
00065     if ( isSymbol() ) {
00066         return getSymbolTable().charClass( character );
00067     }
00068 
00069     switch ( character.unicode() ) {
00070     case '+':
00071     case '-':
00072     case '*':
00073         //case '/':  because it counts as text -- no extra spaces
00074         return BINOP;
00075     case '=':
00076     case '<':
00077     case '>':
00078         return RELATION;
00079     case ',':
00080     case ';':
00081     case ':':
00082         return PUNCTUATION;
00083     case '\\':
00084         return SEPARATOR;
00085     case '\0':
00086         return ELEMENT;
00087     default:
00088         if ( character.isNumber() ) {
00089             return NUMBER;
00090         }
00091         else {
00092             return ORDINARY;
00093         }
00094     }
00095 }
00096 
00097 
00098 bool TextElement::isInvisible() const
00099 {
00100     if (getElementType() != 0) {
00101         return getElementType()->isInvisible(*this);
00102     }
00103     return false;
00104 }
00105 
00106 
00111 void TextElement::calcSizes( const ContextStyle& context, 
00112                              ContextStyle::TextStyle tstyle, 
00113                              ContextStyle::IndexStyle /*istyle*/,
00114                              StyleAttributes& style )
00115 {
00116     double factor = style.sizeFactor();
00117     luPt mySize = context.getAdjustedSize( tstyle, factor );
00118 
00119     setCharStyle( style.charStyle() );
00120     setCharFamily( style.charFamily() );
00121 
00122     QFont font = getFont( context, style );
00123     double fontsize = context.layoutUnitPtToPt( mySize );
00124     double scriptsize = pow( style.scriptSizeMultiplier(), style.scriptLevel() );
00125     double size = fontsize * scriptsize;
00126     font.setPointSizeFloat( size < style.scriptMinSize() ? style.scriptMinSize() : size );
00127 
00128     QFontMetrics fm( font );
00129     if ( character == applyFunctionChar || character == invisibleTimes || character == invisibleComma ) {
00130         setWidth( 0 );
00131         setHeight( 0 );
00132         setBaseline( getHeight() );
00133     }
00134     else {
00135         QChar ch = getRealCharacter(context);
00136         if ( ch == QChar::null ) {
00137             setWidth( qRound( context.getEmptyRectWidth( factor ) * 2./3. ) );
00138             setHeight( qRound( context.getEmptyRectHeight( factor ) * 2./3. ) );
00139             setBaseline( getHeight() );
00140         }
00141         else {
00142             QRect bound = fm.boundingRect( ch );
00143             setWidth( context.ptToLayoutUnitPt( fm.width( ch ) ) );
00144             setHeight( context.ptToLayoutUnitPt( bound.height() ) );
00145             setBaseline( context.ptToLayoutUnitPt( -bound.top() ) );
00146 
00147             // There are some glyphs in TeX that have
00148             // baseline==0. (\int, \sum, \prod)
00149             if ( getBaseline() == 0 ) {
00150                 //setBaseline( getHeight()/2 + context.axisHeight( tstyle ) );
00151                 setBaseline( -1 );
00152             }
00153         }
00154     }
00155 }
00156 
00162 void TextElement::draw( QPainter& painter, const LuPixelRect& /*r*/,
00163                         const ContextStyle& context,
00164                         ContextStyle::TextStyle tstyle,
00165                         ContextStyle::IndexStyle /*istyle*/,
00166                         StyleAttributes& style,
00167                         const LuPixelPoint& parentOrigin )
00168 {
00169     if ( character == applyFunctionChar || character == invisibleTimes || character == invisibleComma ) {
00170         return;
00171     }
00172 
00173     LuPixelPoint myPos( parentOrigin.x()+getX(), parentOrigin.y()+getY() );
00174     //if ( !LuPixelRect( myPos.x(), myPos.y(), getWidth(), getHeight() ).intersects( r ) )
00175     //    return;
00176 
00177     // Let container set the color, instead of elementType
00178     //setUpPainter( context, painter );
00179     painter.setPen( style.color() );
00180 
00181     setCharStyle( style.charStyle() );
00182     setCharFamily( style.charFamily() );
00183 
00184     double factor = style.sizeFactor();
00185     luPt mySize = context.getAdjustedSize( tstyle, factor );
00186     QFont font = getFont( context, style );
00187     double fontsize = context.layoutUnitPtToPt( mySize );
00188     double scriptsize = pow( style.scriptSizeMultiplier(), style.scriptLevel() );
00189     double size = fontsize * scriptsize;
00190     font.setPointSizeFloat( size < style.scriptMinSize() ? style.scriptMinSize() : size );
00191     painter.setFont( font );
00192 
00193     //kdDebug( DEBUGID ) << "TextElement::draw font=" << font.rawName() << endl;
00194     //kdDebug( DEBUGID ) << "TextElement::draw size=" << mySize << endl;
00195     //kdDebug( DEBUGID ) << "TextElement::draw size=" << context.layoutUnitToFontSize( mySize, false ) << endl;
00196     //kdDebug( DEBUGID ) << "TextElement::draw height: " << getHeight() << endl;
00197     //kdDebug( DEBUGID ) << "TextElement::draw width: " << getWidth() << endl;
00198     //kdDebug( DEBUGID ) << endl;
00199 
00200     // Each starting element draws the whole token
00201     /*
00202     ElementType* token = getElementType();
00203     if ( ( token != 0 ) && !symbol ) {
00204         QString text = token->text( static_cast<SequenceElement*>( getParent() ) );
00205 //         kdDebug() << "draw text: " << text[0].unicode()
00206 //                   << " " << painter.font().family().latin1()
00207 //                   << endl;
00208         painter.fillRect( context.layoutUnitToPixelX( parentOrigin.x() ),
00209                           context.layoutUnitToPixelY( parentOrigin.y() ),
00210                           context.layoutUnitToPixelX( getParent()->getWidth() ),
00211                           context.layoutUnitToPixelY( getParent()->getHeight() ),
00212                           style.background() );
00213         painter.drawText( context.layoutUnitToPixelX( myPos.x() ),
00214                           context.layoutUnitToPixelY( myPos.y()+getBaseline() ),
00215                           text );
00216     }
00217     else {
00218     */
00219         QChar ch = getRealCharacter(context);
00220         if ( ch != QChar::null ) {
00221             luPixel bl = getBaseline();
00222             if ( bl == -1 ) {
00223                 // That's quite hacky and actually not the way it's
00224                 // meant to be. You shouldn't calculate a lot in
00225                 // draw. But I don't see how else to deal with
00226                 // baseline==0 glyphs without yet another flag.
00227                 bl = -( getHeight()/2 + context.axisHeight( tstyle, factor ) );
00228             }
00229             painter.drawText( context.layoutUnitToPixelX( myPos.x() ),
00230                               context.layoutUnitToPixelY( myPos.y()+bl ),
00231                               ch );
00232         }
00233         else {
00234             painter.setPen( QPen( context.getErrorColor(),
00235                                   context.layoutUnitToPixelX( context.getLineWidth( factor ) ) ) );
00236             painter.drawRect( context.layoutUnitToPixelX( myPos.x() ),
00237                               context.layoutUnitToPixelY( myPos.y() ),
00238                               context.layoutUnitToPixelX( getWidth() ),
00239                               context.layoutUnitToPixelY( getHeight() ) );
00240         }
00241 //    }
00242 
00243     // Debug
00244     //painter.setBrush(Qt::NoBrush);
00245 //     if ( isSymbol() ) {
00246 //         painter.setPen( Qt::red );
00247 //         painter.drawRect( context.layoutUnitToPixelX( myPos.x() ),
00248 //                           context.layoutUnitToPixelX( myPos.y() ),
00249 //                           context.layoutUnitToPixelX( getWidth() ),
00250 //                           context.layoutUnitToPixelX( getHeight() ) );
00251 //         painter.setPen(Qt::green);
00252 //         painter.drawLine(myPos.x(), myPos.y()+axis( context, tstyle ),
00253 //                          myPos.x()+getWidth(), myPos.y()+axis( context, tstyle ));
00254 //     }
00255 }
00256 
00257 
00258 void TextElement::dispatchFontCommand( FontCommand* cmd )
00259 {
00260     cmd->addTextElement( this );
00261 }
00262 
00263 void TextElement::setCharStyle( CharStyle cs )
00264 {
00265     charStyle( cs );
00266     formula()->changed();
00267 }
00268 
00269 void TextElement::setCharFamily( CharFamily cf )
00270 {
00271     charFamily( cf );
00272     formula()->changed();
00273 }
00274 
00275 QChar TextElement::getRealCharacter(const ContextStyle& context)
00276 {
00277     return character;
00278 /*
00279     if ( !isSymbol() ) {
00280         const FontStyle& fontStyle = context.fontStyle();
00281         const AlphaTable* alphaTable = fontStyle.alphaTable();
00282         if ( alphaTable != 0 ) {
00283             AlphaTableEntry ate = alphaTable->entry( character,
00284                                                      charFamily(),
00285                                                      charStyle() );
00286             if ( ate.valid() ) {
00287                 return ate.pos;
00288             }
00289         }
00290         return character;
00291     }
00292     else {
00293         return getSymbolTable().character(character, charStyle());
00294     }
00295 */
00296 }
00297 
00298 
00299 QFont TextElement::getFont(const ContextStyle& context, const StyleAttributes& style)
00300 {
00301     const FontStyle& fontStyle = context.fontStyle();
00302     QFont font;
00303     if ( style.customFont() ) {
00304         font = style.font();
00305     }
00306     else if (getElementType() != 0) {
00307         font = getElementType()->getFont(context);
00308     }
00309     else {
00310         font = context.getDefaultFont();
00311     }
00312     switch ( charStyle() ) {
00313     case anyChar:
00314         font.setItalic( false );
00315         font.setBold( false );
00316         break;
00317     case normalChar:
00318         font.setItalic( false );
00319         font.setBold( false );
00320         break;
00321     case boldChar:
00322         font.setItalic( false );
00323         font.setBold( true );
00324         break;
00325     case italicChar:
00326         font.setItalic( true );
00327         font.setBold( false );
00328         break;
00329     case boldItalicChar:
00330         font.setItalic( true );
00331         font.setBold( true );
00332         break;
00333     }
00334     return fontStyle.symbolTable()->font( character, font );
00335 }
00336 
00337 
00338 void TextElement::setUpPainter(const ContextStyle& context, QPainter& painter)
00339 {
00340     if (getElementType() != 0) {
00341         getElementType()->setUpPainter(context, painter);
00342     }
00343     else {
00344         painter.setPen(Qt::red);
00345     }
00346 }
00347 
00348 const SymbolTable& TextElement::getSymbolTable() const
00349 {
00350     return formula()->getSymbolTable();
00351 }
00352 
00353 
00354 void TextElement::writeMathML( QDomDocument& doc, QDomNode& parent, bool ) const
00355 {
00356     parent.appendChild( doc.createTextNode( getCharacter() ) );
00357 }
00358 
00362 void TextElement::writeDom(QDomElement element)
00363 {
00364     BasicElement::writeDom(element);
00365     element.setAttribute("CHAR", QString(character));
00366     //QString s;
00367     //element.setAttribute("CHAR", s.sprintf( "#x%05X", character ) );
00368     if (symbol) element.setAttribute("SYMBOL", "3");
00369 
00370     switch ( charStyle() ) {
00371     case anyChar: break;
00372     case normalChar: element.setAttribute("STYLE", "normal"); break;
00373     case boldChar: element.setAttribute("STYLE", "bold"); break;
00374     case italicChar: element.setAttribute("STYLE", "italic"); break;
00375     case boldItalicChar: element.setAttribute("STYLE", "bolditalic"); break;
00376     }
00377 
00378     switch ( charFamily() ) {
00379     case normalFamily: element.setAttribute("FAMILY", "normal"); break;
00380     case scriptFamily: element.setAttribute("FAMILY", "script"); break;
00381     case frakturFamily: element.setAttribute("FAMILY", "fraktur"); break;
00382     case doubleStruckFamily: element.setAttribute("FAMILY", "doublestruck"); break;
00383     case anyFamily: break;
00384     }
00385 }
00386 
00391 bool TextElement::readAttributesFromDom(QDomElement element)
00392 {
00393     if (!BasicElement::readAttributesFromDom(element)) {
00394         return false;
00395     }
00396     QString charStr = element.attribute("CHAR");
00397     if(!charStr.isNull()) {
00398         character = charStr.at(0);
00399     }
00400     QString symbolStr = element.attribute("SYMBOL");
00401     if(!symbolStr.isNull()) {
00402         int symbolInt = symbolStr.toInt();
00403         if ( symbolInt == 1 ) {
00404             character = getSymbolTable().unicodeFromSymbolFont(character);
00405         }
00406         if ( symbolInt == 2 ) {
00407             switch ( character.unicode() ) {
00408             case 0x03D5: character = 0x03C6; break;
00409             case 0x03C6: character = 0x03D5; break;
00410             case 0x03Ba: character = 0x03BA; break;
00411             case 0x00B4: character = 0x2032; break;
00412             case 0x2215: character = 0x2244; break;
00413             case 0x00B7: character = 0x2022; break;
00414             case 0x1D574: character = 0x2111; break;
00415             case 0x1D579: character = 0x211C; break;
00416             case 0x2219: character = 0x22C5; break;
00417             case 0x2662: character = 0x26C4; break;
00418             case 0x220B: character = 0x220D; break;
00419             case 0x224C: character = 0x2245; break;
00420             case 0x03DB: character = 0x03C2; break;
00421             }
00422         }
00423         symbol = symbolInt != 0;
00424     }
00425 
00426     QString styleStr = element.attribute("STYLE");
00427     if ( styleStr == "normal" ) {
00428         charStyle( normalChar );
00429     }
00430     else if ( styleStr == "bold" ) {
00431         charStyle( boldChar );
00432     }
00433     else if ( styleStr == "italic" ) {
00434         charStyle( italicChar );
00435     }
00436     else if ( styleStr == "bolditalic" ) {
00437         charStyle( boldItalicChar );
00438     }
00439     else {
00440         charStyle( anyChar );
00441     }
00442 
00443     QString familyStr = element.attribute( "FAMILY" );
00444     if ( familyStr == "normal" ) {
00445         charFamily( normalFamily );
00446     }
00447     else if ( familyStr == "script" ) {
00448         charFamily( scriptFamily );
00449     }
00450     else if ( familyStr == "fraktur" ) {
00451         charFamily( frakturFamily );
00452     }
00453     else if ( familyStr == "doublestruck" ) {
00454         charFamily( doubleStruckFamily );
00455     }
00456     else {
00457         charFamily( anyFamily );
00458     }
00459 
00460 //     kdDebug() << "charStyle=" << charStyle()
00461 //               << "  charFamily=" << charFamily()
00462 //               << "  format=" << int( format() ) << endl;
00463 
00464     return true;
00465 }
00466 
00472 bool TextElement::readContentFromDom(QDomNode& node)
00473 {
00474     return BasicElement::readContentFromDom(node);
00475 }
00476 
00477 QString TextElement::toLatex()
00478 {
00479     if ( isSymbol() ) {
00480         QString texName = getSymbolTable().name( character );
00481         if ( !texName.isNull() )
00482             return " \\" + texName + " ";
00483         return  " ? ";
00484     }
00485     else {
00486         return QString(character);
00487     }
00488 }
00489 
00490 QString TextElement::formulaString()
00491 {
00492     if ( isSymbol() ) {
00493         QString texName = getSymbolTable().name( character );
00494         if ( !texName.isNull() )
00495             return " " + texName + " ";
00496         return " ? ";
00497     }
00498     else {
00499         return character;
00500     }
00501 }
00502 
00503 
00504 EmptyElement::EmptyElement( BasicElement* parent )
00505     : BasicElement( parent )
00506 {
00507 }
00508 
00509 EmptyElement::EmptyElement( const EmptyElement& other )
00510     : BasicElement( other )
00511 {
00512 }
00513 
00514 
00515 bool EmptyElement::accept( ElementVisitor* visitor )
00516 {
00517     return visitor->visit( this );
00518 }
00519 
00520 
00521 void EmptyElement::calcSizes( const ContextStyle& context,
00522                               ContextStyle::TextStyle tstyle,
00523                               ContextStyle::IndexStyle /*istyle*/,
00524                               StyleAttributes& style )
00525 {
00526     luPt mySize = context.getAdjustedSize( tstyle, style.sizeFactor() );
00527     //kdDebug( DEBUGID ) << "TextElement::calcSizes size=" << mySize << endl;
00528 
00529     QFont font = context.getDefaultFont();
00530     font.setPointSizeFloat( context.layoutUnitPtToPt( mySize ) );
00531 
00532     QFontMetrics fm( font );
00533     QChar ch = 'A';
00534     QRect bound = fm.boundingRect( ch );
00535     setWidth( 0 );
00536     setHeight( context.ptToLayoutUnitPt( bound.height() ) );
00537     setBaseline( context.ptToLayoutUnitPt( -bound.top() ) );
00538 }
00539 
00540 void EmptyElement::draw( QPainter& painter, const LuPixelRect& /*r*/,
00541                          const ContextStyle& context,
00542                          ContextStyle::TextStyle /*tstyle*/,
00543                          ContextStyle::IndexStyle /*istyle*/,
00544                          StyleAttributes& /*style*/ ,
00545                          const LuPixelPoint& parentOrigin )
00546 {
00547     LuPixelPoint myPos( parentOrigin.x()+getX(), parentOrigin.y()+getY() );
00548     /*
00549     if ( !LuPixelRect( myPos.x(), myPos.y(), getWidth(), getHeight() ).intersects( r ) )
00550         return;
00551     */
00552 
00553     if ( context.edit() ) {
00554         painter.setPen( context.getHelpColor() );
00555         painter.drawLine( context.layoutUnitToPixelX( myPos.x() ),
00556                           context.layoutUnitToPixelY( myPos.y() ),
00557                           context.layoutUnitToPixelX( myPos.x() ),
00558                           context.layoutUnitToPixelY( myPos.y()+getHeight() ) );
00559     }
00560 }
00561 
00562 QString EmptyElement::toLatex()
00563 {
00564     return "{}";
00565 }
00566 
00567 KFORMULA_NAMESPACE_END
KDE Home | KDE Accessibility Home | Description of Access Keys