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, ContextStyle::TextStyle tstyle, ContextStyle::IndexStyle /*istyle*/)
00112 {
00113     luPt mySize = context.getAdjustedSize( tstyle );
00114     //kdDebug( DEBUGID ) << "TextElement::calcSizes size=" << mySize << endl;
00115 
00116     QFont font = getFont( context );
00117     font.setPointSizeFloat( context.layoutUnitPtToPt( mySize ) );
00118 
00119     QFontMetrics fm( font );
00120     QChar ch = getRealCharacter(context);
00121     if ( ch != QChar::null ) {
00122         QRect bound = fm.boundingRect( ch );
00123         setWidth( context.ptToLayoutUnitPt( fm.width( ch ) ) );
00124         setHeight( context.ptToLayoutUnitPt( bound.height() ) );
00125         setBaseline( context.ptToLayoutUnitPt( -bound.top() ) );
00126 
00127         // There are some glyphs in TeX that have
00128         // baseline==0. (\int, \sum, \prod)
00129         if ( getBaseline() == 0 ) {
00130             //setBaseline( getHeight()/2 + context.axisHeight( tstyle ) );
00131             setBaseline( -1 );
00132         }
00133     }
00134     else {
00135         setWidth( qRound( context.getEmptyRectWidth() * 2./3. ) );
00136         setHeight( qRound( context.getEmptyRectHeight() * 2./3. ) );
00137         setBaseline( getHeight() );
00138     }
00139 
00140     //kdDebug( DEBUGID ) << "height: " << getHeight() << endl;
00141     //kdDebug( DEBUGID ) << "width: " << getWidth() << endl;
00142 }
00143 
00149 void TextElement::draw( QPainter& painter, const LuPixelRect& /*r*/,
00150                         const ContextStyle& context,
00151                         ContextStyle::TextStyle tstyle,
00152                         ContextStyle::IndexStyle /*istyle*/,
00153                         const LuPixelPoint& parentOrigin )
00154 {
00155     LuPixelPoint myPos( parentOrigin.x()+getX(), parentOrigin.y()+getY() );
00156     //if ( !LuPixelRect( myPos.x(), myPos.y(), getWidth(), getHeight() ).intersects( r ) )
00157     //    return;
00158 
00159     setUpPainter( context, painter );
00160 
00161     luPt mySize = context.getAdjustedSize( tstyle );
00162     QFont font = getFont( context );
00163     font.setPointSizeFloat( context.layoutUnitToFontSize( mySize, false ) );
00164     painter.setFont( font );
00165 
00166     //kdDebug( DEBUGID ) << "TextElement::draw font=" << font.rawName() << endl;
00167     //kdDebug( DEBUGID ) << "TextElement::draw size=" << mySize << endl;
00168     //kdDebug( DEBUGID ) << "TextElement::draw size=" << context.layoutUnitToFontSize( mySize, false ) << endl;
00169     //kdDebug( DEBUGID ) << "TextElement::draw height: " << getHeight() << endl;
00170     //kdDebug( DEBUGID ) << "TextElement::draw width: " << getWidth() << endl;
00171     //kdDebug( DEBUGID ) << endl;
00172 
00173     // Each starting element draws the whole token
00174     ElementType* token = getElementType();
00175     if ( ( token != 0 ) && !symbol ) {
00176         QString text = token->text( static_cast<SequenceElement*>( getParent() ) );
00177 //         kdDebug() << "draw text: " << text[0].unicode()
00178 //                   << " " << painter.font().family().latin1()
00179 //                   << endl;
00180         painter.drawText( context.layoutUnitToPixelX( myPos.x() ),
00181                           context.layoutUnitToPixelY( myPos.y()+getBaseline() ),
00182                           text );
00183     }
00184     else {
00185         //kdDebug() << "draw char" << endl;
00186         QChar ch = getRealCharacter(context);
00187         if ( ch != QChar::null ) {
00188             luPixel bl = getBaseline();
00189             if ( bl == -1 ) {
00190                 // That's quite hacky and actually not the way it's
00191                 // meant to be. You shouldn't calculate a lot in
00192                 // draw. But I don't see how else to deal with
00193                 // baseline==0 glyphs without yet another flag.
00194                 bl = -( getHeight()/2 + context.axisHeight( tstyle ) );
00195             }
00196             painter.drawText( context.layoutUnitToPixelX( myPos.x() ),
00197                               context.layoutUnitToPixelY( myPos.y()+bl ),
00198                               ch );
00199         }
00200         else {
00201             painter.setPen( QPen( context.getErrorColor(),
00202                                   context.layoutUnitToPixelX( context.getLineWidth() ) ) );
00203             painter.drawRect( context.layoutUnitToPixelX( myPos.x() ),
00204                               context.layoutUnitToPixelY( myPos.y() ),
00205                               context.layoutUnitToPixelX( getWidth() ),
00206                               context.layoutUnitToPixelY( getHeight() ) );
00207         }
00208     }
00209 
00210     // Debug
00211     //painter.setBrush(Qt::NoBrush);
00212 //     if ( isSymbol() ) {
00213 //         painter.setPen( Qt::red );
00214 //         painter.drawRect( context.layoutUnitToPixelX( myPos.x() ),
00215 //                           context.layoutUnitToPixelX( myPos.y() ),
00216 //                           context.layoutUnitToPixelX( getWidth() ),
00217 //                           context.layoutUnitToPixelX( getHeight() ) );
00218 //         painter.setPen(Qt::green);
00219 //         painter.drawLine(myPos.x(), myPos.y()+axis( context, tstyle ),
00220 //                          myPos.x()+getWidth(), myPos.y()+axis( context, tstyle ));
00221 //     }
00222 }
00223 
00224 
00225 void TextElement::dispatchFontCommand( FontCommand* cmd )
00226 {
00227     cmd->addTextElement( this );
00228 }
00229 
00230 void TextElement::setCharStyle( CharStyle cs )
00231 {
00232     charStyle( cs );
00233     formula()->changed();
00234 }
00235 
00236 void TextElement::setCharFamily( CharFamily cf )
00237 {
00238     charFamily( cf );
00239     formula()->changed();
00240 }
00241 
00242 QChar TextElement::getRealCharacter(const ContextStyle& context)
00243 {
00244     if ( !isSymbol() ) {
00245         const FontStyle& fontStyle = context.fontStyle();
00246         const AlphaTable* alphaTable = fontStyle.alphaTable();
00247         if ( alphaTable != 0 ) {
00248             AlphaTableEntry ate = alphaTable->entry( character,
00249                                                      charFamily(),
00250                                                      charStyle() );
00251             if ( ate.valid() ) {
00252                 return ate.pos;
00253             }
00254         }
00255         return character;
00256     }
00257     else {
00258         return getSymbolTable().character(character, charStyle());
00259     }
00260 }
00261 
00262 
00263 QFont TextElement::getFont(const ContextStyle& context)
00264 {
00265     if ( !isSymbol() ) {
00266         const FontStyle& fontStyle = context.fontStyle();
00267         const AlphaTable* alphaTable = fontStyle.alphaTable();
00268         if ( alphaTable != 0 ) {
00269             AlphaTableEntry ate = alphaTable->entry( character,
00270                                                      charFamily(),
00271                                                      charStyle() );
00272             if ( ate.valid() ) {
00273                 return ate.font;
00274             }
00275         }
00276         QFont font;
00277         if (getElementType() != 0) {
00278             font = getElementType()->getFont(context);
00279         }
00280         else {
00281             font = context.getDefaultFont();
00282         }
00283         switch ( charStyle() ) {
00284         case anyChar:
00285             break;
00286         case normalChar:
00287             font.setItalic( false );
00288             font.setBold( false );
00289             break;
00290         case boldChar:
00291             font.setItalic( false );
00292             font.setBold( true );
00293             break;
00294         case italicChar:
00295             font.setItalic( true );
00296             font.setBold( false );
00297             break;
00298         case boldItalicChar:
00299             font.setItalic( true );
00300             font.setBold( true );
00301             break;
00302         }
00303         return font;
00304     }
00305     return context.symbolTable().font( character, charStyle() );
00306 }
00307 
00308 
00309 void TextElement::setUpPainter(const ContextStyle& context, QPainter& painter)
00310 {
00311     if (getElementType() != 0) {
00312         getElementType()->setUpPainter(context, painter);
00313     }
00314     else {
00315         painter.setPen(Qt::red);
00316     }
00317 }
00318 
00319 const SymbolTable& TextElement::getSymbolTable() const
00320 {
00321     return formula()->getSymbolTable();
00322 }
00323 
00324 
00328 void TextElement::writeDom(QDomElement element)
00329 {
00330     BasicElement::writeDom(element);
00331     element.setAttribute("CHAR", QString(character));
00332     //QString s;
00333     //element.setAttribute("CHAR", s.sprintf( "#x%05X", character ) );
00334     if (symbol) element.setAttribute("SYMBOL", "3");
00335 
00336     switch ( charStyle() ) {
00337     case anyChar: break;
00338     case normalChar: element.setAttribute("STYLE", "normal"); break;
00339     case boldChar: element.setAttribute("STYLE", "bold"); break;
00340     case italicChar: element.setAttribute("STYLE", "italic"); break;
00341     case boldItalicChar: element.setAttribute("STYLE", "bolditalic"); break;
00342     }
00343 
00344     switch ( charFamily() ) {
00345     case normalFamily: element.setAttribute("FAMILY", "normal"); break;
00346     case scriptFamily: element.setAttribute("FAMILY", "script"); break;
00347     case frakturFamily: element.setAttribute("FAMILY", "fraktur"); break;
00348     case doubleStruckFamily: element.setAttribute("FAMILY", "doublestruck"); break;
00349     case anyFamily: break;
00350     }
00351 }
00352 
00357 bool TextElement::readAttributesFromDom(QDomElement element)
00358 {
00359     if (!BasicElement::readAttributesFromDom(element)) {
00360         return false;
00361     }
00362     QString charStr = element.attribute("CHAR");
00363     if(!charStr.isNull()) {
00364         character = charStr.at(0);
00365     }
00366     QString symbolStr = element.attribute("SYMBOL");
00367     if(!symbolStr.isNull()) {
00368         int symbolInt = symbolStr.toInt();
00369         if ( symbolInt == 1 ) {
00370             character = getSymbolTable().unicodeFromSymbolFont(character);
00371         }
00372         if ( symbolInt == 2 ) {
00373             switch ( character.unicode() ) {
00374             case 0x03D5: character = 0x03C6; break;
00375             case 0x03C6: character = 0x03D5; break;
00376             case 0x03Ba: character = 0x03BA; break;
00377             case 0x00B4: character = 0x2032; break;
00378             case 0x2215: character = 0x2244; break;
00379             case 0x00B7: character = 0x2022; break;
00380             case 0x1D574: character = 0x2111; break;
00381             case 0x1D579: character = 0x211C; break;
00382             case 0x2219: character = 0x22C5; break;
00383             case 0x2662: character = 0x26C4; break;
00384             case 0x220B: character = 0x220D; break;
00385             case 0x224C: character = 0x2245; break;
00386             case 0x03DB: character = 0x03C2; break;
00387             }
00388         }
00389         symbol = symbolInt != 0;
00390     }
00391 
00392     QString styleStr = element.attribute("STYLE");
00393     if ( styleStr == "normal" ) {
00394         charStyle( normalChar );
00395     }
00396     else if ( styleStr == "bold" ) {
00397         charStyle( boldChar );
00398     }
00399     else if ( styleStr == "italic" ) {
00400         charStyle( italicChar );
00401     }
00402     else if ( styleStr == "bolditalic" ) {
00403         charStyle( boldItalicChar );
00404     }
00405     else {
00406         charStyle( anyChar );
00407     }
00408 
00409     QString familyStr = element.attribute( "FAMILY" );
00410     if ( familyStr == "normal" ) {
00411         charFamily( normalFamily );
00412     }
00413     else if ( familyStr == "script" ) {
00414         charFamily( scriptFamily );
00415     }
00416     else if ( familyStr == "fraktur" ) {
00417         charFamily( frakturFamily );
00418     }
00419     else if ( familyStr == "doublestruck" ) {
00420         charFamily( doubleStruckFamily );
00421     }
00422     else {
00423         charFamily( anyFamily );
00424     }
00425 
00426 //     kdDebug() << "charStyle=" << charStyle()
00427 //               << "  charFamily=" << charFamily()
00428 //               << "  format=" << int( format() ) << endl;
00429 
00430     return true;
00431 }
00432 
00438 bool TextElement::readContentFromDom(QDomNode& node)
00439 {
00440     return BasicElement::readContentFromDom(node);
00441 }
00442 
00443 QString TextElement::toLatex()
00444 {
00445     if ( isSymbol() ) {
00446         QString texName = getSymbolTable().name( character );
00447         if ( !texName.isNull() )
00448             return " \\" + texName + " ";
00449         return  " ? ";
00450     }
00451     else {
00452         return QString(character);
00453     }
00454 }
00455 
00456 QString TextElement::formulaString()
00457 {
00458     if ( isSymbol() ) {
00459         QString texName = getSymbolTable().name( character );
00460         if ( !texName.isNull() )
00461             return " " + texName + " ";
00462         return " ? ";
00463     }
00464     else {
00465         return character;
00466     }
00467 }
00468 
00469 
00470 EmptyElement::EmptyElement( BasicElement* parent )
00471     : BasicElement( parent )
00472 {
00473 }
00474 
00475 EmptyElement::EmptyElement( const EmptyElement& other )
00476     : BasicElement( other )
00477 {
00478 }
00479 
00480 
00481 bool EmptyElement::accept( ElementVisitor* visitor )
00482 {
00483     return visitor->visit( this );
00484 }
00485 
00486 
00487 void EmptyElement::calcSizes( const ContextStyle& context,
00488                               ContextStyle::TextStyle tstyle,
00489                               ContextStyle::IndexStyle /*istyle*/ )
00490 {
00491     luPt mySize = context.getAdjustedSize( tstyle );
00492     //kdDebug( DEBUGID ) << "TextElement::calcSizes size=" << mySize << endl;
00493 
00494     QFont font = context.getDefaultFont();
00495     font.setPointSizeFloat( context.layoutUnitPtToPt( mySize ) );
00496 
00497     QFontMetrics fm( font );
00498     QChar ch = 'A';
00499     QRect bound = fm.boundingRect( ch );
00500     setWidth( 0 );
00501     setHeight( context.ptToLayoutUnitPt( bound.height() ) );
00502     setBaseline( context.ptToLayoutUnitPt( -bound.top() ) );
00503 }
00504 
00505 void EmptyElement::draw( QPainter& painter, const LuPixelRect& /*r*/,
00506                          const ContextStyle& context,
00507                          ContextStyle::TextStyle /*tstyle*/,
00508                          ContextStyle::IndexStyle /*istyle*/,
00509                          const LuPixelPoint& parentOrigin )
00510 {
00511     LuPixelPoint myPos( parentOrigin.x()+getX(), parentOrigin.y()+getY() );
00512     /*
00513     if ( !LuPixelRect( myPos.x(), myPos.y(), getWidth(), getHeight() ).intersects( r ) )
00514         return;
00515     */
00516 
00517     if ( context.edit() ) {
00518         painter.setPen( context.getHelpColor() );
00519         painter.drawLine( context.layoutUnitToPixelX( myPos.x() ),
00520                           context.layoutUnitToPixelY( myPos.y() ),
00521                           context.layoutUnitToPixelX( myPos.x() ),
00522                           context.layoutUnitToPixelY( myPos.y()+getHeight() ) );
00523     }
00524 }
00525 
00526 QString EmptyElement::toLatex()
00527 {
00528     return "{}";
00529 }
00530 
00531 KFORMULA_NAMESPACE_END
KDE Home | KDE Accessibility Home | Description of Access Keys