/***************************************************************************
*   Copyright (C) 2004-2009 by Thomas Fischer                             *
*   fischer@unix-ag.uni-kl.de                                             *
*                                                                         *
*   This program is free software; you can redistribute it and/or modify  *
*   it under the terms of the GNU General Public License as published by  *
*   the Free Software Foundation; either version 2 of the License, or     *
*   (at your option) any later version.                                   *
*                                                                         *
*   This program 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 General Public License for more details.                          *
*                                                                         *
*   You should have received a copy of the GNU General Public License     *
*   along with this program; if not, write to the                         *
*   Free Software Foundation, Inc.,                                       *
*   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
***************************************************************************/
#include <cmath>

#include <tqlayout.h>
#include <tqfile.h>
#include <tqdragobject.h>
#include <tqregexp.h>
#include <tqsplitter.h>
#include <tqurl.h>
#include <tqtextedit.h>
#include <tqprogressdialog.h>
#include <tqbuffer.h>
#include <tqtooltip.h>
#include <tqapplication.h>
#include <tqpushbutton.h>
#include <tqfileinfo.h>

#include <kdebug.h>
#include <tdelocale.h>
#include <tdetempfile.h>
#include <tdeglobalsettings.h>
#include <kurl.h>
#include <kinputdialog.h>
#include <tdepopupmenu.h>
#include <kiconloader.h>
#include <tdeio/netaccess.h>
#include <kstandarddirs.h>
#include <tdemessagebox.h>
#include <tdeapplication.h>
#include <klineedit.h>
#include <kdebug.h>
#include <ktextedit.h>

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "searchbar.h"
#include "sidebar.h"
#include "documentlistview.h"
#include "documentlistviewitem.h"
#include "documentsourceview.h"
#include "macrowidget.h"
#include "preamblewidget.h"
#include "commentwidget.h"
#include "entrywidget.h"
#include "fileimporterbibtex.h"
#include "fileimporterbibutils.h"
#include "fileimporterris.h"
#include "fileexporterbibtex.h"
#include "fileexporterbibutils.h"
#include "fileexporterris.h"
#include "fileexporterxml.h"
#include "fileexporterrtf.h"
#include "fileexporterdocbook5.h"
#include "fileexporterpdf.h"
#include "fileexporterps.h"
#include "fileexporterxslt.h"
#include "fileimporterexternal.h"
#include "fileexporterexternal.h"
#include "xsltransform.h"
#include "entry.h"
#include "entryfield.h"
#include "comment.h"
#include "macro.h"
#include "preamble.h"
#include "settings.h"
#include "webquery.h"
#include "mergeelements.h"
#include "idsuggestions.h"

#include "documentwidget.h"

namespace KBibTeX
{

    DocumentWidget::DocumentWidget( bool isReadOnly, TQWidget *parent, const char *name )
            : TQTabWidget( parent, name ), m_lineEditNewKeyword( NULL ), m_isReadOnly( isReadOnly ), m_filename( TQString::null ), m_progressDialog( NULL ), m_newElementCounter( 1 ), m_editMode( emList ), m_viewDocumentActionMenu( NULL ), m_assignKeywordsActionMenu( NULL ), m_searchWebsitesActionMenu( NULL ), m_actionEditCut( NULL ), m_actionEditCopy( NULL ), m_actionEditCopyRef( NULL ), m_actionEditPaste( NULL ), m_actionEditSelectAll( NULL ), m_actionEditFind( NULL ), m_actionEditFindNext( NULL ), m_dirWatch( this )
    {
        m_bibtexfile = new BibTeX::File();

        setupGUI();

        m_listViewElements->setBibTeXFile( m_bibtexfile );
        m_sourceView->setBibTeXFile( m_bibtexfile );
    }

    DocumentWidget::~DocumentWidget()
    {
        delete m_bibtexfile;
    }

    void DocumentWidget::setupGUI()
    {
        setAcceptDrops( true );
        setFocusPolicy( TQWidget::ClickFocus );

        // List view tab widget ===============================

        m_container = new TQWidget( this );
        TQVBoxLayout *layout = new TQVBoxLayout( m_container, 0, 0 );
        addTab( m_container, i18n( "L&ist view" ) );

        m_searchBar = new SearchBar( m_container );
        layout->addWidget( m_searchBar );

        m_horSplitter = new TQSplitter( TQt::Horizontal, m_container );
        layout->addWidget( m_horSplitter );
        m_horSplitter->setSizePolicy( TQSizePolicy::MinimumExpanding, TQSizePolicy::MinimumExpanding );

        m_sideBar = new SideBar( m_isReadOnly, m_horSplitter );
        m_vertSplitter = new TQSplitter( TQt::Vertical, m_horSplitter );
        m_vertSplitter->setSizePolicy( TQSizePolicy::MinimumExpanding, TQSizePolicy::MinimumExpanding );

        m_listViewElements = new DocumentListView( this, m_isReadOnly, m_vertSplitter );

        m_preview = new KTextEdit( m_vertSplitter );
        m_preview->setReadOnly( true );
        m_preview->setAlignment( TQt::AlignTop && TQt::AlignLeft );

        // Source view tab widget =============================

        m_sourceView = new DocumentSourceView( this, m_isReadOnly, this , "source_view" );
        addTab( m_sourceView, i18n( "So&urce view" ) );
        m_sourceView->setFont( TDEGlobalSettings::fixedFont() );

        // Setup actions
        connect( m_searchBar, TQ_SIGNAL( onlineSearch() ), this, TQ_SLOT( onlineSearch() ) );
        connect( m_searchBar, TQ_SIGNAL( doSearch( const TQString&, BibTeX::Element::FilterType, BibTeX::EntryField::FieldType ) ), m_listViewElements, TQ_SLOT( filter( const TQString&, BibTeX::Element::FilterType, BibTeX::EntryField::FieldType ) ) );
        connect( m_listViewElements, TQ_SIGNAL( executed( DocumentListViewItem* ) ), this, TQ_SLOT( executeElement( DocumentListViewItem* ) ) );
        connect( m_listViewElements, TQ_SIGNAL( selectionChanged() ), this, TQ_SLOT( slotSelectionChanged() ) );
        connect( m_listViewElements, TQ_SIGNAL( currentChanged( TQListViewItem* ) ), this, TQ_SLOT( slotPreviewElement( TQListViewItem* ) ) );
        connect( m_listViewElements, TQ_SIGNAL( clicked( TQListViewItem* ) ), this, TQ_SLOT( slotPreviewElement( TQListViewItem* ) ) );
        connect( this, TQ_SIGNAL( currentChanged( TQWidget * ) ), this, TQ_SLOT( slotTabChanged( TQWidget* ) ) );
        connect( m_sourceView, TQ_SIGNAL( modified() ), this, TQ_SLOT( slotModified() ) );
        connect( m_listViewElements, TQ_SIGNAL( modified() ), this, TQ_SLOT( slotModified() ) );
        connect( m_sideBar, TQ_SIGNAL( valueRenamed() ), this, TQ_SLOT( slotModified() ) );
        connect( m_sideBar, TQ_SIGNAL( valueRenamed() ), this, TQ_SLOT( refreshBibTeXFile() ) );
        connect( m_sideBar, TQ_SIGNAL( selected( const TQString&, BibTeX::Element::FilterType, BibTeX::EntryField::FieldType ) ), m_searchBar, TQ_SLOT( setSearch( const TQString&, BibTeX::Element::FilterType, BibTeX::EntryField::FieldType ) ) );
        connect( &m_dirWatch, TQ_SIGNAL( dirty( const TQString& ) ), this, TQ_SLOT( slotFileGotDirty( const TQString & ) ) );
    }


    bool DocumentWidget::open( const TQString &fileName, bool mergeOnly )
    {
        bool result = false;
        if ( !mergeOnly )
            m_dirWatch.removeFile( m_filename );

        BibTeX::FileImporter *importer = fileImporterFactory( fileName );
        if ( importer != NULL )
        {
            TQFile file( fileName );
            if ( file.open( IO_ReadOnly ) )
            {
                result = open( &file, mergeOnly, TQString( i18n( "<qt>Loading file <b>%1</b></qt>" ) ).arg( fileName ), importer );
                if ( result )
                {
                    m_bibtexfile->fileName = fileName;
                    m_filename = fileName;
                }
                file.close();
            }
            else
                kdDebug() << "Cannot open file " << fileName << endl;

            delete importer;
        }


        if ( !mergeOnly )
            m_dirWatch.addFile( m_filename );

        return result;
    }

