kword

KWTextDocument.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2001-2005 David Faure <faure@kde.org>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
00018 */
00019 
00020 #include "KWCommand.h"
00021 #include "KWDocument.h"
00022 #include "KWTextDocument.h"
00023 #include "KWTextFrameSet.h"
00024 #include "KWTextParag.h"
00025 #include "KWLoadingInfo.h"
00026 #include "KWVariable.h"
00027 #include "KWAnchor.h"
00028 #include "KWOasisLoader.h"
00029 #include "KWTableFrameSet.h"
00030 
00031 #include <KoOasisContext.h>
00032 #include <KoXmlNS.h>
00033 #include <KoDom.h>
00034 
00035 #include <kdebug.h>
00036 #include <kglobalsettings.h>
00037 #include <klocale.h>
00038 
00039 KWTextDocument::KWTextDocument( KWTextFrameSet * textfs, KoTextFormatCollection *fc, KoTextFormatter *formatter )
00040     : KoTextDocument( textfs->kWordDocument(), fc, formatter, false ), m_textfs( textfs )
00041 {
00042     init();
00043 }
00044 
00045 KWTextDocument::KWTextDocument( KoTextZoomHandler * zoomHandler )
00046     : KoTextDocument( zoomHandler, new KoTextFormatCollection( KGlobalSettings::generalFont() /*unused*/, QColor(), KGlobal::locale()->language(), false), 0L, false ),
00047       m_textfs( 0 )
00048 {
00049     init();
00050 }
00051 
00052 void KWTextDocument::init()
00053 {
00054     // Create initial paragraph as a KWTextParag
00055     clear( true );
00056 }
00057 
00058 KWTextDocument::~KWTextDocument()
00059 {
00060 }
00061 
00062 KoTextParag * KWTextDocument::createParag( KoTextDocument *d, KoTextParag *pr, KoTextParag *nx, bool updateIds )
00063 {
00064     return new KWTextParag( static_cast<KoTextDocument *>(d), static_cast<KoTextParag *>(pr), static_cast<KoTextParag *>(nx), updateIds );
00065 }
00066 
00067 KoTextDocCommand *KWTextDocument::deleteTextCommand( KoTextDocument *textdoc, int id, int index, const QMemArray<KoTextStringChar> & str, const CustomItemsMap & customItemsMap, const QValueList<KoParagLayout> & oldParagLayouts )
00068 {
00069     //kdDebug(32500)<<" KoTextDocument::deleteTextCommand************\n";
00070     return new KWTextDeleteCommand( textdoc, id, index, str, customItemsMap, oldParagLayouts );
00071 }
00072 
00073 void KWTextDocument::loadOasisTOC( const QDomElement& tag, KoOasisContext& context, KoTextParag* & lastParagraph, KoStyleCollection * styleColl, KoTextParag* nextParagraph )
00074 {
00075     // table-of-content OOo SPEC 7.5 p452
00076     //fillStyleStack( tag, "text:style-name" ); that's the section style
00077 
00078     //QDomElement tocSource = KoDom::namedItemNS( toc, KoXmlNS::text, "table-of-content-source" );
00079     // TODO parse templates and generate "Contents ..." styles from it
00080     //for ( QDomNode n(tocSource.firstChild()); !text.isNull(); text = text.nextSibling() )
00081     //{
00082     //}
00083 
00084     QDomElement tocIndexBody = KoDom::namedItemNS( tag, KoXmlNS::text, "index-body" );
00085     QDomElement t;
00086     forEachElement( t, tocIndexBody )
00087     {
00088         context.styleStack().save();
00089         const QString localName = t.localName();
00090         QDomElement e;
00091         bool isTextNS = tag.namespaceURI() == KoXmlNS::text;
00092         if ( isTextNS && localName == "index-title" ) {
00093             lastParagraph = loadOasisText( t, context, lastParagraph, styleColl, nextParagraph ); // recurse again
00094             lastParagraph->setPartOfTableOfContents( true );
00095         } else if ( isTextNS && localName == "p" ) {
00096             context.fillStyleStack( t, KoXmlNS::text, "style-name", "paragraph" );
00097             lastParagraph = createParag( this, lastParagraph, nextParagraph );
00098             uint pos = 0;
00099             lastParagraph->loadOasis( t, context, styleColl, pos );
00100             lastParagraph->setPartOfTableOfContents( true );
00101         } else
00102             kdWarning() << "OASIS TOC loading: unknown tag " << t.tagName() << " found in index-body" << endl;
00103         context.styleStack().restore();
00104     }
00105 
00106     m_textfs->kWordDocument()->setTocPresent( true );
00107 }
00108 
00109 bool KWTextDocument::loadOasisBodyTag( const QDomElement& tag, KoOasisContext& context,
00110                                        KoTextParag* & lastParagraph, KoStyleCollection* styleColl,
00111                                        KoTextParag* nextParagraph )
00112 {
00113     const QString localName( tag.localName() );
00114     // Non-inline frame (i.e. anchored to page)
00115     if ( localName == "frame" && tag.namespaceURI() == KoXmlNS::draw )
00116     {
00117         KWDocument* doc = m_textfs->kWordDocument();
00118         KWOasisLoader loader( doc );
00119         KWFrame* frame = loader.loadFrame( tag, context, KoPoint() );
00120         if ( frame )
00121             return true;
00122     }
00123 
00124     // Anchored-to-paragraph table.
00125     else if ( localName == "table" && tag.namespaceURI() == KoXmlNS::table )
00126     {
00127         KWDocument* doc = m_textfs->kWordDocument();
00128         KWOasisLoader loader( doc );
00129         KWTableFrameSet* table = loader.loadOasisTable( tag, context );
00130         table->finalize();
00131         // Create paragraph for this table
00132         KoTextParag *parag = createParag( this, lastParagraph, nextParagraph );
00133         if ( !lastParagraph )        // First parag
00134             setFirstParag( parag );
00135         lastParagraph = parag;
00136         // Put inline table in that paragraph
00137         parag->insert( 0, KoTextObject::customItemChar() );
00138         table->setAnchorFrameset( m_textfs );
00139         parag->setCustomItem( 0, table->createAnchor( m_textfs->textDocument(), 0 ), 0 );
00140         return true;
00141     }
00142     else if ( localName == "table-of-content" && tag.namespaceURI() == KoXmlNS::text )
00143     {
00144         loadOasisTOC( tag, context, lastParagraph, styleColl, nextParagraph );
00145         return true;
00146     }
00147 
00148     return false;
00149 }
00150 
00151 void KWTextDocument::appendBookmark( KoTextParag* parag, int pos, KoTextParag* endParag, int endPos, const QString& name )
00152 {
00153     // The OASIS format is cool. No need to store the bookmarks until end of loading (e.g. KWLoadingInfo)
00154     // We can "resolve" them right away.
00155     m_textfs->kWordDocument()->insertBookmark( name, parag, endParag, pos, endPos );
00156 }
00157 
00158 void KWTextDocument::loadOasisFootnote( const QDomElement& tag, KoOasisContext& context,
00159                                         KoTextCustomItem* & customItem )
00160 {
00161     const QString frameName( tag.attributeNS( KoXmlNS::text, "id", QString::null) );
00162     const QString localName( tag.localName() );
00163     const QDomElement citationElem = tag.namedItem( localName + "-citation" ).toElement();
00164 
00165     bool endnote = localName == "endnote" && tag.namespaceURI() == KoXmlNS::text;
00166 
00167     QString label = citationElem.attributeNS( KoXmlNS::text, "label", QString::null );
00168     bool autoNumbered = label.isEmpty();
00169 
00170     KWFootNoteFrameSet *fs = m_textfs->insertFootNote(
00171         endnote ? EndNote : FootNote,
00172         autoNumbered ? KWFootNoteVariable::Auto : KWFootNoteVariable::Manual,
00173         label );
00174     customItem = fs->footNoteVariable();
00175 
00176     fs->createInitialFrame( 0 ); // we don't know the page number...
00177 
00178     // Parse contents into the frameset
00179     const QDomElement bodyElem = KoDom::namedItemNS( tag, KoXmlNS::text, QCString( localName.latin1() ) + "-body" ).toElement();
00180     fs->loadOasisContent( bodyElem, context );
00181 }
00182 
00183 bool KWTextDocument::loadSpanTag( const QDomElement& tag, KoOasisContext& context,
00184                                   KoTextParag* parag, uint pos,
00185                                   QString& textData, KoTextCustomItem* & customItem )
00186 {
00187     const QString localName( tag.localName() );
00188     const bool isTextNS = tag.namespaceURI() == KoXmlNS::text;
00189     kdDebug(32500) << "KWTextDocument::loadSpanTag: " << localName << endl;
00190 
00191     if ( isTextNS )
00192     {
00193         if ( localName == "a" )
00194         {
00195             QString href( tag.attributeNS( KoXmlNS::xlink, "href", QString::null) );
00196             if ( href.startsWith("#") )
00197             {
00198                 context.styleStack().save();
00199                 // We have a reference to a bookmark (### TODO)
00200                 // As we do not support it now, treat it as a <span> without formatting
00201                 parag->loadOasisSpan( tag, context, pos ); // recurse
00202                 context.styleStack().restore();
00203             }
00204             else
00205             {
00206                 // The text is contained in a <span> inside the <a> element. In theory
00207                 // we could have multiple spans there, but OO ensures that there is always only one,
00208                 // splitting the hyperlink if necessary (at format changes).
00209                 // Note that we ignore the formatting of the span.
00210                 QDomElement spanElem = KoDom::namedItemNS( tag, KoXmlNS::text, "span" );
00211                 QString text;
00212                 if( spanElem.isNull() )
00213                     text = tag.text();
00214                 else {
00215                     // The save/restore of the stack is done by the caller (KoTextParag::loadOasisSpan)
00216                     // This allows to use the span's format for the variable.
00217                     //kdDebug(32500) << "filling stack with " << spanElem.attributeNS( KoXmlNS::text, "style-name", QString::null ) << endl;
00218                     context.fillStyleStack( spanElem, KoXmlNS::text, "style-name", "text" );
00219                     text = spanElem.text();
00220                 }
00221                 textData = KoTextObject::customItemChar(); // hyperlink placeholder
00222                 // unused tag.attributeNS( KoXmlNS::office, "name", QString::null )
00223                 KoVariableCollection& coll = context.variableCollection();
00224                 customItem = new KoLinkVariable( this, text, href,
00225                                                  coll.formatCollection()->format( "STRING" ),
00226                                                  &coll );
00227             }
00228             return true;
00229         }
00230         else if ( localName == "bookmark" )
00231         {
00232             appendBookmark( parag, pos, parag, pos, tag.attributeNS( KoXmlNS::text, "name", QString::null ) );
00233             return true;
00234         }
00235         else if ( localName == "bookmark-start" ) {
00236             KWLoadingInfo* loadingInfo = m_textfs->kWordDocument()->loadingInfo();
00237             loadingInfo->m_bookmarkStarts.insert( tag.attributeNS( KoXmlNS::text, "name", QString::null ),
00238                                                   KWLoadingInfo::BookmarkStart( this, parag, pos ) );
00239             return true;
00240         }
00241         else if ( localName == "bookmark-end" ) {
00242             KWLoadingInfo* loadingInfo = m_textfs->kWordDocument()->loadingInfo();
00243             QString bkName = tag.attributeNS( KoXmlNS::text, "name", QString::null );
00244             KWLoadingInfo::BookmarkStartsMap::iterator it = loadingInfo->m_bookmarkStarts.find( bkName );
00245             if ( it == loadingInfo->m_bookmarkStarts.end() ) { // bookmark end without start. This seems to happen..
00246                 // insert simple bookmark then
00247                 appendBookmark( parag, pos, parag, pos, tag.attributeNS( KoXmlNS::text, "name", QString::null ) );
00248             } else {
00249                 if ( (*it).doc != this ) {
00250                     // Oh tell me this never happens...
00251                     kdWarning(32500) << "Cross-frameset bookmark! Not supported." << endl;
00252                 } else {
00253                     appendBookmark( (*it).parag, (*it).pos, parag, pos, it.key() );
00254                 }
00255                 loadingInfo->m_bookmarkStarts.remove( it );
00256             }
00257             return true;
00258         }
00259         else if ( localName == "footnote" || localName == "endnote" )
00260         {
00261             textData = KoTextObject::customItemChar(); // anchor placeholder
00262             loadOasisFootnote( tag, context, customItem );
00263             return true;
00264         }
00265     }
00266     else // not in the "text" namespace
00267     {
00268         if ( tag.namespaceURI() == KoXmlNS::draw && localName == "frame" )
00269         {
00270             if ( tag.attributeNS( KoXmlNS::koffice, "is-wrapper-frame", QString::null )
00271                  == "true" )
00272             {
00273                 QDomElement textbox = KoDom::namedItemNS( tag, KoXmlNS::draw, "text-box" );
00274                 if ( !textbox.isNull() )
00275                 {
00276                     int numberOfElements = 0;
00277                     QDomElement elem;
00278                     QDomElement firstElem;
00279                     forEachElement( elem, textbox )
00280                     {
00281                         ++numberOfElements;
00282                         firstElem = elem;
00283                     }
00284                     if ( numberOfElements == 1 ) // if someone added more stuff, keep the wrapper frame
00285                     {
00286                         kdDebug(32001) << "Wrapper frame removed, loading " << firstElem.tagName() << " directly" << endl;
00287                         // load the only child, e.g. table:table
00288                         return loadSpanTag( firstElem, context, parag, pos, textData, customItem );
00289                     }
00290                 }
00291                 return true;
00292             }
00293 
00294             KWDocument* doc = m_textfs->kWordDocument();
00295             KWOasisLoader loader( doc );
00296             KWFrame* frame = loader.loadFrame( tag, context, KoPoint() );
00297             if ( frame )
00298             {
00299                 KWFrameSet* fs = frame->frameSet();
00300                 // Hmm, if this is a continuation frame of a non-inline frameset,
00301                 // it's going to inline the whole frameset...
00302                 // ###### In fact this shows we should inline frames, not framesets, in KWord (!!!!) (big TODO)
00303                 // ## well, for tables it's the whole frameset.
00304                 textData = KoTextObject::customItemChar();
00305                 fs->setAnchorFrameset( m_textfs );
00306                 customItem = fs->createAnchor( m_textfs->textDocument(), 0 /*frame number; TODO somehow*/ );
00307             }
00308             return true;
00309         }
00310         // anchored-as-char table. Not supported by OASIS directly, but we end up
00311         // calling this when removing the wrapper frame above.
00312         else if ( tag.namespaceURI() == KoXmlNS::table && localName == "table" )
00313         {
00314             KWDocument* doc = m_textfs->kWordDocument();
00315             KWOasisLoader loader( doc );
00316             KWTableFrameSet* table = loader.loadOasisTable( tag, context );
00317             table->finalize();
00318             textData = KoTextObject::customItemChar();
00319             table->setAnchorFrameset( m_textfs );
00320             customItem = table->createAnchor( m_textfs->textDocument(), 0 /*frame number*/ );
00321             return true;
00322         }
00323     }
00324     return false;
00325 }
00326 
00327 #include "KWTextDocument.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys