// // C++ Implementation: kxedocument // // Description: // // // Author: Adam Charytoniuk , (C) 2004 // // Copyright: See COPYING file that comes with this distribution // // #include "kxedocument.h" #include "kxmleditorfactory.h" #include "kxeconfiguration.h" #include "kxenewfilesettings.h" #include "kxearchiveextssettings.h" #include "kxeprintsettings.h" #include "kxetextviewsettings.h" #include "kxechoosestringdialog.h" #include "kxeattachdialogbase.h" #include "kxespecprocinstrdialog.h" #include "kxefilenewdialog.h" #include "commands_file.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include KXEDocument::KXEDocument(TQObject *parent, const char *name) :TQObject (parent,name), TQDomDocument(), KXMLGUIClient() { m_bDocIsCompressed = false; m_bIsModified = false; m_strCompressedTarEntryName = ""; m_url = ""; //setXMLFile("kxedocument.rc"); } KXEDocument::~KXEDocument() { } bool KXEDocument::save(const TQString &strFileName) { if (this->documentElement().isNull() && KMessageBox::warningContinueCancel(0, i18n("Your file doesn't have root element defined. \n\ Continue saving?"))==KMessageBox::Cancel ) { return false; } TQString strXML; TQTextStream streamXML(&strXML, IO_WriteOnly); int iIndent = KXMLEditorFactory::configuration()->textview()->indentSteps(); ((TQDomDocument*)this)->save(streamXML, iIndent); TQString strEncoding; TQTextCodec *pTextCodec; // find encoding info if(strXML.left(5) == " 0) { // info about encoding found; iStart += 8; // skip encoding // search " or ' after encoding if((iStart = strXML.find(TQRegExp("[\"']"), iStart)) > 0) { TQChar ch = strXML[iStart]; iStart++; // skip ch if((iEnd = strXML.find(ch, iStart)) > 0) { strEncoding = strXML.mid(iStart, iEnd - iStart); } } } } if(strEncoding.length() <= 0) pTextCodec = TQTextCodec::codecForLocale(); // default else pTextCodec = TQTextCodec::codecForName(strEncoding.latin1()); if(pTextCodec == 0) { if(KMessageBox::questionYesNo(0, i18n("Codec for encoding %1 not found ! Continue saving ?").arg(strEncoding)) != KMessageBox::Yes) return false; } TQCString strDecoded; if(pTextCodec) { strDecoded = pTextCodec->fromUnicode(strXML); } // save string to file if(!m_bDocIsCompressed) { TQFile file(strFileName); if(file.open(IO_WriteOnly) == true) { file.writeBlock(strDecoded, strDecoded.length()); file.flush(); file.close(); } else { KMessageBox::error(0, i18n("Can't create file %1").arg(strFileName), i18n("Write error !")); } } else { // obtain file extension ----------------------------------------- TQString strExtension; int iPos = strFileName.findRev('.'); if(iPos > 0) { strExtension = strFileName.mid(iPos + 1); } if(strExtension == "svgz") { KMessageBox::sorry(0, "Saving *.svgz not implemented yet", "sory"); return false; } else { KZip tarGzFile(strFileName); // New KOffice use KZip instead of KTarGz for storing files if(tarGzFile.open(IO_WriteOnly)) { tarGzFile.writeFile(m_strCompressedTarEntryName, "user", "group", strDecoded.length(), strDecoded); tarGzFile.close(); } else { KMessageBox::error(0, i18n("Can't create archive %1").arg(strFileName), i18n("Write error !")); } } } return true; } bool KXEDocument::open(const TQString &strFileName) { TQString strCompressedTarEntryName; kdDebug() << "KXEDocument::open: opening file " << strFileName << endl; // obtain file extension ----------------------------------------- TQString strExtension; int iPos = strFileName.findRev('.'); if(iPos > 0) { strExtension = strFileName.mid(iPos + 1); } TQString strTmpfileName; if ( KXMLEditorFactory::configuration()->archexts()->extensions().contains(strExtension) ) { KTempFile tmp; if (tmp.status() != 0) { kdError() << "Couldn't open temp file" << endl; KMessageBox::sorry(0, i18n("Couldn't open temp file !")); return false; } tmp.setAutoDelete(false); TQFile &fileTemporary = *(tmp.file()); if(strExtension == "svgz") { //----------------------- It is gzip compressed file ----------------------- m_strCompressedTarEntryName = strFileName.left(strFileName.length() - 5); // For SVG compressed icons strip extension, e.g. "kate.svgz" has entry "kate" etc iPos = m_strCompressedTarEntryName.findRev('/'); if(iPos > 0) { m_strCompressedTarEntryName = m_strCompressedTarEntryName.mid(iPos + 1); } TQIODevice *pIODevice = KFilterDev::deviceForFile(strFileName, "application/x-gzip"); if(pIODevice->open( IO_ReadOnly )) { TQTextStream stream(pIODevice); TQString line; //int i = 1; while ( !stream.atEnd() ) { line = stream.readLine(); // line of text excluding '\n' //printf( "%3d: %s\n", i++, line.latin1() ); fileTemporary.writeBlock(line.utf8(), line.utf8().length()); } pIODevice->close(); } } else { //----------------------- It is zip archive file --------------------------- KZip tarGzFile(strFileName); // new KOffice use KZip instead of KTarGz for storing files tarGzFile.open(IO_ReadOnly); fileTemporary.open(IO_WriteOnly); const KTarDirectory *root = tarGzFile.directory(); if(!root) { return false; } // For KOffice files let user to choose maindoc or documentinfo if(strCompressedTarEntryName.length() == 0) { KXEChooseStringDialog dlgChooseString(0, 0, i18n("Choose file").utf8(), i18n("File:").utf8()); dlgChooseString.m_pComboBox->insertItem("maindoc.xml"); dlgChooseString.m_pComboBox->insertItem("documentinfo.xml"); if(dlgChooseString.exec() != KXEChooseStringDialog::Accepted) { return false; } m_strCompressedTarEntryName = dlgChooseString.m_strChoosedText; } else { m_strCompressedTarEntryName = strCompressedTarEntryName; } const KArchiveEntry *entry = root->entry(m_strCompressedTarEntryName); if(entry && entry->isFile()) { const KArchiveFile *pTarFile = static_cast (entry); TQBuffer buffer(pTarFile->data()); buffer.open(IO_ReadOnly); fileTemporary.writeBlock(buffer.buffer(), buffer.size()); } else m_strCompressedTarEntryName.truncate(0); tarGzFile.close(); } strTmpfileName = fileTemporary.name(); fileTemporary.close(); m_bDocIsCompressed = true; } else m_bDocIsCompressed = false; // ( 1.) parse the file and fill our document TQFile file(m_bDocIsCompressed ? strTmpfileName : strFileName); if(! file.open(IO_ReadOnly)) { kdDebug() << "KXEDocument::openFile: Can't open file." << endl; return false; } // auxiliary file for obtaining encoding info TQFile fileAux(m_bDocIsCompressed ? strTmpfileName : strFileName); if(! fileAux.open(IO_ReadOnly)) { kdDebug() << "KXEDocument::openFile: Can't open file." << endl; return false; } TQTextStream txtStreamLocal( & file ); // Lookup at XML document encoding ----------------------------------------------- TQTextStream txtStreamAux( & fileAux ); TQString strFirstLine = txtStreamAux.readLine(); fileAux.close(); int iStart, iEnd; if((iStart = strFirstLine.find("encoding", 0)) > 0) { TQString strEncoding; // info about encoding found; iStart += 8; // skip encoding // search " or ' after encoding if((iStart = strFirstLine.find(TQRegExp("[\"']"), iStart)) > 0) { TQChar ch = strFirstLine[iStart]; iStart++; // skip ch if((iEnd = strFirstLine.find(ch, iStart)) > 0) { strEncoding = strFirstLine.mid(iStart, iEnd - iStart); TQTextCodec *pTextCodec = TQTextCodec::codecForName(strEncoding.latin1()); if(pTextCodec) txtStreamLocal.setCodec(pTextCodec); else { KMessageBox::sorry(0, i18n("Codec for encoding %1 not found ! Using locale encoding for load.").arg(strEncoding)); txtStreamLocal.setEncoding(TQTextStream::Locale); } } } } else { // XML documment dont have info about encoding, set default UTF-8 txtStreamLocal.setCodec(TQTextCodec::codecForName("UTF-8")); } //-------------------------------------------------------------------------------- TQString strFileContents = txtStreamLocal.read(); file.close(); if(m_bDocIsCompressed) { TQDir dir; dir.remove(strTmpfileName); } //-- Set string with XML to TQDomDocument ------------------------------------------ TQString strErrorMsg; int iErrorLine, iErrorColumn; TQDomDocument * pNewDoc = new TQDomDocument; // first try with a new document if( ! pNewDoc->setContent(strFileContents, true, &strErrorMsg, &iErrorLine, &iErrorColumn) ) { kdDebug() << "KXEDocument::openFile: Failed parsing the file." << endl; KMessageBox::error(0, i18n("%1 in line %2, column %3").arg(strErrorMsg).arg(iErrorLine).arg(iErrorColumn), i18n("Parsing error !")); delete pNewDoc; // remove the new document, because it's useless return false; } // The following commented code is performance wise buggy, because the string // gets parsed a second time. I replaced it with this code. // copy the content of the parsed document to this one TQDomNode e = pNewDoc->removeChild( pNewDoc->documentElement() ); TQDomDocument::operator=( *pNewDoc ); appendChild( e ); // Here comes the "buggy" code. //this->setContent(pNewDoc->toString(),true,0,0); // and take the new one //delete pNewDoc; // remove the former document // To test/see the difference in loading time, you can switch the commented // codeblocks above and compare the loading-time-differences measured in // KXMLEditorPart::openFile. // Olaf // TODO: remove the comments above later emit sigOpened(); return true; } void KXEDocument::setModified(bool value) { m_bIsModified = value; emit sigModified(value); } void KXEDocument::setURL(KURL url) { m_url = url; emit sigURLChanged(url); } void KXEDocument::updateNodeCreated(const TQDomNode & node) { emit sigNodeCreated(node); setModified(); } void KXEDocument::updateNodeDeleted(const TQDomNode & node) { emit sigNodeDeleted(node); setModified(); } void KXEDocument::updateNodeChanged( const TQDomElement & domElement ) { emit sigNodeChanged(domElement); setModified(); } void KXEDocument::updateNodeChanged( const TQDomCharacterData & node ) { emit sigNodeChanged(node); setModified(); } void KXEDocument::updateNodeChanged( const TQDomProcessingInstruction &domProcInstr ) { emit sigNodeChanged(domProcInstr); setModified(); } void KXEDocument::updateNodeMoved( const TQDomNode & node ) { emit sigNodeMoved(node); setModified(); } void KXEDocument::attachStylesheet(const KURL& stylesheet) { setSpecProcInstr("xml-stylesheet",TQString("type = 'text/xsl' href = '")+stylesheet.url()+"' "); } void KXEDocument::detachStylesheet() { removeSpecProcInstr("xml-stylesheet"); } void KXEDocument::attachSchema(const KURL& schema) { TQDomElement domElement = documentElement(); if (!domElement.isNull()) { domElement.setAttributeNS(SCHEMA_NAMESPACE, SCHEMA_ATTRIBUTE_XSI, schema.url()); // refresh views updateNodeChanged(domElement); setModified(); } } void KXEDocument::detachSchema() { TQDomElement domElement = this->documentElement(); if (!domElement.isNull()) { domElement.removeAttributeNS(SCHEMA_NAMESPACE,SCHEMA_ATTRIBUTE); // refresh views updateNodeChanged(domElement); setModified(); } } void KXEDocument::setSpecProcInstr(const TQString& target, const TQString& data) { // removing old one removeSpecProcInstr(target); // create new one if (!data.isEmpty()) { TQDomProcessingInstruction domProcInstr = this->createProcessingInstruction(target,data); TQDomNode node = getSpecProcInstr("xml"); if (!node.isNull()) // if there is already xml instruction, then put that one below it this->insertAfter(domProcInstr,node); else // otherwise put it always on the top this->insertBefore(domProcInstr,this->firstChild()); updateNodeCreated(domProcInstr); } setModified(); } void KXEDocument::removeSpecProcInstr(const TQString &target) { TQDomNode domNode = getSpecProcInstr(target); if (!domNode.isNull()) { updateNodeDeleted(domNode); ((TQDomDocument*)this)->removeChild(domNode); setModified(); } } TQDomNode KXEDocument::getSpecProcInstr(const TQString& target) { TQDomNode result; TQDomNodeList domNodeList = this->childNodes(); for (uint i=0;inewfile()->newFileCreaBehav() ) { case KXENewFileSettings::CreateEmptyFile: break; // nothing to do in this case case KXENewFileSettings::CreateWithAssistance: { KXEFileNewDialog dlg( 0L); dlg.fillDialog( KXMLEditorFactory::configuration()->newfile()->dfltVersion(), KXMLEditorFactory::configuration()->newfile()->dfltEncoding() ); if( dlg.exec() ) { // if the dialog has been accepted (OK pressed) setSpecProcInstr( "xml", dlg.getData() ); // if the dialog shouldn't be shown anymore, the settings have to be changed if ( dlg.m_pDontShowAgain->isChecked() ) KXMLEditorFactory::configuration()->newfile()->setNewFileCreaBehav( KXENewFileSettings::UseDefaults, instance()->config() ); } break; } case KXENewFileSettings::UseDefaults: setSpecProcInstr( "xml", TQString( "version='%1' encoding='%2'" ).arg(KXMLEditorFactory::configuration()->newfile()->dfltVersion()).arg(KXMLEditorFactory::configuration()->newfile()->dfltEncoding()) ); break; } emit sigOpened(); setModified(); } //------------- SLOTS, called from Part -------------------------------- KCommand * KXEDocument::actDetachStylesheet() { TQDomNode domNode = getSpecProcInstr("xml-stylesheet"); if (!domNode.isNull()) { KCommand *pCmd = new KXEStylesheetDetachCommand(this,domNode.toProcessingInstruction().data()); return pCmd; } return 0L; } KCommand * KXEDocument::actAttachStylesheet() { KXEAttachDialogBase dlg; dlg.Label->setText(i18n("Stylesheet URL:")); if (dlg.exec()) { TQDomNode domNode = getSpecProcInstr("xml-stylesheet"); TQString data = ""; if (!domNode.isNull()) data = domNode.toProcessingInstruction().data(); KCommand *pCmd = new KXEStylesheetAttachCommand(this,data,dlg.attachURI->url()); return pCmd; } return 0L; } KCommand * KXEDocument::actDetachSchema() { if (!documentElement().isNull()) // just for sure... { KCommand *pCmd = new KXESchemaDetachCommand(this, documentElement().attributeNS(SCHEMA_NAMESPACE, SCHEMA_ATTRIBUTE,"") ); return pCmd; } return 0L; } KCommand * KXEDocument::actAttachSchema() { KXEAttachDialogBase dlg; dlg.Label->setText(i18n("Schema URL:")); if (dlg.exec()) { if (!documentElement().isNull()) // just for sure... { KCommand *pCmd = new KXESchemaAttachCommand(this,dlg.attachURI->url(), documentElement().attributeNS(SCHEMA_NAMESPACE,SCHEMA_ATTRIBUTE,"")); return pCmd; } } return 0L; } // Instert or edit special processing instruction KCommand * KXEDocument::actVersionEncoding() { TQDomNode node = getSpecProcInstr("xml"); KXESpecProcInstrDialog dlg; if(!node.isNull()) dlg.fillDialog(node.toProcessingInstruction().data()); else dlg.fillDialog( KXMLEditorFactory::configuration()->newfile()->dfltVersion(), KXMLEditorFactory::configuration()->newfile()->dfltEncoding() ); if(dlg.exec()) { TQString strOldData = ""; if (!node.isNull()) strOldData = node.toProcessingInstruction().data(); KCommand *pCmd = new KXEVersionEncodingCommand(this,strOldData,dlg.getData()); return pCmd; } return 0L; } #include "kxedocument.moc"