    bool DocumentWidget::open( TQIODevice *iodevice, bool mergeOnly, const TQString &label, BibTeX::FileImporter *importer )
    {
        bool result = false;
        setEnabled( false );

        bool usingDefaultImporter = importer == NULL;
        if ( usingDefaultImporter )
        {
            Settings * settings = Settings::self( NULL );
            importer = new BibTeX::FileImporterBibTeX( settings->editing_FirstNameFirst, settings->fileIO_Encoding );
        }

        startProgress( label, importer );
        BibTeX::File *newFile = importer->load( iodevice );
        endProgress( importer );

        if ( usingDefaultImporter )
            delete importer;

        if ( newFile != NULL )
        {
            if ( mergeOnly )
            {
                for ( TQValueList<BibTeX::Element*>::ConstIterator eit = newFile->constBegin(); eit != newFile->constEnd(); ++eit )
                    m_bibtexfile->appendElement(( *eit )->clone() );

                if ( KMessageBox::questionYesNo( this, i18n( "Do you want to search for duplicates in the merged document?" ), i18n( "Find duplicates?" ), KGuiItem( i18n( "Find Duplicates" ) ) ) == KMessageBox::Yes )
                {
                    MergeElements *me = new MergeElements( this );
                    me->mergeDuplicates( m_bibtexfile );
                    delete me;
                }
                delete newFile;
            }
            else
            {
                delete m_bibtexfile;
                m_bibtexfile = newFile;
            }

            if ( currentPage() == m_sourceView )
                m_sourceView->setBibTeXFile( m_bibtexfile );
            else if ( currentPage() == m_container )
                m_listViewElements->setBibTeXFile( m_bibtexfile );

            Settings * settings = Settings::self( m_bibtexfile );
            settings->addToCompletion( m_bibtexfile );
            m_sideBar->refreshLists( m_bibtexfile );

            result = true;
        }
        else
        {
            kdDebug() << "Could not load bibliography file from io device" << endl;
            result = false;
        }

        setEnabled( true );
        return result;
    }

    bool DocumentWidget::save( const TQString &fileName, TQStringList *errorLog )
    {
        bool result = false;
        m_dirWatch.removeFile( m_filename );
        Settings * settings = Settings::self( NULL );

        BibTeX::File::FileFormat format = BibTeX::File::formatUndefined;
        if ( fileName.endsWith( ".rtf", false ) )
            format = BibTeX::File::formatRTF;
        else if ( fileName.endsWith( ".pdf", false ) )
            format = BibTeX::File::formatPDF;
        else if ( fileName.endsWith( ".bib", false ) )
            format = BibTeX::File::formatBibTeX;
        else if ( settings->external_xml2bibAvailable && settings->external_end2xmlAvailable && ( fileName.endsWith( ".ref", false ) || fileName.endsWith( ".refer", false ) || fileName.endsWith( ".txt", false ) || fileName.endsWith( ".rfr", false ) ) )
            format = BibTeX::File::formatEndNote;
        else if ( settings->external_xml2bibAvailable && settings->external_end2xmlAvailable && ( fileName.endsWith( ".isi", false ) || fileName.endsWith( ".cgi", false ) ) )
            format = BibTeX::File::formatISI;
        else if ( fileName.endsWith( ".ris", false ) )
            format = BibTeX::File::formatRIS;
        else if ( fileName.endsWith( ".ps", false ) )
            format = BibTeX::File::formatPS;
        else if ( fileName.endsWith( ".xml", false ) )
        {
            TQStringList options = TQStringList::split( '|', ( settings->external_xml2bibAvailable && settings->external_end2xmlAvailable ? "DocBook5|MODS|internal XML" : "DocBook5|internal XML" ) );
            bool ok = false;
            TQString answer = KInputDialog::getItem( i18n( "Choose file format" ), TQString( i18n( "Choose file format of file '%1':" ) ).arg( fileName ), options, 0, false, &ok, this );
            if ( ok && !answer.isNull() && answer == "DocBook5" )
                format = BibTeX::File::formatDocBook5;
            else if ( ok && !answer.isNull() && answer == "MODS" )
                format = BibTeX::File::formatMODS;
            else if ( ok && !answer.isNull() && answer == "internal XML" )
                format = BibTeX::File::formatXML;
        }
        else if ( fileName.endsWith( ".html", false ) || fileName.endsWith( ".xhtml", false ) || fileName.endsWith( ".htm", false ) )
            format = BibTeX::File::formatHTML;

        if ( format != BibTeX::File::formatUndefined )
        {
            TQString usedFileName = fileName;
            TQFileInfo fi( fileName );
            if ( !fi.readLink().isNull() && KMessageBox::questionYesNo( this, TQString( i18n( "The selected filename \"%1\" is a symbolic link pointing to \"%2\".\nReplace the link with a new file or overwrite the existing file the link points to?" ) ).arg( usedFileName ).arg( Settings::resolveLink( fileName, fi.readLink() ) ), i18n( "Symbolic Link" ), KGuiItem( i18n( "Replace link" ) ), KGuiItem( i18n( "Overwrite file the link points to" ) ) ) == KMessageBox::No )
                usedFileName = Settings::resolveLink( fileName, fi.readLink() );
            TQFile file( usedFileName );
            if ( file.open( IO_WriteOnly ) )
            {
                result = save( &file, format, TQString( i18n( "<qt>Writing file <b>%1</b></qt>" ) ).arg( fileName ), errorLog );
                if ( result )
                {
                    m_bibtexfile->fileName = fileName;
                    m_filename = fileName;
                }
                file.close();
            }
            else
                kdDebug() << "Cannot write to file " << fileName << endl;
        }
        else
            kdDebug() << "Unknown file format to save to (filename is " << fileName << ")" << endl;

        m_dirWatch.addFile( m_filename );
        return result;
    }

    bool DocumentWidget::save( TQIODevice *iodevice, BibTeX::File::FileFormat format, const TQString &label, TQStringList *errorLog )
    {
        Settings * settings = Settings::self( m_bibtexfile );

        bool result = false;
        setEnabled( false );

        updateFromGUI();

        BibTeX::XSLTransform *transform = NULL;

        BibTeX::FileExporter * exporter = NULL;
        switch ( format )
        {
        case BibTeX::File::formatBibTeX:
        {
            BibTeX::FileExporterBibTeX * bibtexExporter = new BibTeX::FileExporterBibTeX();
            bibtexExporter->setStringDelimiter( settings->fileIO_BibtexStringOpenDelimiter, settings->fileIO_BibtexStringCloseDelimiter );
            bibtexExporter->setKeywordCasing( settings->fileIO_KeywordCasing );
            bibtexExporter->setEncoding( settings->fileIO_Encoding );
            bibtexExporter->setEnclosingCurlyBrackets( settings->fileIO_EnclosingCurlyBrackets );
            exporter = bibtexExporter;
        }
        break;
        case BibTeX::File::formatRIS:
            if ( settings->external_xml2bibAvailable && settings->external_end2xmlAvailable && settings->fileIO_useBibUtils )
                exporter = new BibTeX::FileExporterBibUtils( BibTeX::File::formatRIS );
            else
                exporter = new BibTeX::FileExporterRIS( );
            break;
        case BibTeX::File::formatEndNote:
        case BibTeX::File::formatEndNoteXML:
        case BibTeX::File::formatMODS:
        case BibTeX::File::formatISI:
            if ( settings->external_xml2bibAvailable && settings->external_end2xmlAvailable )
                exporter = new BibTeX::FileExporterBibUtils( format );
            break;
        case BibTeX::File::formatXML:
            exporter = new BibTeX::FileExporterXML();
            break;
        case BibTeX::File::formatHTML:
            switch ( settings->fileIO_ExporterHTML )
            {
            case BibTeX::FileExporterExternal::exporterNone:
            case BibTeX::FileExporterExternal::exporterXSLT:
            {
                TDEStandardDirs * kstd = TDEGlobal::dirs();
                TQString resPath = kstd->findResource( "data", "kbibtexpart/xslt/html.xsl" );
                if ( resPath != NULL )
                    transform = new BibTeX::XSLTransform( resPath );

                if ( transform != NULL )
                    exporter = new BibTeX::FileExporterXSLT( transform );
                else
                {
                    kdDebug() << "XSLT for HTML export is not available" << endl;
                }
            }
            break;
            default:
            {
                exporter = new BibTeX::FileExporterExternal( settings->fileIO_ExporterHTML, BibTeX::File::formatHTML );
            }
            }
            break;
        case BibTeX::File::formatRTF:
        {
            if ( !settings->external_latex2rtfAvailable )
            {
                TQString msg = i18n( "To export a BibTeX document to the Rich Text Format (RTF) KBibTeX requires the program 'latex2rtf'." ) ;
                KMessageBox::information( this, msg );
                errorLog->append( msg );
            }
            else
            {
                BibTeX::FileExporterRTF *rtfExporter = new BibTeX::FileExporterRTF();
                rtfExporter->setLaTeXLanguage( settings->fileIO_ExportLanguage );
                rtfExporter->setLaTeXBibliographyStyle( settings->fileIO_ExportBibliographyStyle );
                exporter = rtfExporter;
            }
        }
        break;
        case BibTeX::File::formatDocBook5:
        {
            if ( settings->fileIO_bib2db5ClassPath == TQString::null )
            {
                TQString msg = i18n( "To export a BibTeX document to the DocBook5 format KBibTeX requires the program 'bib2db5'." ) ;
                KMessageBox::information( this, msg );
                errorLog->append( msg );
            }
            else
            {
                BibTeX::FileExporterDocBook5 *db5Exporter = new BibTeX::FileExporterDocBook5( settings->fileIO_bib2db5ClassPath );
                exporter = db5Exporter;
            }
        }
        break;
        case BibTeX::File::formatPDF:
        {
            if ( settings->fileIO_EmbedFiles && !Settings::kpsewhich( "embedfile.sty" ) )
            {
                KMessageBox::sorry( this, i18n( "Embedding files into the PDF file is enabled, but the required file 'embedfile.sty' was not found. Embedding files will be disabled." ), i18n( "Embedding files disabled" ) );
                settings->fileIO_EmbedFiles = false;
            }

            BibTeX::FileExporterPDF *pdfExporter = new BibTeX::FileExporterPDF( settings->fileIO_EmbedFiles );
            pdfExporter->setLaTeXLanguage( settings->fileIO_ExportLanguage );
            pdfExporter->setLaTeXBibliographyStyle( settings->fileIO_ExportBibliographyStyle );
            TQStringList searchPaths;
            for ( TQStringList::Iterator it = settings->editing_DocumentSearchPaths.begin(); it != settings->editing_DocumentSearchPaths.end(); ++it )
                searchPaths.append( *it );
            if ( m_bibtexfile->fileName != TQString::null )
                searchPaths.append( KURL( m_bibtexfile->fileName ).directory( false, false ) );
            pdfExporter->setDocumentSearchPaths( searchPaths );
            exporter = pdfExporter;
        }
        break;
        case BibTeX::File::formatPS:
        {
            BibTeX::FileExporterPS *psExporter = new BibTeX::FileExporterPS();
            psExporter->setLaTeXLanguage( settings->fileIO_ExportLanguage );
            psExporter->setLaTeXBibliographyStyle( settings->fileIO_ExportBibliographyStyle );
            exporter = psExporter;
        }
        break;
        default:
            kdDebug() << "Unsupported export format selected" << endl;
        }

        if ( exporter != NULL )
        {
            startProgress( label, exporter );
            result = exporter->save( iodevice, m_bibtexfile, errorLog );
            endProgress( exporter );

            if ( transform != NULL )
                delete transform;
            delete exporter;
        }

        setEnabled( true );
        return result;
    }

