/* This file is part of the KDE project Copyright (C) 2001, 2002, 2003 The Karbon Developers 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 <tqdom.h> #include <tqfileinfo.h> #include <tqpainter.h> #include <tqpaintdevicemetrics.h> #include <tdeconfig.h> #include <kdebug.h> #include <tdelocale.h> #include <tdetempfile.h> #include <KoTemplateChooseDia.h> #include <KoStoreDevice.h> #include <KoOasisStyles.h> #include <KoOasisLoadingContext.h> #include <KoXmlWriter.h> #include <KoXmlNS.h> #include <KoDom.h> #include <KoOasisSettings.h> #include <KoMainWindow.h> #include "karbon_factory.h" #include "karbon_part.h" #include "karbon_part_iface.h" #include "karbon_view.h" #include "vcommand.h" #include "vglobal.h" #include "vpainter.h" #include "vpainterfactory.h" #include "vselection.h" #include "vcanvas.h" #include "vlayer.h" #include "vdocumentdocker.h" #include "vtoolcontroller.h" #include "KoApplication.h" #include "vtool.h" #include "commands/vtransformcmd.h" // Make sure an appropriate DTD is available in www/koffice/DTD if changing this value // static const char * CURRENT_DTD_VERSION = "1.2"; KarbonPart::KarbonPart( TQWidget* parentWidget, const char* widgetName, TQObject* parent, const char* name, bool singleViewMode ) : KoDocument( parentWidget, widgetName, parent, name, singleViewMode ) { setInstance( KarbonFactory::instance(), false ); setTemplateType( "karbon_template" ); m_bShowStatusBar = true; dcop = 0L; m_commandHistory = new VCommandHistory( this ); connect( m_commandHistory, TQT_SIGNAL( documentRestored() ), this, TQT_SLOT( slotDocumentRestored() ) ); connect( m_commandHistory, TQT_SIGNAL( commandExecuted( VCommand * ) ), this, TQT_SLOT( slotCommandExecuted( VCommand * ) ) ); initConfig(); m_merge = false; m_maxRecentFiles = 10; //if( name ) dcopObject(); // set as default paper m_pageLayout.format = KoPageFormat::defaultFormat(); m_pageLayout.orientation = PG_PORTRAIT; m_pageLayout.ptWidth = MM_TO_POINT( KoPageFormat::width( m_pageLayout.format, m_pageLayout.orientation ) ); m_pageLayout.ptHeight = MM_TO_POINT( KoPageFormat::height( m_pageLayout.format, m_pageLayout.orientation ) ); m_doc.setWidth( m_pageLayout.ptWidth ); m_doc.setHeight( m_pageLayout.ptHeight ); // enable selection drawing m_doc.selection()->showHandle(); m_doc.selection()->setSelectObjects(); m_doc.selection()->setState( VObject::selected ); m_doc.selection()->selectNodes(); } KarbonPart::~KarbonPart() { // delete the command-history: delete m_commandHistory; delete dcop; } DCOPObject* KarbonPart::dcopObject() { if( !dcop ) dcop = new KarbonPartIface( this ); return dcop; } void KarbonPart::setPageLayout( KoPageLayout& layout, KoUnit::Unit _unit ) { m_pageLayout = layout; m_doc.setUnit( _unit ); m_doc.setWidth( m_pageLayout.ptWidth ); m_doc.setHeight( m_pageLayout.ptHeight ); } bool KarbonPart::initDoc(InitDocFlags flags, TQWidget* parentWidget) { if (flags==KoDocument::InitDocEmpty) { return true; } TQString file; KoTemplateChooseDia::ReturnType result; KoTemplateChooseDia::DialogType dlgtype; if (flags != KoDocument::InitDocFileNew) dlgtype = KoTemplateChooseDia::Everything; else dlgtype = KoTemplateChooseDia::OnlyTemplates; result = KoTemplateChooseDia::choose( KarbonFactory::instance(), file, dlgtype, "karbon_template", parentWidget ); if( result == KoTemplateChooseDia::Template ) { resetURL(); bool ok = loadNativeFormat( file ); if ( !ok ) showLoadingErrorDialog(); setEmpty(); return ok; } else if( result == KoTemplateChooseDia::Empty ) { return true; } else if( result == KoTemplateChooseDia::File ) { KURL url( file ); return openURL( url ); } return false; } KoView* KarbonPart::createViewInstance( TQWidget* parent, const char* name ) { KarbonView *result = new KarbonView( this, parent, name ); return result; } void KarbonPart::removeView( KoView *view ) { kdDebug(38000) << "KarbonPart::removeView" << endl; KoDocument::removeView( view ); } double getAttribute(TQDomElement &element, const char *attributeName, double defaultValue) { TQString value; if ( ( value = element.attribute( attributeName ) ) != TQString() ) return value.toDouble(); else return defaultValue; } int getAttribute(TQDomElement &element, const char *attributeName, int defaultValue) { TQString value; if ( ( value = element.attribute( attributeName ) ) != TQString() ) return value.toInt(); else return defaultValue; } bool KarbonPart::loadXML( TQIODevice*, const TQDomDocument& document ) { bool success = false; TQDomElement doc = document.documentElement(); if( m_merge ) { m_doc.loadDocumentContent( doc ); return true; } success = m_doc.loadXML( doc ); //m_pageLayout = KoPageLayout::standardLayout(); // <PAPER> TQDomElement paper = doc.namedItem( "PAPER" ).toElement(); if ( !paper.isNull() ) { m_pageLayout.format = static_cast<KoFormat>( getAttribute( paper, "format", 0 ) ); m_pageLayout.orientation = static_cast<KoOrientation>( getAttribute( paper, "orientation", 0 ) ); if( m_pageLayout.format == PG_CUSTOM ) { m_pageLayout.ptWidth = m_doc.width(); m_pageLayout.ptHeight = m_doc.height(); } else { m_pageLayout.ptWidth = getAttribute( paper, "width", 0.0 ); m_pageLayout.ptHeight = getAttribute( paper, "height", 0.0 ); } } else { m_pageLayout.ptWidth = getAttribute( doc, "width", 595.277); m_pageLayout.ptHeight = getAttribute( doc, "height", 841.891 ); } kdDebug() << " ptWidth=" << m_pageLayout.ptWidth << endl; kdDebug() << " ptHeight=" << m_pageLayout.ptHeight << endl; TQDomElement borders = paper.namedItem( "PAPERBORDERS" ).toElement(); if( !borders.isNull() ) { if( borders.hasAttribute( "ptLeft" ) ) m_pageLayout.ptLeft = borders.attribute( "ptLeft" ).toDouble(); if( borders.hasAttribute( "ptTop" ) ) m_pageLayout.ptTop = borders.attribute( "ptTop" ).toDouble(); if( borders.hasAttribute( "ptRight" ) ) m_pageLayout.ptRight = borders.attribute( "ptRight" ).toDouble(); if( borders.hasAttribute( "ptBottom" ) ) m_pageLayout.ptBottom = borders.attribute( "ptBottom" ).toDouble(); } setUnit( m_doc.unit() ); return success; } TQDomDocument KarbonPart::saveXML() { TQDomDocument doc = m_doc.saveXML(); TQDomElement me = doc.documentElement(); TQDomElement paper = doc.createElement( "PAPER" ); me.appendChild( paper ); paper.setAttribute( "format", static_cast<int>( m_pageLayout.format ) ); paper.setAttribute( "pages", pageCount() ); paper.setAttribute( "width", m_pageLayout.ptWidth ); paper.setAttribute( "height", m_pageLayout.ptHeight ); paper.setAttribute( "orientation", static_cast<int>( m_pageLayout.orientation ) ); TQDomElement paperBorders = doc.createElement( "PAPERBORDERS" ); paperBorders.setAttribute( "ptLeft", m_pageLayout.ptLeft ); paperBorders.setAttribute( "ptTop", m_pageLayout.ptTop ); paperBorders.setAttribute( "ptRight", m_pageLayout.ptRight ); paperBorders.setAttribute( "ptBottom", m_pageLayout.ptBottom ); paper.appendChild(paperBorders); return doc; } bool KarbonPart::loadOasis( const TQDomDocument &doc, KoOasisStyles &styles, const TQDomDocument &settings, KoStore *store ) { kdDebug(38000) << "Start loading OASIS document..." << doc.toString() << endl; TQDomElement contents = doc.documentElement(); kdDebug(38000) << "Start loading OASIS document..." << contents.text() << endl; kdDebug(38000) << "Start loading OASIS contents..." << contents.lastChild().localName() << endl; kdDebug(38000) << "Start loading OASIS contents..." << contents.lastChild().namespaceURI() << endl; kdDebug(38000) << "Start loading OASIS contents..." << contents.lastChild().isElement() << endl; TQDomElement body( KoDom::namedItemNS( contents, KoXmlNS::office, "body" ) ); kdDebug(38000) << "Start loading OASIS document..." << body.text() << endl; if( body.isNull() ) { kdDebug(38000) << "No office:body found!" << endl; setErrorMessage( i18n( "Invalid OASIS document. No office:body tag found." ) ); return false; } body = KoDom::namedItemNS( body, KoXmlNS::office, "drawing"); if(body.isNull()) { kdDebug(38000) << "No office:drawing found!" << endl; setErrorMessage( i18n( "Invalid OASIS document. No office:drawing tag found." ) ); return false; } TQDomElement page( KoDom::namedItemNS( body, KoXmlNS::draw, "page" ) ); if(page.isNull()) { kdDebug(38000) << "No office:drawing found!" << endl; setErrorMessage( i18n( "Invalid OASIS document. No draw:page tag found." ) ); return false; } TQString masterPageName = "Standard"; // use default layout as fallback TQDomElement *master = styles.masterPages()[ masterPageName ]; if ( !master ) //last test... master = styles.masterPages()[ "Default" ]; // last resort, use the first found master page style if ( ! master ) { TQDictIterator<TQDomElement> it( styles.masterPages() ); master = it.current(); } Q_ASSERT( master ); const TQDomElement *style = master ? styles.findStyle( master->attributeNS( KoXmlNS::style, "page-layout-name", TQString() ) ) : 0; if( style ) { m_pageLayout.loadOasis( *style ); m_doc.setWidth( m_pageLayout.ptWidth ); m_doc.setHeight( m_pageLayout.ptHeight ); } else return false; KoOasisLoadingContext context( this, styles, store ); m_doc.loadOasis( page, context ); // do y-mirroring here TQWMatrix mat; mat.scale( 1, -1 ); mat.translate( 0, -m_doc.height() ); VTransformCmd trafo( 0L, mat ); trafo.visit( m_doc ); loadOasisSettings( settings ); return true; } void KarbonPart::loadOasisSettings( const TQDomDocument&settingsDoc ) { if ( settingsDoc.isNull() ) return ; // not an error if some file doesn't have settings.xml KoOasisSettings settings( settingsDoc ); KoOasisSettings::Items viewSettings = settings.itemSet( "view-settings" ); if ( !viewSettings.isNull() ) { setUnit(KoUnit::unit(viewSettings.parseConfigItemString("unit"))); // FIXME: add other config here. } } bool KarbonPart::saveOasis( KoStore *store, KoXmlWriter *manifestWriter ) { if( !store->open( "content.xml" ) ) return false; KoStoreDevice storeDev( store ); KoXmlWriter* docWriter = createOasisXmlWriter( &storeDev, "office:document-content" ); KoGenStyles mainStyles; KoGenStyle pageLayout = m_pageLayout.saveOasis(); TQString layoutName = mainStyles.lookup( pageLayout, "PL" ); KoGenStyle masterPage( KoGenStyle::STYLE_MASTER ); masterPage.addAttribute( "style:page-layout-name", layoutName ); mainStyles.lookup( masterPage, "Default", KoGenStyles::DontForceNumbering ); KTempFile contentTmpFile; contentTmpFile.setAutoDelete( true ); TQFile* tmpFile = contentTmpFile.file(); KoXmlWriter contentTmpWriter( TQT_TQIODEVICE(tmpFile), 1 ); contentTmpWriter.startElement( "office:body" ); contentTmpWriter.startElement( "office:drawing" ); m_doc.saveOasis( store, &contentTmpWriter, mainStyles ); // Save contents contentTmpWriter.endElement(); // office:drawing contentTmpWriter.endElement(); // office:body docWriter->startElement( "office:automatic-styles" ); TQValueList<KoGenStyles::NamedStyle> styles = mainStyles.styles( VDocument::STYLE_GRAPHICAUTO ); TQValueList<KoGenStyles::NamedStyle>::const_iterator it = styles.begin(); for( ; it != styles.end() ; ++it ) (*it).style->writeStyle( docWriter, mainStyles, "style:style", (*it).name , "style:graphic-properties" ); docWriter->endElement(); // office:automatic-styles // And now we can copy over the contents from the tempfile to the real one tmpFile->close(); docWriter->addCompleteElement( TQT_TQIODEVICE(tmpFile) ); contentTmpFile.close(); docWriter->endElement(); // Root element docWriter->endDocument(); delete docWriter; if( !store->close() ) return false; manifestWriter->addManifestEntry( "content.xml", "text/xml" ); if( !store->open( "styles.xml" ) ) return false; KoXmlWriter* styleWriter = createOasisXmlWriter( &storeDev, "office:document-styles" ); styleWriter->startElement( "office:styles" ); styles = mainStyles.styles( VDocument::STYLE_LINEAR_GRADIENT ); it = styles.begin(); for( ; it != styles.end() ; ++it ) (*it).style->writeStyle( styleWriter, mainStyles, "svg:linearGradient", (*it).name, 0, true, true /*add draw:name*/); styles = mainStyles.styles( VDocument::STYLE_RADIAL_GRADIENT ); it = styles.begin(); for( ; it != styles.end() ; ++it ) (*it).style->writeStyle( styleWriter, mainStyles, "svg:radialGradient", (*it).name, 0, true, true /*add draw:name*/); styleWriter->endElement(); // office:styles styleWriter->startElement( "office:automatic-styles" ); TQValueList<KoGenStyles::NamedStyle> styleList = mainStyles.styles( KoGenStyle::STYLE_PAGELAYOUT ); it = styleList.begin(); for( ; it != styleList.end(); ++it ) (*it).style->writeStyle( styleWriter, mainStyles, "style:page-layout", (*it).name, "style:page-layout-properties" ); styleWriter->endElement(); // office:automatic-styles styles = mainStyles.styles( KoGenStyle::STYLE_MASTER ); it = styles.begin(); styleWriter->startElement("office:master-styles"); for( ; it != styles.end(); ++it) (*it).style->writeStyle( styleWriter, mainStyles, "style:master-page", (*it).name, ""); styleWriter->endElement(); // office:master-styles styleWriter->endElement(); // Root element styleWriter->endDocument(); delete styleWriter; if( !store->close() ) return false; manifestWriter->addManifestEntry( "styles.xml", "text/xml" ); if(!store->open("settings.xml")) return false; KoXmlWriter& settingsWriter = *createOasisXmlWriter(&storeDev, "office:document-settings"); settingsWriter.startElement("office:settings"); settingsWriter.startElement("config:config-item-set"); settingsWriter.addAttribute("config:name", "view-settings"); KoUnit::saveOasis(&settingsWriter, unit()); saveOasisSettings( settingsWriter ); settingsWriter.endElement(); // config:config-item-set settingsWriter.endElement(); // office:settings settingsWriter.endElement(); // Root element settingsWriter.endDocument(); delete &settingsWriter; if(!store->close()) return false; manifestWriter->addManifestEntry("settings.xml", "text/xml"); setModified( false ); return true; } void KarbonPart::saveOasisSettings( KoXmlWriter &/*settingsWriter*/ ) { //todo } void KarbonPart::insertObject( VObject* object ) { // don't repaint here explicitly. some commands might want to insert many // objects. m_doc.append( object ); setModified( true ); } void KarbonPart::addCommand( VCommand* cmd, bool repaint ) { m_commandHistory->addCommand( cmd ); setModified( true ); if( repaint ) repaintAllViews(); } void KarbonPart::slotDocumentRestored() { setModified( false ); } void KarbonPart::slotCommandExecuted( VCommand *command ) { setModified( true ); } void KarbonPart::clearHistory() { m_commandHistory->clear(); } void KarbonPart::repaintAllViews( bool repaint ) { TQPtrListIterator<KoView> itr( views() ); for( ; itr.current() ; ++itr ) static_cast<KarbonView*>( itr.current() )->canvasWidget()->repaintAll( repaint ); } void KarbonPart::repaintAllViews( const KoRect &rect ) { TQPtrListIterator<KoView> itr( views() ); for( ; itr.current() ; ++itr ) static_cast<KarbonView*>( itr.current() )->canvasWidget()->repaintAll( rect ); } void KarbonPart::paintContent( TQPainter& painter, const TQRect& rect, bool /*transparent*/, double /*zoomX*/, double /*zoomY*/ ) { kdDebug(38000) << "**** part->paintContent()" << endl; KoRect r = KoRect::fromTQRect( rect ); double zoomFactorX = double( r.width() ) / double( document().width() ); double zoomFactorY = double( r.height() ) / double( document().height() ); double zoomFactor = kMin( zoomFactorX, zoomFactorY ); painter.eraseRect( rect ); VPainterFactory *painterFactory = new VPainterFactory; //TQPaintDeviceMetrics metrics( painter.device() ); painterFactory->setPainter( painter.device(), rect.width(), rect.height() ); VPainter *p = painterFactory->painter(); //VPainter *p = new VKoPainter( painter.device() ); p->begin(); p->setZoomFactor( zoomFactor ); kdDebug(38000) << "painter.worldMatrix().dx() : " << painter.worldMatrix().dx() << endl; kdDebug(38000) << "painter.worldMatrix().dy() : " << painter.worldMatrix().dy() << endl; kdDebug(38000) << "rect.x() : "<< rect.x() << endl; kdDebug(38000) << "rect.y() : "<< rect.y() << endl; kdDebug(38000) << "rect.width() : "<< rect.width() << endl; kdDebug(38000) << "rect.height() : "<< rect.height() << endl; r = document().boundingBox(); TQWMatrix mat = painter.worldMatrix(); mat.scale( 1, -1 ); mat.translate( 0, -r.height() * zoomFactor ); p->setWorldMatrix( mat ); m_doc.selection()->clear(); TQPtrListIterator<VLayer> itr( m_doc.layers() ); for( ; itr.current(); ++itr ) { itr.current()->draw( p, &r ); } p->end(); delete painterFactory; } void KarbonPart::setShowStatusBar( bool b ) { m_bShowStatusBar = b; } void KarbonPart::reorganizeGUI() { TQPtrListIterator<KoView> itr( views() ); for( ; itr.current(); ++itr ) { static_cast<KarbonView*>( itr.current() )->reorganizeGUI(); } } void KarbonPart::setUndoRedoLimit( int undos ) { m_commandHistory->setUndoLimit( undos ); m_commandHistory->setRedoLimit( undos ); } void KarbonPart::initConfig() { TDEConfig* config = KarbonPart::instance()->config(); if( config->hasGroup( "Interface" ) ) { config->setGroup( "Interface" ); setAutoSave( config->readNumEntry( "AutoSave", defaultAutoSave() / 60 ) * 60 ); m_maxRecentFiles = config->readNumEntry( "NbRecentFile", 10 ); setShowStatusBar( config->readBoolEntry( "ShowStatusBar" , true ) ); setBackupFile( config->readNumEntry( "BackupFile", true ) ); m_doc.saveAsPath( config->readBoolEntry( "SaveAsPath", true ) ); } int undos = 30; if( config->hasGroup( "Misc" ) ) { config->setGroup( "Misc" ); undos = config->readNumEntry( "UndoRedo", -1 ); TQString defaultUnit = "cm"; if( TDEGlobal::locale()->measureSystem() == TDELocale::Imperial ) defaultUnit = "in"; setUnit( KoUnit::unit( config->readEntry( "Units", defaultUnit ) ) ); m_doc.setUnit( unit() ); } if( undos != -1 ) setUndoRedoLimit( undos ); } bool KarbonPart::mergeNativeFormat( const TQString &file ) { m_merge = true; bool result = loadNativeFormat( file ); if ( !result ) showLoadingErrorDialog(); m_merge = false; return result; } void KarbonPart::addShell( KoMainWindow *shell ) { connect( shell, TQT_SIGNAL( documentSaved() ), m_commandHistory, TQT_SLOT( documentSaved() ) ); KoDocument::addShell( shell ); } void KarbonPart::slotUnitChanged( KoUnit::Unit /*unit*/ ) { #if 0 // VDocument has its own storage of the unit... m_doc.setUnit( unit ); if( m_toolController->activeTool() ) m_toolController->activeTool()->refreshUnit(); #endif } #include "karbon_part.moc"