//
// C++ Implementation: kxedocument
//
// Description:
//
//
// Author: Adam Charytoniuk <achary@poczta.onet.pl>, (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 <tdefile.h>
# include <ktar.h>
# include <kzip.h>
# include <kfilterdev.h>
# include <tdetempfile.h>
# include <kdebug.h>
# include <tdemessagebox.h>
# include <tdelocale.h>
# include <kcommand.h>
# include <tdeaction.h>
# include <kurl.h>
# include <kurlrequester.h>
# include <tqcombobox.h>
# include <tqbuffer.h>
# include <tqregexp.h>
# include <tqtextcodec.h>
# include <tqlabel.h>
# include <tqcheckbox.h>
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 ) = = " <?xml " )
{ int iStart , iEnd ;
if ( ( iStart = strXML . find ( " encoding " , 0 ) ) > 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 ) ;
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 , line . 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 " ) , i18n ( " File: " ) ) ;
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 < const KArchiveFile * > ( 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 ) ;
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 ; i < domNodeList . count ( ) ; i + + )
if ( domNodeList . item ( i ) . isProcessingInstruction ( ) )
{
TQDomProcessingInstruction domProcInstr = domNodeList . item ( i ) . toProcessingInstruction ( ) ;
if ( domProcInstr . target ( ) = = target )
return domNodeList . item ( i ) ;
}
return result ;
}
void KXEDocument : : newFile ( )
{
switch ( KXMLEditorFactory : : configuration ( ) - > newfile ( ) - > 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 <?xml ... ?>
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"