    bool DocumentWidget::newElement( const TQString& elementType )
    {
        Settings * settings = Settings::self( m_bibtexfile );

        if ( m_editMode == emList )
        {
            if ( elementType.lower() == "macro" )
            {
                TQString name = TQString( i18n( "May only contain ASCII characters, in case of doubt keep English form", "NewMacro%1" ) ).arg( m_newElementCounter++ );
                BibTeX::Macro *macro = new BibTeX::Macro( name );
                if ( MacroWidget::execute( macro, m_isReadOnly ) == TQDialog::Accepted )
                {
                    new DocumentListViewItem( m_bibtexfile, macro, m_listViewElements );
                    m_bibtexfile->appendElement( macro );
                    settings->addToCompletion( macro );
                    m_sideBar->refreshLists( m_bibtexfile );
                    return true;
                }
                else
                {
                    delete macro;
                    return false;
                }
            }
            else if ( elementType.lower() == "comment" )
            {
                BibTeX::Comment * comment = new BibTeX::Comment( i18n( "Put your comment here..." ) );
                if ( CommentWidget::execute( comment, m_isReadOnly ) == TQDialog::Accepted )
                {
                    new DocumentListViewItem( m_bibtexfile, comment, m_listViewElements );
                    m_bibtexfile->appendElement( comment );
                    return true;
                }
                else
                {
                    delete comment;
                    return false;
                }
            }
            else if ( elementType.lower() == "preamble" )
            {
                BibTeX::Preamble * preamble = new BibTeX::Preamble( );
                if ( PreambleWidget::execute( preamble, m_isReadOnly ) == TQDialog::Accepted )
                {
                    new DocumentListViewItem( m_bibtexfile, preamble, m_listViewElements );
                    m_bibtexfile->appendElement( preamble );
                    return true;
                }
                else
                {
                    delete preamble;
                    return false;
                }
            }
            else
            {
                TQString name = nextNewEntry();
                BibTeX::Entry *entry = new BibTeX::Entry( elementType, name );
                m_dirWatch.stopScan();
                if ( EntryWidget::execute( entry, m_bibtexfile, m_isReadOnly, true ) == TQDialog::Accepted )
                {
                    new DocumentListViewItem( m_bibtexfile, entry, m_listViewElements );
                    m_bibtexfile->appendElement( entry );
                    settings->addToCompletion( entry );
                    m_sideBar->refreshLists( m_bibtexfile );
                    m_dirWatch.startScan();
                    return true;
                }
                else
                {
                    delete entry;
                    m_dirWatch.startScan();
                    return false;
                }
            }
        }
        else if ( m_editMode == emSource )
        {
            if ( elementType.lower() == "macro" )
            {
                TQString name = TQString( i18n( "May only contain ASCII characters, in case of doubt keep English form", "NewString%1" ) ).arg( m_newElementCounter++ );
                m_sourceView->insertLines( TQString( "@string{ %1 = \"%2\" }" ).arg( name ).arg( i18n( "No text yet" ) ) );
            }
            else if ( elementType.lower() == "comment" )
            {
                m_sourceView->insertLines( i18n( "@comment{ Put your comment here... }" ) );
            }
            else if ( elementType.lower() == "preamble" )
            {
                m_sourceView->insertLines( i18n( "@preamble{\"Put your preamble here using double quotes...\"}" ), 0 );
            }
            else
            {
                TQString name = nextNewEntry();
                BibTeX::Entry *entry = new BibTeX::Entry( elementType, name );

                for ( int t = 0; t < 2; t++ )
                    for ( int i = ( int ) BibTeX::EntryField::ftAbstract; i <= ( int ) BibTeX::EntryField::ftYear; i++ )
                    {
                        BibTeX::EntryField::FieldType fieldType = ( BibTeX::EntryField::FieldType ) i;
                        BibTeX::Entry::FieldRequireStatus fieldRequireStatus = BibTeX::Entry::getRequireStatus( entry->entryType(), fieldType );

                        if (( t == 0 && fieldRequireStatus == BibTeX::Entry::frsRequired ) || ( t == 1 && fieldRequireStatus == BibTeX::Entry::frsOptional ) )
                        {
                            BibTeX::Value * value = new BibTeX::Value();
                            value->items.append( new BibTeX::PlainText( fieldRequireStatus == BibTeX::Entry::frsRequired ? i18n( "REQUIRED" ) : i18n( "optional" ) ) );
                            BibTeX::EntryField *field = new BibTeX::EntryField( fieldType );
                            field->setValue( value );
                            entry->addField( field );
                        }
                    }

                BibTeX::FileExporter * exporter = new BibTeX::FileExporterBibTeX( );
                TQBuffer buffer;
                buffer.open( IO_WriteOnly );
                bool result = exporter->save( &buffer, entry );
                buffer.close();

                if ( result )
                {
                    buffer.open( IO_ReadOnly );
                    TQTextStream textStream( &buffer );
                    textStream.setEncoding( TQTextStream::UnicodeUTF8 );
                    TQString text = textStream.read();
                    buffer.close();
                    TQStringList lines = TQStringList::split( '\n', text );
                    for ( TQStringList::Iterator it = lines.begin(); it != lines.end(); ++it )
                        m_sourceView->insertLines( *it );
                }

                delete exporter;
            }

            return true;
        }

        return false;
    }

    void DocumentWidget::updateViews()
    {
        m_listViewElements->updateVisiblity();
    }

    void DocumentWidget::showStatistics()
    {
        int n = m_bibtexfile->count();
        KMessageBox::information( this, i18n( "This BibTeX file contains 1 element.", "This BibTeX file contains %n elements.", n ), i18n( "File Statistics" ) );
    }

    void DocumentWidget::refreshBibTeXFile()
    {
        if ( currentPage() == m_sourceView )
            m_sourceView->setBibTeXFile( m_bibtexfile );
        else if ( currentPage() == m_container )
            m_listViewElements->setBibTeXFile( m_bibtexfile );
    }

