You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdesdk/kompare/komparepart/kompare_part.cpp

760 lines
22 KiB

/***************************************************************************
kompare_part.cpp - description
-------------------
begin : Sun Mar 4 2001
copyright : (C) 2001-2004 Otto Bruggeman
(C) 2001-2003 John Firebaugh
(C) 2004 Jeff Snyder
email : otto.bruggeman@home.nl
jfirebaugh@kde.org
jeff@caffeinated.me.uk
****************************************************************************/
/***************************************************************************
**
** 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.
**
***************************************************************************/
#include "kompare_qsplitter.h" // make sure we get there first
#include <tqlayout.h>
#include <tqwidget.h>
#include <kaction.h>
#include <kapplication.h>
#include <kdebug.h>
#include <kfiletreeview.h>
#include <kfiledialog.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <kstdaction.h>
#include <kinstance.h>
#include <ktempfile.h>
#include <kparts/genericfactory.h>
//#include <ktempdir.h>
#include <kio/netaccess.h>
#include "diffmodel.h"
#include "komparelistview.h"
#include "kompareconnectwidget.h"
#include "diffsettings.h"
#include "viewsettings.h"
#include "kompareprefdlg.h"
#include "komparesaveoptionswidget.h"
#include "komparesplitter.h"
#include "kompare_part.h"
typedef KParts::GenericFactory<KomparePart> KomparePartFactory;
K_EXPORT_COMPONENT_FACTORY( libkomparepart, KomparePartFactory )
ViewSettings* KomparePart::m_viewSettings = 0L;
DiffSettings* KomparePart::m_diffSettings = 0L;
KomparePart::KomparePart( TQWidget *parentWidget, const char *widgetName,
TQObject *parent, const char *name, const TQStringList & /*args*/ ) :
KParts::ReadWritePart(parent, name),
m_tempDiff( 0 ),
m_info()
{
// we need an instance
setInstance( KomparePartFactory::instance() );
if( !m_viewSettings ) {
m_viewSettings = new ViewSettings( 0 );
}
if( !m_diffSettings ) {
m_diffSettings = new DiffSettings( 0 );
}
readProperties( kapp->config() );
// This creates the "Model creator" and connects the signals and slots
m_modelList = new Diff2::KompareModelList( m_diffSettings, m_info, this, "komparemodellist" );
connect( m_modelList, TQT_SIGNAL(status( Kompare::Status )),
this, TQT_SLOT(slotSetStatus( Kompare::Status )) );
connect( m_modelList, TQT_SIGNAL(setStatusBarModelInfo( int, int, int, int, int )),
this, TQT_SIGNAL(setStatusBarModelInfo( int, int, int, int, int )) );
connect( m_modelList, TQT_SIGNAL(error( TQString )),
this, TQT_SLOT(slotShowError( TQString )) );
connect( m_modelList, TQT_SIGNAL(applyAllDifferences( bool )),
this, TQT_SLOT(updateActions()) );
connect( m_modelList, TQT_SIGNAL(applyDifference( bool )),
this, TQT_SLOT(updateActions()) );
connect( m_modelList, TQT_SIGNAL(applyAllDifferences( bool )),
this, TQT_SIGNAL(appliedChanged()) );
connect( m_modelList, TQT_SIGNAL(applyDifference( bool )),
this, TQT_SIGNAL(appliedChanged()) );
connect( m_modelList, TQT_SIGNAL( setModified( bool ) ),
this, TQT_SLOT( slotSetModified( bool ) ) );
// This is the stuff to connect the "interface" of the kompare part to the model inside
connect( m_modelList, TQT_SIGNAL(modelsChanged(const Diff2::DiffModelList*)),
this, TQT_SIGNAL(modelsChanged(const Diff2::DiffModelList*)) );
connect( m_modelList, TQT_SIGNAL(setSelection(const Diff2::DiffModel*, const Diff2::Difference*)),
this, TQT_SIGNAL(setSelection(const Diff2::DiffModel*, const Diff2::Difference*)) );
connect( this, TQT_SIGNAL(selectionChanged(const Diff2::DiffModel*, const Diff2::Difference*)),
m_modelList, TQT_SLOT(slotSelectionChanged(const Diff2::DiffModel*, const Diff2::Difference*)) );
connect( m_modelList, TQT_SIGNAL(setSelection(const Diff2::Difference*)),
this, TQT_SIGNAL(setSelection(const Diff2::Difference*)) );
connect( this, TQT_SIGNAL(selectionChanged(const Diff2::Difference*)),
m_modelList, TQT_SLOT(slotSelectionChanged(const Diff2::Difference*)) );
connect( m_modelList, TQT_SIGNAL(applyDifference(bool)),
this, TQT_SIGNAL(applyDifference(bool)) );
connect( m_modelList, TQT_SIGNAL(applyAllDifferences(bool)),
this, TQT_SIGNAL(applyAllDifferences(bool)) );
connect( m_modelList, TQT_SIGNAL(applyDifference(const Diff2::Difference*, bool)),
this, TQT_SIGNAL(applyDifference(const Diff2::Difference*, bool)) );
// This creates the splitterwidget and connects the signals and slots
m_splitter = new KompareSplitter ( m_viewSettings, parentWidget, widgetName );
connect( m_modelList, TQT_SIGNAL(setSelection(const Diff2::DiffModel*, const Diff2::Difference*)),
m_splitter, TQT_SLOT(slotSetSelection(const Diff2::DiffModel*, const Diff2::Difference*)) );
// connect( m_splitter, TQT_SIGNAL(selectionChanged(const Diff2::Difference*, const Diff2::Difference*)),
// m_modelList, TQT_SLOT(slotSelectionChanged(const Diff2::Difference*, const Diff2::Difference*)) );
connect( m_modelList, TQT_SIGNAL(setSelection(const Diff2::Difference*)),
m_splitter, TQT_SLOT(slotSetSelection(const Diff2::Difference*)) );
connect( m_splitter, TQT_SIGNAL(selectionChanged(const Diff2::Difference*)),
m_modelList, TQT_SLOT(slotSelectionChanged(const Diff2::Difference*)) );
connect( m_modelList, TQT_SIGNAL(applyDifference(bool)),
m_splitter, TQT_SLOT(slotApplyDifference(bool)) );
connect( m_modelList, TQT_SIGNAL(applyAllDifferences(bool)),
m_splitter, TQT_SLOT(slotApplyAllDifferences(bool)) );
connect( m_modelList, TQT_SIGNAL(applyDifference(const Diff2::Difference*, bool)),
m_splitter, TQT_SLOT(slotApplyDifference(const Diff2::Difference*, bool)) );
connect( this, TQT_SIGNAL(configChanged()), m_splitter, TQT_SIGNAL(configChanged()) );
// notify the part that this is our internal widget
setWidget( m_splitter->parentWidget() );
setupActions();
// set our XML-UI resource file
setXMLFile( "komparepartui.rc" );
// we are read-write by default -> uhm what if we are opened by lets say konq in RO mode ?
// Then we should not be doing this...
setReadWrite( true );
// we are not modified since we haven't done anything yet
setModified( false );
}
KomparePart::~KomparePart()
{
// This is the only place allowed to call cleanUpTemporaryFiles
// because before there might still be a use for them (when swapping)
cleanUpTemporaryFiles();
}
void KomparePart::setupActions()
{
// create our actions
m_saveAll = new KAction( i18n("Save &All"), "save_all", 0,
this, TQT_SLOT(saveAll()),
actionCollection(), "file_save_all" );
m_saveDiff = new KAction( i18n("Save .&diff..."), 0,
this, TQT_SLOT(saveDiff()),
actionCollection(), "file_save_diff" );
m_swap = new KAction( i18n( "Swap Source with Destination" ), 0,
this, TQT_SLOT(slotSwap()),
actionCollection(), "file_swap" );
m_diffStats = new KAction( i18n( "Show Statistics" ), 0,
this, TQT_SLOT(slotShowDiffstats()),
actionCollection(), "file_diffstats" );
KStdAction::preferences(this, TQT_SLOT(optionsPreferences()), actionCollection());
}
void KomparePart::updateActions()
{
m_saveAll->setEnabled ( m_modelList->isModified() );
m_saveDiff->setEnabled ( m_modelList->mode() == Kompare::ComparingFiles || m_modelList->mode() == Kompare::ComparingDirs );
m_swap->setEnabled ( m_modelList->mode() == Kompare::ComparingFiles || m_modelList->mode() == Kompare::ComparingDirs );
m_diffStats->setEnabled( m_modelList->modelCount() > 0 );
}
void KomparePart::setEncoding( const TQString& encoding )
{
kdDebug() << "Encoding: " << encoding << endl;
m_modelList->setEncoding( encoding );
}
bool KomparePart::openDiff( const KURL& url )
{
kdDebug(8103) << "Url = " << url.url() << endl;
emit kompareInfo( &m_info );
m_info.mode = Kompare::ShowingDiff;
m_info.source = url;
bool result = false;
m_info.localSource = fetchURL( url );
if ( !m_info.localSource.isEmpty() )
{
kdDebug(8103) << "Download succeeded " << endl;
result = m_modelList->openDiff( m_info.localSource );
updateActions();
updateCaption();
updateStatus();
}
else
{
kdDebug(8103) << "Download failed !" << endl;
}
return result;
}
bool KomparePart::openDiff( const TQString& diffOutput )
{
bool value = false;
emit kompareInfo( &m_info );
m_info.mode = Kompare::ShowingDiff;
if ( m_modelList->parseDiffOutput( diffOutput ) == 0 )
{
value = true;
m_modelList->show();
updateActions();
updateCaption();
updateStatus();
}
return value;
}
bool KomparePart::openDiff3( const KURL& diff3Url )
{
// FIXME: Implement this !!!
kdDebug(8103) << "Not implemented yet. Filename is: " << diff3Url.url() << endl;
return false;
}
bool KomparePart::openDiff3( const TQString& diff3Output )
{
// FIXME: Implement this !!!
kdDebug(8103) << "Not implemented yet. diff3 output is: " << endl;
kdDebug(8103) << diff3Output << endl;
return false;
}
bool KomparePart::exists( const TQString& url )
{
TQFileInfo fi( url );
return fi.exists();
}
const TQString KomparePart::fetchURL( const KURL& url )
{
TQString tempFileName( "" );
if ( !url.isLocalFile() )
{
if ( ! KIO::NetAccess::download( url, tempFileName, widget() ) )
{
slotShowError( i18n( "<qt>The URL <b>%1</b> cannot be downloaded.</qt>" ).arg( url.prettyURL() ) );
tempFileName = "";
}
return tempFileName;
}
else
{
// is Local already, check if exists
if ( exists( url.path() ) )
return url.path();
else
{
slotShowError( i18n( "<qt>The URL <b>%1</b> does not exist on your system.</qt>" ).arg( url.prettyURL() ) );
return tempFileName;
}
}
}
void KomparePart::cleanUpTemporaryFiles()
{
// i hope a local file will not be removed if it was not downloaded...
if ( !m_info.localSource.isEmpty() )
KIO::NetAccess::removeTempFile( m_info.localSource );
if ( !m_info.localDestination.isEmpty() )
KIO::NetAccess::removeTempFile( m_info.localDestination );
}
void KomparePart::compare( const KURL& source, const KURL& destination )
{
m_info.source = source;
m_info.destination = destination;
m_info.localSource = fetchURL( source );
m_info.localDestination = fetchURL( destination );
emit kompareInfo( &m_info );
if ( !m_info.localSource.isEmpty() && !m_info.localDestination.isEmpty() )
{
m_modelList->compare( m_info.localSource, m_info.localDestination );
updateActions();
updateCaption();
updateStatus();
}
}
void KomparePart::compareFiles( const KURL& sourceFile, const KURL& destinationFile )
{
emit kompareInfo( &m_info );
m_info.mode = Kompare::ComparingFiles;
m_info.source = sourceFile;
m_info.destination = destinationFile;
m_info.localSource = fetchURL( sourceFile );
m_info.localDestination = fetchURL( destinationFile );
if ( !m_info.localSource.isEmpty() && !m_info.localDestination.isEmpty() )
{
m_modelList->compareFiles( m_info.localSource, m_info.localDestination );
updateActions();
updateCaption();
updateStatus();
}
}
void KomparePart::compareDirs( const KURL& sourceDirectory, const KURL& destinationDirectory )
{
emit kompareInfo( &m_info );
m_info.mode = Kompare::ComparingDirs;
m_info.source = sourceDirectory;
m_info.destination = destinationDirectory;
m_info.localSource = fetchURL( sourceDirectory );
m_info.localDestination = fetchURL( destinationDirectory );
if ( !m_info.localSource.isEmpty() && !m_info.localDestination.isEmpty() )
{
m_modelList->compareDirs( m_info.localSource, m_info.localDestination );
updateActions();
updateCaption();
updateStatus();
}
}
void KomparePart::compare3Files( const KURL& /*originalFile*/, const KURL& /*changedFile1*/, const KURL& /*changedFile2*/ )
{
// FIXME: actually implement this some day :)
updateActions();
updateCaption();
updateStatus();
}
void KomparePart::openFileAndDiff( const KURL& file, const KURL& diffFile )
{
emit kompareInfo( &m_info );
m_info.source = file;
m_info.destination = diffFile;
m_info.localSource = fetchURL( file );
m_info.localDestination = fetchURL( diffFile );
m_info.mode = Kompare::BlendingFile;
if ( !m_info.localSource.isEmpty() && !m_info.localDestination.isEmpty() )
{
m_modelList->openFileAndDiff( m_info.localSource, m_info.localDestination );
updateActions();
updateCaption();
updateStatus();
}
}
void KomparePart::openDirAndDiff ( const KURL& dir, const KURL& diffFile )
{
emit kompareInfo( &m_info );
m_info.source = dir;
m_info.destination = diffFile;
m_info.localSource = fetchURL( dir );
m_info.localDestination = fetchURL( diffFile );
m_info.mode = Kompare::BlendingDir;
if ( !m_info.localSource.isEmpty() && !m_info.localDestination.isEmpty() )
{
m_modelList->openDirAndDiff( m_info.localSource, m_info.localDestination );
updateActions();
updateCaption();
updateStatus();
}
}
bool KomparePart::openFile()
{
// This is called from openURL
// This is a little inefficient but i will do it anyway
openDiff( m_url );
return true;
}
bool KomparePart::saveAll()
{
bool result = m_modelList->saveAll();
updateActions();
updateCaption();
updateStatus();
return result;
}
void KomparePart::saveDiff()
{
KDialogBase* dlg = new KDialogBase( widget(), "save_options",
true /* modal */, i18n("Diff Options"),
KDialogBase::Ok|KDialogBase::Cancel );
KompareSaveOptionsWidget* w = new KompareSaveOptionsWidget(
m_info.localSource,
m_info.localDestination,
m_diffSettings, dlg );
dlg->setMainWidget( w );
dlg->setButtonOK( KStdGuiItem::save() );
if( dlg->exec() ) {
w->saveOptions();
KConfig* config = instance()->config();
saveProperties( config );
config->sync();
while ( 1 )
{
KURL url = KFileDialog::getSaveURL( m_info.destination.url(),
i18n("*.diff *.dif *.patch|Patch Files"), widget(), i18n( "Save .diff" ) );
if ( KIO::NetAccess::exists( url, false, widget() ) )
{
int result = KMessageBox::warningYesNoCancel( widget(), i18n("The file exists or is write-protected; do you want to overwrite it?"), i18n("File Exists"), i18n("Overwrite"), i18n("Do Not Overwrite") );
if ( result == KMessageBox::Cancel )
{
break;
}
else if ( result == KMessageBox::No )
{
continue;
}
else
{
kdDebug(8103) << "URL = " << url.prettyURL() << endl;
kdDebug(8103) << "Directory = " << w->directory() << endl;
kdDebug(8103) << "DiffSettings = " << m_diffSettings << endl;
m_modelList->saveDiff( url.url(), w->directory(), m_diffSettings );
break;
}
}
else
{
kdDebug(8103) << "URL = " << url.prettyURL() << endl;
kdDebug(8103) << "Directory = " << w->directory() << endl;
kdDebug(8103) << "DiffSettings = " << m_diffSettings << endl;
m_modelList->saveDiff( url.url(), w->directory(), m_diffSettings );
break;
}
}
}
delete dlg;
}
KAboutData *KomparePart::createAboutData()
{
KAboutData *about = new KAboutData("kompare", I18N_NOOP("KomparePart"), "3.2");
about->addAuthor("John Firebaugh", "Author", "jfirebaugh@kde.org");
about->addAuthor("Otto Bruggeman", "Author", "otto.bruggeman@home.nl" );
return about;
}
void KomparePart::slotSetStatus( enum Kompare::Status status )
{
updateActions();
switch( status ) {
case Kompare::RunningDiff:
emit setStatusBarText( i18n( "Running diff..." ) );
break;
case Kompare::Parsing:
emit setStatusBarText( i18n( "Parsing diff output..." ) );
break;
case Kompare::FinishedParsing:
updateStatus();
break;
case Kompare::FinishedWritingDiff:
updateStatus();
emit diffURLChanged();
break;
default:
break;
}
}
void KomparePart::updateCaption()
{
TQString source = m_info.source.prettyURL();
TQString destination = m_info.destination.prettyURL();
TQString text;
switch ( m_info.mode )
{
case Kompare::ComparingFiles :
case Kompare::ComparingDirs :
case Kompare::BlendingFile :
case Kompare::BlendingDir :
text = source + ":" + destination;
break;
case Kompare::ShowingDiff :
text = source;
break;
default:
break;
}
emit setWindowCaption( text );
}
void KomparePart::updateStatus()
{
TQString source = m_info.source.prettyURL();
TQString destination = m_info.destination.prettyURL();
TQString text;
switch ( m_info.mode )
{
case Kompare::ComparingFiles :
text = i18n( "Comparing file %1 with file %2" )
.arg( source )
.arg( destination );
break;
case Kompare::ComparingDirs :
text = i18n( "Comparing files in %1 with files in %2" )
.arg( source )
.arg( destination );
break;
case Kompare::ShowingDiff :
text = i18n( "Viewing diff output from %1" ).arg( source );
break;
case Kompare::BlendingFile :
text = i18n( "Blending diff output from %1 into file %2" )
.arg( source )
.arg( destination );
break;
case Kompare::BlendingDir :
text = i18n( "Blending diff output from %1 into folder %2" )
.arg( m_info.source.prettyURL() )
.arg( m_info.destination.prettyURL() );
break;
default:
break;
}
emit setStatusBarText( text );
}
void KomparePart::slotShowError( TQString error )
{
KMessageBox::error( widget(), error );
}
void KomparePart::slotSwap()
{
if ( isModified() )
{
int query = KMessageBox::warningYesNoCancel
(
widget(),
i18n( "You have made changes to the destination file(s).\n"
"Would you like to save them?" ),
i18n( "Save Changes?" ),
KStdGuiItem::save(),
KStdGuiItem::discard()
);
if ( query == KMessageBox::Yes )
m_modelList->saveAll();
if ( query == KMessageBox::Cancel )
return; // Abort prematurely so no swapping
}
// Swap the info in the Kompare::Info struct
KURL url = m_info.source;
m_info.source = m_info.destination;
m_info.destination = url;
TQString string = m_info.localSource;
m_info.localSource = m_info.localDestination;
m_info.localDestination = string;
// Update window caption and statusbar text
updateCaption();
updateStatus();
m_modelList->swap();
}
void KomparePart::slotShowDiffstats( void )
{
// Fetch all the args needed for komparestatsmessagebox
// oldfile, newfile, diffformat, noofhunks, noofdiffs
TQString oldFile;
TQString newFile;
TQString diffFormat;
int filesInDiff;
int noOfHunks;
int noOfDiffs;
oldFile = m_modelList->selectedModel() ? m_modelList->selectedModel()->sourceFile() : TQString( "" );
newFile = m_modelList->selectedModel() ? m_modelList->selectedModel()->destinationFile() : TQString( "" );
if ( m_modelList->selectedModel() )
{
switch( m_info.format ) {
case Kompare::Unified :
diffFormat = i18n( "Unified" );
break;
case Kompare::Context :
diffFormat = i18n( "Context" );
break;
case Kompare::RCS :
diffFormat = i18n( "RCS" );
break;
case Kompare::Ed :
diffFormat = i18n( "Ed" );
break;
case Kompare::Normal :
diffFormat = i18n( "Normal" );
break;
case Kompare::UnknownFormat :
default:
diffFormat = i18n( "Unknown" );
break;
}
}
else
{
diffFormat = "";
}
filesInDiff = m_modelList->modelCount();
noOfHunks = m_modelList->selectedModel() ? m_modelList->selectedModel()->hunkCount() : 0;
noOfDiffs = m_modelList->selectedModel() ? m_modelList->selectedModel()->differenceCount() : 0;
if ( m_modelList->modelCount() == 0 ) { // no diff loaded yet
KMessageBox::information( 0L, i18n(
"No diff file, or no 2 files have been diffed. "
"Therefore no stats are available."),
i18n("Diff Statistics"), TQString::null, false );
}
else if ( m_modelList->modelCount() == 1 ) { // 1 file in diff, or 2 files compared
KMessageBox::information( 0L, i18n(
"Statistics:\n"
"\n"
"Old file: %1\n"
"New file: %2\n"
"\n"
"Format: %3\n"
"Number of hunks: %4\n"
"Number of differences: %5")
.arg(oldFile).arg(newFile).arg(diffFormat)
.arg(noOfHunks).arg(noOfDiffs),
i18n("Diff Statistics"), TQString::null, false );
} else { // more than 1 file in diff, or 2 directories compared
KMessageBox::information( 0L, i18n(
"Statistics:\n"
"\n"
"Number of files in diff file: %1\n"
"Format: %2\n"
"\n"
"Current old file: %3\n"
"Current new file: %4\n"
"\n"
"Number of hunks: %5\n"
"Number of differences: %6")
.arg(filesInDiff).arg(diffFormat).arg(oldFile)
.arg(newFile).arg(noOfHunks).arg(noOfDiffs),
i18n("Diff Statistics"), TQString::null, false );
}
}
bool KomparePart::queryClose()
{
if( !isModified() ) return true;
int query = KMessageBox::warningYesNoCancel
(
widget(),
i18n("You have made changes to the destination file(s).\n"
"Would you like to save them?" ),
i18n( "Save Changes?" ),
KStdGuiItem::save(),
KStdGuiItem::discard()
);
if( query == KMessageBox::Cancel )
return false;
if( query == KMessageBox::Yes )
return m_modelList->saveAll();
return true;
}
int KomparePart::readProperties( KConfig *config )
{
m_viewSettings->loadSettings( config );
m_diffSettings->loadSettings( config );
emit configChanged();
return 0;
}
int KomparePart::saveProperties( KConfig *config )
{
m_viewSettings->saveSettings( config );
m_diffSettings->saveSettings( config );
return 0;
}
void KomparePart::optionsPreferences()
{
// show preferences
KomparePrefDlg* pref = new KomparePrefDlg( m_viewSettings, m_diffSettings );
connect( pref, TQT_SIGNAL(applyClicked()), this, TQT_SIGNAL(configChanged()) );
if ( pref->exec() )
emit configChanged();
}
void KomparePart::slotSetModified( bool modified )
{
kdDebug() << "KomparePart::slotSetModified( " << modified << " );" << endl;
setModified( modified );
updateActions();
updateCaption();
}
#include "kompare_part.moc"