    void DocumentWidget::setFactory( KXMLGUIFactory *factory, KXMLGUIClient *client )
    {
        m_searchBar->setFactory( factory, client );
        m_listViewElements->setFactory( factory, client );
        m_sourceView->setFactory( factory, client );

        m_viewDocumentActionMenu = dynamic_cast<TDEActionMenu*>( client->action( "view_document" ) );
        if ( m_viewDocumentActionMenu != NULL )
            connect( m_viewDocumentActionMenu->popupMenu(), TQ_SIGNAL( activated( int ) ), this, TQ_SLOT( slotViewDocument( int ) ) );
        m_assignKeywordsActionMenu = dynamic_cast<TDEActionMenu*>( client->action( "assign_keywords" ) );
        if ( m_assignKeywordsActionMenu != NULL )
            connect( m_assignKeywordsActionMenu->popupMenu(), TQ_SIGNAL( activated( int ) ), this, TQ_SLOT( slotAssignKeywords( int ) ) );

        m_actionEditCut = client->action( "edit_cut" );
        m_actionEditCopy = client->action( "edit_copy" );
        m_actionEditCopyRef = client->action( "edit_copyref" );
        m_actionEditPaste = client->action( "edit_paste" );
        m_actionEditSelectAll = client->action( "edit_select_all" );
        m_actionEditFind = client->action( "edit_find" );
        m_actionEditFindNext = client->action( "edit_find_next" );
        m_listViewElements->setViewShowColumnsMenu( dynamic_cast<TDEActionMenu*>( client->action( "view_showcolumns" ) ) );
        m_searchWebsitesActionMenu = dynamic_cast<TDEActionMenu*>( client->action( "search_document_online" ) );
    }

    void DocumentWidget::updateViewDocumentMenu( )
    {
        if ( m_viewDocumentActionMenu == NULL )
        {
            kdDebug() << "FIXME: m_viewDocumentActionMenu is not set" << endl;
            return;
        }

        TDEPopupMenu * popup = m_viewDocumentActionMenu->popupMenu();
        popup->clear();
        m_viewDocumentActionMenuURLs.clear();

        BibTeX::Element * currentElement = NULL;

        TQListViewItem * item = m_listViewElements->selectedItem();
        if ( item == NULL )
            item = m_listViewElements->currentItem();
        DocumentListViewItem * dlvi = dynamic_cast<DocumentListViewItem*>( item );
        if ( dlvi )
            currentElement = dlvi->element();

        BibTeX::Entry *entry = NULL;
        if ( currentElement != NULL )
            entry = dynamic_cast<BibTeX::Entry*>( currentElement );

        m_viewDocumentActionMenu->setEnabled( false );
        if ( entry != NULL )
        {
            KURL::List documentURLs = getEntryURLs( entry );
            if ( !documentURLs.isEmpty() )
            {
                for ( KURL::List::Iterator i = documentURLs.begin(); i != documentURLs.end(); ++i )
                {
                    TQString prettyURL = ( *i ).prettyURL();
                    if ( prettyURL.endsWith( ".pdf", false ) || prettyURL.find( "/pdf/" ) > 0 )
                        popup->insertItem( SmallIcon( "application-pdf" ), prettyURL );
                    else if ( prettyURL.endsWith( ".ps", false ) )
                        popup->insertItem( SmallIcon( "application-postscript" ), prettyURL );
                    else if ( prettyURL.endsWith( ".html", false ) || prettyURL.startsWith( "http://", false ) )
                        popup->insertItem( SmallIcon( "text-html" ), prettyURL );
                    else
                        popup->insertItem( prettyURL );
                    m_viewDocumentActionMenuURLs.append( prettyURL );
                }
                m_viewDocumentActionMenu->setEnabled( true );
            }
        }
    }

    void DocumentWidget::updateAssignKeywords()
    {
        if ( m_assignKeywordsActionMenu == NULL )
        {
            kdDebug() << "FIXME: m_assignKeywordsActionMenu is not set" << endl;
            return;
        }

        TDEPopupMenu * popup = m_assignKeywordsActionMenu->popupMenu();
        popup->clear();
        m_assignKeywordsActionMenuURLs.clear();
        TQStringList entryKeywords;
        TQStringList fileKeywords;

        /**
        * Fetch keywords from selected entries into entryKeywords list
        */
        TQValueList<BibTeX::Entry*> entryList;
        for ( TQListViewItemIterator it( m_listViewElements, TQListViewItemIterator::Selected ); it.current(); ++it )
        {
            BibTeX::Element * currentElement = NULL;
            DocumentListViewItem * dlvi = dynamic_cast<DocumentListViewItem*>( it.current() );
            if ( dlvi && (( currentElement = dlvi->element() ) != NULL ) )
            {
                BibTeX::Entry *entry = dynamic_cast<BibTeX::Entry*>( currentElement );
                if ( entry != NULL )
                    entryList << entry;
            }
        }

        for ( TQValueList<BibTeX::Entry*>::Iterator it = entryList.begin(); it != entryList.end(); ++it )
        {
            BibTeX::Entry *entry = *it;
            BibTeX::EntryField *field = NULL;
            BibTeX::Value *valueKeywords = NULL;
            if (( field = entry->getField( BibTeX::EntryField::ftKeywords ) ) != NULL && ( valueKeywords = field->value() ) != NULL )
                for ( TQValueList<BibTeX::ValueItem*>::ConstIterator it = valueKeywords->items.begin();it != valueKeywords->items.end();++it )
                {
                    BibTeX::KeywordContainer *container = dynamic_cast<BibTeX::KeywordContainer*>( *it );
                    if ( container != NULL )
                        for ( TQValueList<BibTeX::Keyword*>::ConstIterator kit = container->keywords.begin();kit != container->keywords.end();++kit )
                            entryKeywords.append(( *kit )->text() );
                }
        }

        /**
        * Fetch all keywords from current file into fileKeywords
        */
        for ( TQValueList<BibTeX::Element*>::ConstIterator eit = m_bibtexfile->constBegin(); eit != m_bibtexfile->constEnd(); ++eit )
        {
            BibTeX::Entry *entry = dynamic_cast<BibTeX::Entry*>( *eit );
            BibTeX::EntryField *field = NULL;
            BibTeX::Value *value = NULL;
            if ( entry != NULL && (( field = entry->getField( BibTeX::EntryField::ftKeywords ) ) != NULL ) && (( value = field->value() ) != NULL ) )
            {
                for ( TQValueList<BibTeX::ValueItem*>::ConstIterator vit = value->items.constBegin(); vit != value->items.constEnd();++vit )
                {
                    BibTeX::KeywordContainer *container = dynamic_cast<BibTeX::KeywordContainer *>( *vit );
                    for ( TQValueList<BibTeX::Keyword*>::ConstIterator kit = container->keywords.constBegin(); kit != container->keywords.constEnd();++kit )
                    {
                        TQString text = ( *kit )->text();
                        if ( !fileKeywords.contains( text ) )
                            fileKeywords.append( text );
                    }
                }
            }
        }

        /**
        * Merge keyword lists
        */
        Settings * settings = Settings::self( m_bibtexfile );
        TQStringList allKeywords = TQStringList( fileKeywords );
        for ( TQStringList::Iterator it =  settings->keyword_GlobalList.begin(); it !=  settings->keyword_GlobalList.end(); ++it )
            if ( !allKeywords.contains( *it ) )
                allKeywords.append( *it );
        allKeywords.sort();

        /**
        * Build menu
        */
        bool popupEmpty = false;
        if ( allKeywords.isEmpty() )
            popupEmpty = true;
        else if ( allKeywords.count() < 24 )
        {
            for ( TQStringList::Iterator it = allKeywords.begin(); it != allKeywords.end(); ++it )
            {
                int i = popup->insertItem( *it );
                popup->setItemChecked( i, entryKeywords.contains( *it ) );
                m_assignKeywordsActionMenuURLs[i] = *it;
            }
        }
        else
        {
            int maxCountPerSubMenu = ( int )sqrt( allKeywords.count() ) + 1;
            int countPerSubMenu = 0;
            TDEPopupMenu *subMenu = new TDEPopupMenu( popup );
            connect( subMenu, TQ_SIGNAL( activated( int ) ), this, TQ_SLOT( slotAssignKeywords( int ) ) );
            TQString startWord, endWord;
            for ( TQStringList::Iterator it = allKeywords.begin(); it != allKeywords.end(); ++it )
            {
                if ( countPerSubMenu == 0 )
                    startWord = *it;
                endWord = *it;
                int i = subMenu->insertItem( *it );
                subMenu->setItemChecked( i, entryKeywords.contains( *it ) );
                m_assignKeywordsActionMenuURLs[i] = *it;

                ++countPerSubMenu;
                if ( countPerSubMenu >= maxCountPerSubMenu )
                {
                    popup->insertItem( TQString( i18n( "%1 ... %2" ) ).arg( startWord ).arg( endWord ), subMenu );
                    subMenu = new TDEPopupMenu( popup );
                    connect( subMenu, TQ_SIGNAL( activated( int ) ), this, TQ_SLOT( slotAssignKeywords( int ) ) );
                    countPerSubMenu = 0;
                }
            }

            if ( countPerSubMenu > 0 )
            {
                popup->insertItem( TQString( i18n( "%1 ... %2" ) ).arg( startWord ).arg( endWord ), subMenu );
            }
            else
                delete subMenu;
        }

        if ( !popupEmpty )
            popup->insertSeparator();

        TQWidget *container = new TQWidget( popup );
        container->setBackgroundColor( TDEGlobalSettings::baseColor() );
        TQHBoxLayout *layout = new TQHBoxLayout( container, 1, 1 );
        TQLabel *label = new TQLabel( i18n( "New keyword:" ), container );
        label->setBackgroundColor( TDEGlobalSettings::baseColor() );
        layout->addWidget( label );
        m_lineEditNewKeyword = new KLineEdit( container );
        layout->addWidget( m_lineEditNewKeyword );
        container->setFocusProxy( m_lineEditNewKeyword );
        container->setFocusPolicy( TQWidget::ClickFocus );
        popup->insertItem( container );

        connect( m_lineEditNewKeyword, TQ_SIGNAL( returnPressed() ), this, TQ_SLOT( slotAddKeyword() ) );
    }

    void DocumentWidget::deferredInitialization()
    {
        restoreState();
        m_listViewElements->deferredInitialization();
    }

    void DocumentWidget::saveState()
    {
        Settings * settings = Settings::self( m_bibtexfile );
        settings->editing_HorSplitterSizes = m_horSplitter->sizes();
        settings->editing_VertSplitterSizes = m_vertSplitter->sizes();
    }

    void DocumentWidget::restoreState()
    {
        m_listViewElements->restoreState();
        m_searchBar->restoreState();
        m_sideBar->restoreState();

        Settings * settings = Settings::self( m_bibtexfile );
        m_horSplitter->setSizes( settings->editing_HorSplitterSizes );
        m_vertSplitter->setSizes( settings->editing_VertSplitterSizes );

        if ( m_searchWebsitesActionMenu != NULL )
        {
            TDEPopupMenu * popup = m_searchWebsitesActionMenu->popupMenu();
            popup->clear();
            int i = 0;
            for ( TQValueList<Settings::SearchURL*>::ConstIterator it = settings->searchURLs.begin(); it != settings->searchURLs.end(); ++it )
                popup->insertItem(( *it ) ->description, ++i );
        }

        if ( settings->editing_UseSpecialFont )
            m_preview->setFont( settings->editing_SpecialFont );
        else
            m_preview->setFont( TDEGlobalSettings::generalFont() );

    }

    void DocumentWidget::executeElement( DocumentListViewItem* item )
    {
        Settings * settings = Settings::self( m_bibtexfile );
        bool openingDocumentOK = false;

        if ( settings->editing_MainListDoubleClickAction == 1 )
        {
            BibTeX::Entry *entry = dynamic_cast<BibTeX::Entry*>( item->element() );
            if ( entry != NULL )
            {
                /** prefer local urls over remote urls, so first check for file:// and use other protocols only if no file:// was found */
                TQString protocol = "file";
                KURL::List urls = getEntryURLs( entry );
                KURL selected = KURL();
                while ( !selected.isValid() && protocol != TQString::null )
                {
                    for ( KURL::List::Iterator it = urls.begin(); !selected.isValid() && it != urls.end(); ++it )
                    {
                        KURL url = *it;
                        if ( url.isValid() && ( !url.isLocalFile() || TQFile::exists( url.path() ) ) && url.protocol().startsWith( protocol ) )
                            selected = url;
                    }
                    /** little trick setting protocol prefix in the first round to "file://", then to "", and setting to null to quit the loop */
                    if ( !protocol.isEmpty() ) protocol = "";
                    else protocol = TQString::null;
                }

                if ( selected.isValid() )
                    openingDocumentOK = Settings::openUrl( selected, this );
            }
        }

        if ( !openingDocumentOK )
            editElement( item );
    }

    bool DocumentWidget::editElement()
    {
        bool result = false;
        TQListViewItem * item = m_listViewElements->selectedItem();
        if ( item == NULL )
            item = m_listViewElements->currentItem();
        DocumentListViewItem * dlvi = dynamic_cast<DocumentListViewItem*>( item );
        if ( dlvi )
            result =  editElement( dlvi );
        return result;
    }

    bool DocumentWidget::editElement( DocumentListViewItem*item )
    {
        BibTeX::Element * element = item->element();
        bool result = editElement( element );
        if ( result )
        {
            item->updateItem();
            slotPreviewElement( item );
        }
        return result;
    }

    bool DocumentWidget::editElement( BibTeX::Element*element )
    {
        m_dirWatch.stopScan();
        bool result = editElementDialog( element );
        m_dirWatch.startScan( );

        Settings * settings = Settings::self( m_bibtexfile );
        settings->addToCompletion( element );
        m_sideBar->refreshLists( m_bibtexfile );

        return result;
    }

    bool DocumentWidget::editElementDialog( BibTeX::Element *element )
    {
        TQDialog::DialogCode dialogResult = TQDialog::Rejected;
        BibTeX::Entry * entry = dynamic_cast<BibTeX::Entry*>( element );
        if ( entry )
            dialogResult = KBibTeX::EntryWidget::execute( entry, m_bibtexfile, m_isReadOnly, false );
        else
        {
            BibTeX::Comment * comment = dynamic_cast<BibTeX::Comment*>( element );
            if ( comment )
                dialogResult = KBibTeX::CommentWidget::execute( comment, m_isReadOnly );
            else
            {
                BibTeX::Macro* macro = dynamic_cast<BibTeX::Macro*>( element );
                if ( macro )
                    dialogResult = KBibTeX::MacroWidget::execute( macro, m_isReadOnly );
                else
                {
                    BibTeX::Preamble* preamble = dynamic_cast<BibTeX::Preamble*>( element );
                    if ( preamble )
                        dialogResult = KBibTeX::PreambleWidget::execute( preamble, m_isReadOnly );
                }
            }
        }

        if ( dialogResult == TQDialog::Accepted )
            slotModified();

        return dialogResult == TQDialog::Accepted;
    }

    void DocumentWidget::deleteElements()
    {
        if ( !m_isReadOnly )
        {
            if ( m_editMode == emList )
            {
                m_listViewElements->deleteSelected();
                slotModified();
            }
        }
    }

    void DocumentWidget::sendSelectedToLyx()
    {
        if ( m_editMode == emList )
        {
            TQValueList<BibTeX::Element*> elements = m_listViewElements->selectedItems();
            TQString genericMsg = i18n( "\n\nEither LyX is not running or has not been correctly configured to send references to." );
            TQString title = i18n( "Error communicating with LyX" );

            Settings * settings = Settings::self( m_bibtexfile );
            TQString lyxPipeFilename = settings->detectLyXInPipe();
            kdDebug() << "sendSelectedToLyx: lyxPipeFilename= " << lyxPipeFilename << endl;

            if ( lyxPipeFilename.isNull() )
                KMessageBox::error( this, i18n( "Cannot determine how to send references to LyX." ).append( genericMsg ), title );
            else
            {
                TQFile pipe( lyxPipeFilename );
                if ( !pipe.exists() )
                    KMessageBox::error( this, i18n( "The inpipe as configured in LyX does not exist." ).append( genericMsg ), title );
                else
                {
                    if ( !pipe.open( IO_WriteOnly ) )
                        KMessageBox::error( this, i18n( "Cannot open the inpipe as configured in LyX." ).append( genericMsg ), title );
                    else
                    {
                        TQStringList refsToSend;
                        for ( TQValueList<BibTeX::Element*>::Iterator it = elements.begin(); it != elements.end(); ++it )
                        {
                            BibTeX::Entry *entry = dynamic_cast<BibTeX::Entry*>( *it );
                            if ( entry != NULL ) refsToSend.append( entry->id() );
                        }

                        TQTextStream * writer = new TQTextStream( &pipe );
                        TQString msg = "LYXCMD:kbibtex:citation-insert:" + refsToSend.join( "," );
                        *writer << msg << endl;
                        delete writer;
                        pipe.close();
                    }
                }
            }
        }
    }

    void DocumentWidget::cutElements()
    {
        if ( !m_isReadOnly )
        {
            if ( m_editMode == emList )
            {
                m_listViewElements->cut();
            }
            else if ( m_editMode == emSource )
            {
                m_sourceView->cut();
            }
            slotModified();
        }
    }

    void DocumentWidget::copyElements()
    {
        if ( m_editMode == emList )
        {
            m_listViewElements->copy();
        }
        else if ( m_editMode == emSource )
        {
            m_sourceView->copy();
        }
    }

    void DocumentWidget::copyRefElements()
    {
        if ( m_editMode == emList )
        {
            m_listViewElements->copyReferences();
        }
        else  if ( m_editMode == emSource )
        {
// TODO: Get selected text, parse text into BibTeX::File and join list of entries' ids
        }
    }

    void DocumentWidget::pasteElements()
    {
        if ( !m_isReadOnly )
        {
            if ( m_editMode == emList )
            {
                if ( m_listViewElements->paste() )
                    slotModified();
            }
            else if ( m_editMode == emSource )
            {
                if ( m_sourceView->paste() )
                    slotModified();
            }
        }
    }

    void DocumentWidget::selectAll()
    {
        if ( m_editMode == emList )
            m_listViewElements->selectAll();
        else if ( m_editMode == emSource )
            m_sourceView->selectAll();
    }

    void DocumentWidget::setReadOnly( bool isReadOnly )
    {
        m_isReadOnly = isReadOnly;
        m_listViewElements->setReadOnly( m_isReadOnly );
        m_sourceView->setReadOnly( m_isReadOnly );
        m_sideBar->setReadOnly( m_isReadOnly );
    }

    void DocumentWidget::configureEditor()
    {
        m_sourceView->configureEditor();
    }

    void DocumentWidget::undoEditor()
    {
        m_sourceView->undo();
    }

    void DocumentWidget::find()
    {
        if ( m_editMode == emSource )
            m_sourceView->find();
    }
    void DocumentWidget::findNext()
    {
        if ( m_editMode == emSource )
            m_sourceView->findNext();
    }

    void DocumentWidget::slotShowProgress( int current, int total )
    {
        if ( m_progressDialog != NULL )
        {
            m_progressDialog->setProgress( current, total );
            tqApp->processEvents();
        }
    }

    void DocumentWidget::startProgress( const TQString & label, TQObject * progressFrom )
    {
        m_progressDialog = new TQProgressDialog( this );
        m_progressDialog->setLabelText( label );
        connect( progressFrom, TQ_SIGNAL( progress( int, int ) ), this, TQ_SLOT( slotShowProgress( int, int ) ) );
        connect( m_progressDialog, TQ_SIGNAL( canceled() ), progressFrom, TQ_SLOT( cancel( ) ) );
        TQApplication::setOverrideCursor( TQt::waitCursor );
    }

    void DocumentWidget::endProgress( TQObject * progressFrom )
    {
        disconnect( progressFrom, TQ_SIGNAL( progress( int, int ) ), this, TQ_SLOT( slotShowProgress( int, int ) ) );
        disconnect( m_progressDialog, TQ_SIGNAL( canceled() ), progressFrom, TQ_SLOT( cancel( ) ) );
        delete m_progressDialog;
        m_progressDialog = NULL;
        TQApplication::restoreOverrideCursor();
    }

    void DocumentWidget::searchWebsites( const TQString& searchURL, bool includeAuthor )
    {
        DocumentListViewItem * item = dynamic_cast<DocumentListViewItem*>( m_listViewElements->selectedItem() );
        if ( item == NULL )
            item = dynamic_cast<DocumentListViewItem*>( m_listViewElements->currentItem() );

        if ( item != NULL )
            searchWebsites( item->element(), searchURL, includeAuthor );
    }

    void DocumentWidget::searchWebsites( BibTeX::Element * element, const TQString& searchURL, bool includeAuthor )
    {
        TQString queryString = TQString::null;

        BibTeX::Entry* entry = dynamic_cast<BibTeX::Entry*>( element );
        if ( entry != NULL )
        {
            BibTeX::EntryField * field = entry->getField( BibTeX::EntryField::ftTitle );
            if ( field && field->value() )
                queryString = field->value() ->text();
            if ( includeAuthor )
            {
                field = entry->getField( BibTeX::EntryField::ftAuthor );
                if ( field && field->value() )
                {
                    BibTeX::PersonContainer *personContainer = dynamic_cast<BibTeX::PersonContainer*>( field->value()->items.first() );
                    if ( personContainer != NULL )
                    {
                        TQValueList<BibTeX::Person*> list = personContainer->persons;
                        for ( TQValueList<BibTeX::Person*>::ConstIterator it = list.begin(); it != list.end(); ++it )
                            queryString = queryString.append( " " ).append(( *it )->lastName() );

                    }
                }
            }
        }
        else
        {
            BibTeX::Comment * comment = dynamic_cast<BibTeX::Comment*>( element );
            if ( comment != NULL )
                queryString = comment->text();
            else
            {
                BibTeX::Macro * macro = dynamic_cast<BibTeX::Macro*>( element );
                if ( macro != NULL && macro->value() )
                    queryString = macro->value() ->text();
                else
                    kdDebug() << "Not yet supported" << endl;
            }
        }

        if ( queryString != TQString::null )
        {
            queryString = queryString.stripWhiteSpace().replace( '$', "" ).replace( "%", "%25" ).replace( "+", "%2B" ).replace( " ", "%20" ).replace( "#", "%23" ).replace( "&", "%26" ).replace( "?", "%3F" ).replace( '{', "" ).replace( '}', "" );
            KURL url( TQString( searchURL ).arg( queryString ) );
            Settings::openUrl( url, this );
        }
    }

    void DocumentWidget::onlineSearch()
    {
        if ( !m_isReadOnly )
        {
            BibTeX::FileExporter * exporter = new BibTeX::FileExporterBibTeX( );
            TQValueList<BibTeX::Entry*> list;
            if ( WebQueryWizard::execute( this, list ) == TQDialog::Accepted )
            {
                Settings * settings = Settings::self( m_bibtexfile );
                for ( TQValueList<BibTeX::Entry*>::Iterator it = list.begin(); it != list.end(); ++it )
                {
                    ( *it )->setId( IdSuggestions::resolveConflict( m_bibtexfile, ( *it )->id() ) );
                    if ( m_editMode == emList )
                        m_listViewElements->insertItem( new BibTeX::Entry( *it ) );
                    else
                    {
                        TQBuffer buffer;
                        buffer.open( IO_WriteOnly );
                        bool result = exporter->save( &buffer, *it );
                        buffer.close();
                        if ( result )
                        {
                            buffer.open( IO_ReadOnly );
                            TQTextStream textStream( &buffer );
                            textStream.setEncoding( TQTextStream::UnicodeUTF8 );
                            TQString text = textStream.read();
                            buffer.close();
                            TQStringList lines = TQStringList::split( '\n', text );
                            for ( TQStringList::Iterator it = lines.begin(); it != lines.end(); ++it )
                                m_sourceView->insertLines( *it );
                            m_sourceView->insertLines( "" );
                        }

                    }
                    settings->addToCompletion( *it );
                }

                slotModified();
            }
            delete exporter;
        }
    }

    void DocumentWidget::findDuplicates()
    {
        MergeElements *me = new MergeElements( this );
        if ( me->mergeDuplicates( m_bibtexfile ) == TQDialog::Accepted )
        {
            refreshBibTeXFile();
            slotModified();
        }
        delete me;
    }

    void DocumentWidget::updateFromGUI()
    {
        BibTeX::File * file = NULL;
        if ( currentPage() == m_sourceView )
            file = m_sourceView->getBibTeXFile();
        else if ( currentPage() == m_container )
            file = m_listViewElements->getBibTeXFile();

        if ( file != NULL && file != m_bibtexfile )
        {
            delete m_bibtexfile;
            m_bibtexfile = file;
        }
    }

    TQString DocumentWidget::nextNewEntry()
    {
        TQString name = TQString( i18n( "May only contain ASCII characters, in case of doubt keep English form", "NewEntry%1" ) ).arg( m_newElementCounter++ );

        while ( m_bibtexfile->containsKey( name ) != NULL )
        {
            ++m_newElementCounter;
            name = TQString( i18n( "May only contain ASCII characters, in case of doubt keep English form", "NewEntry%1" ) ).arg( m_newElementCounter++ );
        }

        return name;
    }

    KURL::List DocumentWidget::getEntryURLs( BibTeX::Entry *entry )
    {
        TQStringList urls = entry->urls();
        KURL::List result;

        for ( TQStringList::Iterator it = urls.begin(); it != urls.end(); ++it )
        {
            KURL url = Settings::locateFile( *it, m_bibtexfile->fileName, this );
            if ( url.isValid() )
                result.append( url );
        }

        return result;
    }

    void DocumentWidget::slotSelectionChanged()
    {
        int numSelected = 0;
        TQListViewItemIterator it( m_listViewElements, TQListViewItemIterator::Selected );
        while ( it.current() && numSelected < 3 )
        {
            numSelected++;
            it++;
        }

        emit listViewSelectionChanged( numSelected );
    }

    BibTeX::FileImporter *DocumentWidget::fileImporterFactory( const TQString &fileName )
    {
        Settings * settings = Settings::self( NULL );
        BibTeX::FileImporter * importer = NULL;
        BibTeX::File::FileFormat format = BibTeX::File::formatUndefined;

        if ( fileName.endsWith( ".bib", false ) )
            format = BibTeX::File::formatBibTeX;
        else if ( settings->external_xml2bibAvailable && settings->external_end2xmlAvailable && ( fileName.endsWith( ".ref", false ) || fileName.endsWith( ".refer", false ) || fileName.endsWith( ".txt", false ) || fileName.endsWith( ".rfr", false ) ) )
            format = BibTeX::File::formatEndNote;
        else if ( settings->external_xml2bibAvailable && settings->external_end2xmlAvailable && ( fileName.endsWith( ".isi", false ) || fileName.endsWith( ".cgi", false ) ) )
            format = BibTeX::File::formatISI;
        else if ( fileName.endsWith( ".ris", false ) )
            format = BibTeX::File::formatRIS;
        else if ( fileName.endsWith( ".xml", false ) )
        {
            TQStringList options = TQStringList::split( '|', ( settings->external_xml2bibAvailable && settings->external_end2xmlAvailable ? "MODS|EndNote XML|internal XML" : "internal XML" ) );
            bool ok = false;
            TQString answer = KInputDialog::getItem( i18n( "Choose file format" ), TQString( i18n( "Choose file format of file '%1':" ) ).arg( fileName ), options, 0, false, &ok, this );
            if ( ok && !answer.isNull() && answer == "DocBook5" )
                format = BibTeX::File::formatDocBook5;
            else if ( ok && !answer.isNull() && answer == "MODS" )
                format = BibTeX::File::formatMODS;
            else if ( ok && !answer.isNull() && answer == "EndNote XML" )
                format = BibTeX::File::formatEndNoteXML;
            else if ( ok && !answer.isNull() && answer == "internal XML" )
                format = BibTeX::File::formatXML;
        }
        else
        {
            TQStringList options = TQStringList::split( '|', ( settings->external_xml2bibAvailable && settings->external_end2xmlAvailable ? "BibTeX|EndNote|ISI|RIS" : "BibTeX|RIS" ) );
            bool ok = false;
            TQString answer = KInputDialog::getItem( i18n( "Choose file format" ), TQString( i18n( "Choose file format of file '%1':" ) ).arg( fileName ), options, 0, false, &ok, this );
            if ( ok && !answer.isNull() && answer == "BibTeX" )
                format = BibTeX::File::formatBibTeX;
            else if ( ok && !answer.isNull() && answer == "EndNote" )
                format = BibTeX::File::formatEndNote;
            else if ( ok && !answer.isNull() && answer == "ISI" )
                format = BibTeX::File::formatISI;
            else if ( ok && !answer.isNull() && answer == "RIS" )
                format = BibTeX::File::formatRIS;
        }

        if ( format != BibTeX::File::formatUndefined )
        {
            Settings * settings = Settings::self( NULL );
            switch ( format )
            {
            case BibTeX::File::formatBibTeX:
            {
                Settings * settings = Settings::self( NULL );
                importer = new BibTeX::FileImporterBibTeX( settings->editing_FirstNameFirst, settings->fileIO_Encoding );
            }
            break;
            case BibTeX::File::formatRIS:
            {
                if ( settings->external_xml2bibAvailable && settings->external_end2xmlAvailable && settings->fileIO_useBibUtils )
                    importer = new BibTeX::FileImporterBibUtils( BibTeX::File::formatRIS );
                else
                    importer = new BibTeX::FileImporterRIS( );
                break;
            }
            case BibTeX::File::formatEndNote:
            case BibTeX::File::formatEndNoteXML:
            case BibTeX::File::formatISI:
            case BibTeX::File::formatMODS:
                if ( settings->external_xml2bibAvailable && settings->external_end2xmlAvailable )
                    importer = new BibTeX::FileImporterBibUtils( format );
                break;
            default:
            {
                KMessageBox::sorry( this, TQString( i18n( "The file '%1' does not contain a known type of bibliography." ) ).arg( fileName ), i18n( "Unknown file format" ) );
            }
            }
        }

        return importer;
    }

    void DocumentWidget::slotPreviewElement( TQListViewItem * item )
    {
        if ( item == NULL )
            item = m_listViewElements->selectedItem();
        if ( item == NULL )
            item = m_listViewElements->currentItem();
        if ( item != NULL )
        {
            DocumentListViewItem * dlvi = dynamic_cast<DocumentListViewItem*>( item );
            if ( dlvi != NULL && m_listViewElements->isEnabled() )
            {
                BibTeX::Element *currentElement = dlvi->element()->clone();
                BibTeX::Entry *entry = dynamic_cast<BibTeX::Entry*>( currentElement );
                if ( entry != NULL )
                    m_bibtexfile->completeReferencedFields( entry );

                TDEStandardDirs * kstd = TDEGlobal::dirs();

                BibTeX::XSLTransform *transform = new BibTeX::XSLTransform( kstd->findResource( "data", "kbibtexpart/xslt/html.xsl" ) );
                if ( transform != NULL )
                {
                    BibTeX::FileExporterXSLT * exporter = new BibTeX::FileExporterXSLT( transform );
                    TQBuffer buffer;
                    buffer.open( IO_WriteOnly );
                    bool result = exporter->save( &buffer, currentElement );
                    buffer.close();

                    if ( result )
                    {
                        buffer.open( IO_ReadOnly );
                        TQTextStream htmlTS( &buffer );
                        htmlTS.setEncoding( TQTextStream::UnicodeUTF8 );
                        TQString htmlText = htmlTS.read();
                        buffer.close();
                        TQString text = htmlText.remove( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" ).append( "</qt>" ).prepend( "<qt>" ).replace( TQRegExp( "\\\\emph\\{([^}]+)\\}" ), "<em>\\1</em>" ).replace( TQRegExp( "\\\\[A-Za-z0-9]+" ), "" ).replace( '{', "" ).replace( '}', "" );
                        m_preview->setText( text );
                    }
                    else
                    {
                        int i = ( int ) BibTeX::EntryField::ftTitle - ( int ) BibTeX::EntryField::ftAbstract + 2;
                        m_preview->setText( m_listViewElements->currentItem()->text( i ) );
                    }

                    delete exporter;
                    delete transform;
                }
                else
                    m_preview->setText( i18n( "No preview available" ) );

                delete currentElement;
            }
        }
    }

    void DocumentWidget::slotTabChanged( TQWidget *tab )
    {
        setEnabled( false );
        TQApplication::setOverrideCursor( TQt::waitCursor );

        BibTeX::File *bibTeXFile = NULL;
        if ( m_editMode == emSource )
            bibTeXFile = m_sourceView->getBibTeXFile();
        else if ( m_editMode == emList )
            bibTeXFile = m_listViewElements->getBibTeXFile();

        if ( tab == m_sourceView && m_editMode != emSource )
        {
            // switching from list view to source view
            if ( bibTeXFile != NULL )
            {
                // check whether the selected widget has changed the bibtex file
                if ( bibTeXFile != m_bibtexfile )
                {
                    delete m_bibtexfile;
                    m_bibtexfile = bibTeXFile;
                }
                m_sourceView->setBibTeXFile( m_bibtexfile );
            }
            m_editMode = emSource;

            if ( m_actionEditCut && m_actionEditCopy && m_actionEditPaste )
            {
                m_actionEditCut->setEnabled( true );
                m_actionEditCopy->setEnabled( true );
                m_actionEditPaste->setEnabled( true );
            }
        }
        else if ( tab == m_container && m_editMode != emList )
        {
            // switching from source view to list view
            if ( bibTeXFile != NULL )
            {
                // check whether the selected widget has changed the bibtex file
                if ( bibTeXFile != m_bibtexfile )
                {
                    delete m_bibtexfile;
                    m_bibtexfile = bibTeXFile;

                    Settings * settings = Settings::self( m_bibtexfile );
                    settings->addToCompletion( m_bibtexfile );
                    m_sideBar->refreshLists( m_bibtexfile );
                }
                m_listViewElements->setBibTeXFile( m_bibtexfile );
                emit listViewSelectionChanged( 0 );
            }
            m_editMode = emList;
        }

        setEnabled( true );

        if ( tab == m_sourceView )
            m_sourceView->setFocus();
        else if ( tab == m_container )
            m_listViewElements->setFocus();

        if ( m_actionEditFind && m_actionEditFindNext )
        {
            m_actionEditFind->setEnabled( m_editMode == emSource );
            m_actionEditFindNext->setEnabled( m_editMode == emSource );
        }
        if ( m_actionEditCopyRef )
            m_actionEditCopyRef->setEnabled( m_editMode == emList );

        TQApplication::restoreOverrideCursor();

        emit undoChanged( m_editMode == emSource );
    }

    void DocumentWidget::slotModified()
    {
        if ( isEnabled() )
        {
            m_sideBar->refreshLists( m_bibtexfile );
            emit modified( );
        }
    }

    void DocumentWidget::slotFileGotDirty( const TQString& path )
    {
        if ( path == m_filename )
        {
            m_dirWatch.removeFile( m_filename );
            TQTimer::singleShot( 100, this, TQ_SLOT( slotRefreshDirtyFile( ) ) );
        }
    }

    void DocumentWidget::slotRefreshDirtyFile()
    {
        if ( KMessageBox::questionYesNo( this, TQString( i18n( "File '%1' has been modified. Reload file to import changes or ignore changes?" ) ).arg( m_filename ), i18n( "Reload file?" ), KGuiItem( i18n( "Reload" ), "reload" ), KGuiItem( i18n( "Ignore" ), "ignore" ) ) == KMessageBox::Yes )
            open( m_filename, false );
        else
            m_dirWatch.addFile( m_filename );
    }

    void DocumentWidget::slotViewDocument( int id )
    {
        Settings::openUrl( m_viewDocumentActionMenuURLs[ m_viewDocumentActionMenu->popupMenu() ->indexOf( id )], this );
    }

    void DocumentWidget::slotViewFirstDocument()
    {
        for ( TQStringList::Iterator it = m_viewDocumentActionMenuURLs.begin(); it != m_viewDocumentActionMenuURLs.end(); ++it )
            if (( *it ).endsWith( ".pdf" ) || ( *it ).endsWith( ".ps" ) || ( *it ).endsWith( ".djv" ) )
            {
                Settings::openUrl( *it, this );
                break;
            }
    }

    void DocumentWidget::slotViewFirstDocumentsOnlineRef()
    {
        for ( TQStringList::Iterator it = m_viewDocumentActionMenuURLs.begin(); it != m_viewDocumentActionMenuURLs.end(); ++it )
            if (( *it ).contains( "dx.doi.org" ) )
            {
                Settings::openUrl( *it, this );
                break;
            }
    }

    void DocumentWidget::slotAssignKeywords( int id )
    {
        TQValueList<BibTeX::Entry*> entryList;

        TQListViewItem * item = m_listViewElements->selectedItem();
        if ( item == NULL )
            item = m_listViewElements->currentItem();
        for ( TQListViewItemIterator it( m_listViewElements, TQListViewItemIterator::Selected ); it.current(); ++it )
        {
            BibTeX::Element * currentElement = NULL;
            DocumentListViewItem * dlvi = dynamic_cast<DocumentListViewItem*>( it.current() );
            if ( dlvi && (( currentElement = dlvi->element() ) != NULL ) )
            {
                BibTeX::Entry *entry = dynamic_cast<BibTeX::Entry*>( currentElement );
                if ( entry != NULL )
                    entryList << entry;
            }
        }

        for ( TQValueList<BibTeX::Entry*>::Iterator it = entryList.begin(); it != entryList.end(); ++it )
        {
            BibTeX::Entry *entry = *it;
            BibTeX::EntryField *field = NULL;
            BibTeX::KeywordContainer *keywordContainer = NULL;
            if (( field = entry->getField( BibTeX::EntryField::ftKeywords ) ) == NULL )
            {
                entry->addField( field = new BibTeX::EntryField( BibTeX::EntryField::ftKeywords ) );
            }

            BibTeX::Value *value = field->value();

            if ( !value->items.isEmpty() )
                keywordContainer = dynamic_cast<BibTeX::KeywordContainer*>( value->items.first() );

            if ( keywordContainer == NULL )
                value->items.append( keywordContainer = new BibTeX::KeywordContainer() );

            if ( m_assignKeywordsActionMenu->popupMenu() ->isItemChecked( id ) )
                keywordContainer->remove( m_assignKeywordsActionMenuURLs[ id ] );
            else
                keywordContainer->append( m_assignKeywordsActionMenuURLs[ id ] );

            slotModified();
        }
    }

    void DocumentWidget::slotAddKeyword()
    {
        TQString newKeyword = m_lineEditNewKeyword->text();

        TQValueList<BibTeX::Entry*> entryList;
        for ( TQListViewItemIterator it( m_listViewElements, TQListViewItemIterator::Selected ); it.current(); ++it )
        {
            BibTeX::Element * currentElement = NULL;
            DocumentListViewItem * dlvi = dynamic_cast<DocumentListViewItem*>( it.current() );
            if ( dlvi && (( currentElement = dlvi->element() ) != NULL ) )
            {
                BibTeX::Entry *entry = dynamic_cast<BibTeX::Entry*>( currentElement );
                if ( entry != NULL )
                    entryList << entry;
            }
        }

        for ( TQValueList<BibTeX::Entry*>::Iterator it = entryList.begin(); it != entryList.end(); ++it )
        {
            BibTeX::Entry *entry = *it;
            BibTeX::EntryField *field = entry->getField( BibTeX::EntryField::ftKeywords );
            if ( field == NULL )
            {
                field = new BibTeX::EntryField( BibTeX::EntryField::ftKeywords );
                entry->addField( field );
            }

            BibTeX::Value *value = field->value();
            BibTeX::KeywordContainer *keywordContainer = NULL;

            if ( !value->items.isEmpty() )
                keywordContainer = dynamic_cast<BibTeX::KeywordContainer*>( value->items.first() );

            if ( keywordContainer == NULL )
                value->items.append( keywordContainer = new BibTeX::KeywordContainer() );

            keywordContainer->append( newKeyword );
        }

        if ( !entryList.isEmpty() )
        {
            if ( m_lineEditNewKeyword->isVisible() &&
                    m_lineEditNewKeyword->parentWidget() &&
                    m_lineEditNewKeyword->parentWidget()->parentWidget() &&
                    m_lineEditNewKeyword->parentWidget()->parentWidget()->inherits( "TQPopupMenu" ) )
                m_lineEditNewKeyword->parentWidget()->parentWidget()->close();

            slotModified();
        }
    }

    void DocumentWidget::slotNormalizeIds()
    {
        Settings * settings = Settings::self();
        TQMap<BibTeX::Entry*, TQString> entryToNewId;
        TQMap<BibTeX::Entry*, DocumentListViewItem*> entryToItem;
        TQStringList msgList;

        if ( settings->idSuggestions_default < 0 )
        {
            KMessageBox::sorry( this, i18n( "You must set a default id suggestion in the settings dialog." ) );
            return;
        }

        for ( TQListViewItemIterator it( m_listViewElements, TQListViewItemIterator::Selected ); it.current(); ++it )
        {
            BibTeX::Element * currentElement = NULL;
            DocumentListViewItem * dlvi = dynamic_cast<DocumentListViewItem*>( it.current() );
            if ( dlvi && (( currentElement = dlvi->element() ) != NULL ) )
            {
                BibTeX::Entry *entry = dynamic_cast<BibTeX::Entry*>( currentElement );
                if ( entry != NULL )
                {
                    TQString newEntryId = IdSuggestions::createDefaultSuggestion( m_bibtexfile, entry );
                    entryToNewId[entry] = newEntryId;
                    entryToItem[entry] = dlvi;
                    msgList << "Replacing id of entry \"" + entry->id() + "\" with new id \"" + newEntryId + "\"";
                }
            }
        }

        if ( entryToNewId.isEmpty() )
            KMessageBox::information( this, i18n( "No entries were selected." ) );
        else if ( KMessageBox::questionYesNoList( this, i18n( "The following changes will be applied to the currently selected elements.\nConflicts with duplicate entry ids will be resolved when applying the new ids." ), msgList, i18n( "Normalize Entry Ids" ), KGuiItem( i18n( "Replace Entry Ids" ), "system-run" ), KGuiItem( i18n( "Cancel" ), "cancel" ) ) == KMessageBox::Yes )
        {
            TQApplication::setOverrideCursor( TQt::waitCursor );
            TQMap<BibTeX::Entry*, DocumentListViewItem*>::Iterator itItem = entryToItem.begin();
            for ( TQMap<BibTeX::Entry*, TQString>::Iterator it = entryToNewId.begin(); it != entryToNewId.end(); ++it, ++itItem )
            {
                BibTeX::Entry *entry = it.key();
                TQString newEntryId = it.data();
                newEntryId = IdSuggestions::resolveConflict( m_bibtexfile, newEntryId, entry );
                tqDebug( "Replacing id of entry \"%s\" with new id \"%s\"", entry->id().latin1(), newEntryId.latin1() );
                entry->setId( newEntryId );
                itItem.data()->updateItem();
            }
            slotModified();
            m_sideBar->refreshLists( m_bibtexfile );
            TQApplication::restoreOverrideCursor();
        }
    }

    KDirWatch &DocumentWidget::dirWatch()
    {
        return m_dirWatch;
    }

}

#include "documentwidget.moc"
