/* ****************************************************************************
  This file is part of KBabel

  Copyright (C) 1999-2000 by Matthias Kiefer
                            <matthias.kiefer@gmx.de>
                2001-2004 by Stanislav Visnovsky
                            <visnovsky@kde.org>
  Copyright (C) 2005, 2006 by Nicolas GOUTTE <goutte@kde.org>

  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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

  In addition, as a special exception, the copyright holders give
  permission to link the code of this program with any edition of
  the TQt library by Trolltech AS, Norway (or with modified versions
  of TQt that use the same license as TQt), and distribute linked
  combinations including the two.  You must obey the GNU General
  Public License in all respects for all of the code used other than
  TQt. If you modify this file, you may extend this exception to
  your version of the file, but you are not obligated to do so.  If
  you do not wish to do so, delete this exception statement from
  your version.

**************************************************************************** */

#include "catmanresource.h"
#include "catalogmanager.h"
#include "catmanlistitem.h"
#include "catalog.h"
#include "kbabeldictbox.h"

#include "resources.h"
#include "multiroughtransdlg.h"
#include "msgfmt.h"
#include "kbmailer.h"
#include "validateprogress.h"
#include "cvshandler.h"
#include "svnhandler.h"
#include "markpatterndialog.h"
#include "validationoptions.h"

#include <tqcheckbox.h>
#include <tqpopupmenu.h>
#include <tqlabel.h>
#include <tqpainter.h>

#include <kcmenumngr.h>
#include <kcursor.h>
#include <klocale.h>
#include <kglobal.h>
#include <kglobalsettings.h>
#include <kconfig.h>
#include <kdatatool.h>
#include <kiconloader.h>
#include <kmessagebox.h>
#include <kapplication.h>
#include <kaction.h>
#include <kfiledialog.h>
#include <kio/netaccess.h>
#include <kprogress.h>
#include <kwin.h>
#include <kdeversion.h>
#include <ktempfile.h>

#include <tqfileinfo.h>
#include <tqdir.h>
#include <tqtimer.h>
#include <tqbitmap.h>
#include <tqwhatsthis.h>
#include <tqheader.h>
#include <tqdragobject.h>
#include <tqlayout.h>
#include <tqtextedit.h>

using namespace KBabel;

const char* columnNames[] = {
    I18N_NOOP("Name"),
    I18N_NOOP("M"),
    I18N_NOOP("Fuzzy"),
    I18N_NOOP("Untranslated"),
    I18N_NOOP("Total"),
    I18N_NOOP("CVS/SVN Status"),
    I18N_NOOP("Last Revision"),
    I18N_NOOP("Last Translator")
};

#define COLTEXT(a)  (i18n(columnNames[a]))

CatalogManagerView::CatalogManagerView(KBabel::Project::Ptr project, TQWidget* parent,const char* name)
              : TQListView(parent,name)
	      , _dirWatch(0)
	      , _readInfoCount(0)
	      , _active(false)
	      , _stop(false)
	      , _stopSearch(false)
	      , _updateNesting(0)
	      , _logView(0)
	      , _logWindow(0)
              , m_validPOCVSRepository( false )
              , m_validPOTCVSRepository( false )
              , m_validPOSVNRepository( false )
              , m_validPOTSVNRepository( false )
	      , markPatternDialog(0)
	      , _validateDialog(0)
	      , _validateOptions(0)
	      , _validateOptionsDlg(0)
	      , _markAsFuzzy(false)
	      , _ignoreFuzzy(false)
	      , _project(project)
{
   _dirList.resize(200);
   _fileList.resize(500);
   _readInfoFileList.clear();

   _pendingProcesses.setAutoDelete(true);

   setSelectionMode(Single);

   _dictBox = new KBabelDictBox(this, "dictbox");
   _dictBox->hide();

   _updateTimer = new TQTimer(this);
   connect(_updateTimer,TQT_SIGNAL(timeout()),this,TQT_SLOT(checkUpdate()));

   addColumn(COLTEXT(COL_NAME));
   addColumn(COLTEXT(COL_MARKER),25);
   setColumnAlignment(1,AlignCenter);
   addColumn(COLTEXT(COL_FUZZY));
   setColumnAlignment(1,AlignCenter);
   addColumn(COLTEXT(COL_UNTRANS));
   setColumnAlignment(2,AlignCenter);
   addColumn(COLTEXT(COL_TOTAL));
   setColumnAlignment(3,AlignCenter);
   addColumn(TQString()); // CVS/SVN column, header is set later
   addColumn(COLTEXT(COL_REVISION));
   addColumn(COLTEXT(COL_TRANSLATOR));


   header()->setMovingEnabled(false);
   setAllColumnsShowFocus(true);
   setSorting(0);

   if(KContextMenuManager::showOnButtonPress())
   {
      connect(this,TQT_SIGNAL(rightButtonPressed(TQListViewItem*,const TQPoint &, int))
           ,this, TQT_SLOT(showContentsMenu(TQListViewItem*,const TQPoint &, int)));
   }
   else
   {
      connect(this,TQT_SIGNAL(rightButtonClicked(TQListViewItem*,const TQPoint &, int))
           ,this, TQT_SLOT(showContentsMenu(TQListViewItem*,const TQPoint &, int)));
   }
   connect(this, TQT_SIGNAL(returnPressed(TQListViewItem*))
           ,this, TQT_SLOT(activateItem(TQListViewItem*)));
   connect(this, TQT_SIGNAL(doubleClicked(TQListViewItem*))
           ,this, TQT_SLOT(activateItem(TQListViewItem*)));
   connect(this,TQT_SIGNAL(selectionChanged()),this,TQT_SLOT(checkSelected()));
   connect( this, TQT_SIGNAL( clicked(TQListViewItem *, const TQPoint &, int)),
    	   this, TQT_SLOT( columnClicked(TQListViewItem *, const TQPoint &, int)));

   _dirCommandsMenu = 0;
   _fileCommandsMenu = 0;
   _dirContentsMenu = 0;
   _fileContentsMenu = 0;

   _logWindow = new KDialogBase(0,"log window",false,i18n("Log Window")
                      ,KDialogBase::Close | KDialogBase::User1,KDialogBase::Close);
   _logWindow->setButtonText(KDialogBase::User1,i18n("C&lear"));
   _logWindow->setInitialSize(TQSize(300,200));

   TQWhatsThis::add(_logWindow,i18n("<qt><p><b>Log window</b></p>\n"
				   "<p>In this window the output of "
				   "the executed commands are shown.</p></qt>"));

   _logView = new TQTextEdit(_logWindow);
   _logView->setReadOnly(true);

   _logWindow->setMainWidget(_logView);

   connect(_logWindow,TQT_SIGNAL(user1Clicked()),_logView,TQT_SLOT(clear()));

   TQWhatsThis::add(this,i18n("<qt><p><b>Catalog Manager</b></p>\n"
"<p>The Catalog Manager merges two folders into one tree and displays all\n"
"PO and POT files in these folders. This way you can easily see if a\n"
"template has been added or removed. Also some information about the files\n"
"is displayed.</p>"
"<p>For more information see section <b>The Catalog Manager</b>"
" in the online help.</p></qt>"));

   setAcceptDrops(true); // just to get the drag displayed immediately

   mailer = new KBabelMailer( this, _project );

   // CVS
   cvshandler = new CVSHandler( );
   connect( cvshandler, TQT_SIGNAL( signalIsPORepository( bool ) ),
            this, TQT_SLOT( slotValidPOCVSRepository( bool ) ) );
   connect( cvshandler, TQT_SIGNAL( signalIsPOTRepository( bool ) ),
            this, TQT_SLOT( slotValidPOTCVSRepository( bool ) ) );
   connect( cvshandler, TQT_SIGNAL( signalFilesCommitted( const TQStringList& ) ),
            this, TQT_SLOT( updateFiles( const TQStringList& ) ) );

   // SVN
   svnhandler = new SVNHandler( );
   connect( svnhandler, TQT_SIGNAL( signalIsPORepository( bool ) ),
            this, TQT_SLOT( slotValidPOSVNRepository( bool ) ) );
   connect( svnhandler, TQT_SIGNAL( signalIsPOTRepository( bool ) ),
            this, TQT_SLOT( slotValidPOTSVNRepository( bool ) ) );
   connect( svnhandler, TQT_SIGNAL( signalFilesCommitted( const TQStringList& ) ),
            this, TQT_SLOT( updateFiles( const TQStringList& ) ) );

   KConfig *config = KGlobal::config();
   restoreView(config);

   _dictBox->readSettings(_project->config());
}

CatalogManagerView::~CatalogManagerView()
{
  if(_active)
     stop();

  if(_dirWatch)
      delete _dirWatch;

  if(_settings.killCmdOnExit)
  {
     KProcess* proc;
     for ( proc=_pendingProcesses.first(); proc != 0; proc=_pendingProcesses.next() )
     {
        proc->kill(SIGKILL);
     }
  }

  delete _logWindow;
  delete mailer;
  delete cvshandler;
  delete svnhandler;
  if (markPatternDialog) delete markPatternDialog;
}


void CatalogManagerView::saveView( KConfig *config) const
{
    saveMarker(config);

    KConfigGroupSaver( config, "CatalogManager" );

    config->writeEntry( "ValidateMarkAsFuzzy", _markAsFuzzy );
    config->writeEntry( "ValidateIgnoreFuzzy", _ignoreFuzzy );
}


void CatalogManagerView::restoreView( KConfig *config)
{
    readMarker(config);

    _markAsFuzzy = config->readBoolEntry( "ValidateMarkAsFuzzy", false );
    _ignoreFuzzy = config->readBoolEntry( "ValidateIgnoreFuzzy", false );
}

void CatalogManagerView::setRMBMenuFile( TQPopupMenu *m )
{
    _fileContentsMenu = m;
}

void CatalogManagerView::setRMBMenuDir( TQPopupMenu *m )
{
    _dirContentsMenu = m;
}

void CatalogManagerView::setDirCommandsMenu( TQPopupMenu *m )
{
    _dirCommandsMenu = m;
    connect(_dirCommandsMenu,TQT_SIGNAL(activated(int)),this,TQT_SLOT(slotDirCommand(int)));
}

void CatalogManagerView::setFileCommandsMenu( TQPopupMenu *m )
{
    _fileCommandsMenu = m;
    connect(_fileCommandsMenu,TQT_SIGNAL(activated(int)),this,TQT_SLOT(slotFileCommand(int)));
}

void CatalogManagerView::checkUpdate()
{
   _updateNesting++;
   pause(true);

   TQDictIterator<CatManListItem> it( _fileList ); // iterator for dict

   while ( it.current() && !_stop)
   {
      CatManListItem* item=it.current();

      item->checkUpdate();
      ++it;
   }

   pause(false);
   --_updateNesting;
   if( _updateNesting == 0 )
   {
	emit updateFinished();
   }
}



void CatalogManagerView::pause(bool flag)
{
   if(flag)
   {
      _updateTimer->stop();
   }
   else
   {
      _updateTimer->start(10000);
   }
}


void CatalogManagerView::stop(bool s)
{
   kdDebug(KBABEL_CATMAN) << "Stopping " << s << endl;
   pause(s);
   _stop=s;
   PoInfo::stopStaticRead = true;
}

void CatalogManagerView::stopSearch()
{
    _stopSearch = true;
}

void CatalogManagerView::clear()
{
   pause(true);

   // first clear up
   if(_dirWatch)
      delete _dirWatch;

   _dirWatch= new KDirWatch();
   connect(_dirWatch,TQT_SIGNAL(deleted(const TQString&)),this
				   ,TQT_SLOT(directoryDeleted(const TQString&)));
   connect(_dirWatch,TQT_SIGNAL(dirty(const TQString&)),this
				   ,TQT_SLOT(directoryChanged(const TQString&)));
   connect(_dirWatch,TQT_SIGNAL(created(const TQString&)),this
				   ,TQT_SLOT(directoryChanged(const TQString&)));

   _dirList.clear();
   _fileList.clear();

   TQListView::clear();
}

void CatalogManagerView::toggleAllMarks()
{
   _markerList.clear();

   TQListViewItemIterator it( this );
   CatManListItem* item;

   for ( ; it.current(); ++it )
   {
      item = (CatManListItem*) it.current();
      if(item->isFile())
      {
         bool wasMarked=item->marked();
         item->setMarked(!wasMarked);
         if(!wasMarked)
         {
            _markerList.append(item->package());
         }
         else
         {
            _markerList.remove(item->package());
         }
      }
   }

   checkSelected();
}

void CatalogManagerView::clearAllMarks()
{
   _markerList.clear();
   TQDictIterator<CatManListItem> it( _fileList ); // iterator for dict

   while ( it.current() )
   {
      CatManListItem* item=it.current();

      if(item->marked())
         _markerList.remove(item->package());

      item->setMarked(false);
      ++it;
   }

   checkSelected();
}

void CatalogManagerView::markModifiedFiles()
{
    TQDictIterator<CatManListItem> it( _fileList );
    while ( it.current() )
   {
      CatManListItem* item=it.current();
      /*if(item->marked())
         _markerList.remove(item->package());
      */
      if(item->isModified() && ! item->marked() ) {
          item->setMarked(true);
	  _markerList.append(item->package( ));
      }
      ++it;
   }

   checkSelected();
}

void CatalogManagerView::loadMarks()
{
    const KURL url = KFileDialog::getOpenURL( TQString(),"*.marklist", this );
    if( url.isEmpty() ) return;

    TQString filename;
#if KDE_IS_VERSION( 3, 2, 90 )
    if (!KIO::NetAccess::download( url, filename, this ) )
#else
    if( !KIO::NetAccess::download( url, filename ) )
#endif
    {
	KMessageBox::error(this,i18n(
	    "Error while trying to open file:\n %1").arg(url.prettyURL()));
	return;
    }

    // now load from file
    TQStringList newMarkerList; // better create new list in case of problems
    TQFile f( filename );
    if( f.open( IO_ReadOnly) )
    {
	TQTextStream s(&f);

	TQString input;

	s >> input ;
	if( input == "[Markers]" )
	{
	    while( !s.atEnd() )
	    {
		s >> input;
		newMarkerList.append(input);
	    }
	}
	else
	{
    	    KMessageBox::error(this
		,i18n("Error while trying to read file:\n %1\n"
		"Maybe it is not a valid file with list of markings.").arg(url.prettyURL()));
	    f.close();
	    return;
	}
	f.close();
    }
    else
    {
	KMessageBox::error(this,i18n(
	    "Error while trying to open file:\n %1").arg(url.prettyURL()));
    }

    KIO::NetAccess::removeTempFile( filename );

    // test validity of list items
    TQStringList testedList;
    TQStringList::const_iterator it;
    for( it=newMarkerList.constBegin() ; it!=newMarkerList.constEnd() ; ++it )
	if( _fileList[(*it)] != 0 ) testedList.append( (*it) );

    // apply new list
    for( it=_markerList.constBegin() ; it!=_markerList.constEnd() ; ++it )
    {
	CatManListItem* item = _fileList[(*it)];
	if( item ) item->setMarked(false);
    }

    _markerList = testedList;
    for( it=_markerList.constBegin() ; it!=_markerList.constEnd() ; ++it )
    {
	CatManListItem* item = _fileList[(*it)];
	if( item ) item->setMarked(true);
    }

   checkSelected();
}

void CatalogManagerView::saveMarks()
{
    const KURL url2 = KFileDialog::getSaveURL( TQString(), "*.marklist", this );
    if( url2.isEmpty() ) return;

    // ### FIXME: why is the file dialog not doing this?
    if ( KIO::NetAccess::exists( url2, false, this ) )
    {
        if(KMessageBox::warningContinueCancel(this,TQString("<qt>%1</qt>").arg(i18n("The file %1 already exists. "
	            "Do you want to overwrite it?").arg(url2.prettyURL())),i18n("Warning"),i18n("&Overwrite"))==KMessageBox::Cancel)
	{
	    return;
	}
    }

#if KDE_IS_VERSION( 3, 4, 92 )
    // Support for partially remote KIO slave like media:
    const KURL url ( KIO::NetAccess::mostLocalURL( url2, this ) );
#else
    const KURL url ( url2 );
#endif
    kdDebug() << "Saving marks: " << url2.prettyURL() << " most-local: " << url.prettyURL() << endl;
    
    TQFile* file = 0;
    KTempFile* tempFile = 0;
    TQTextStream* stream = 0;
    bool error = false;

    const bool localFile = url.isLocalFile();
    if ( localFile )
    {
        // We have a local file
        file = new TQFile( url.path() );
        if ( file->open (IO_WriteOnly) )
        {
            stream = new TQTextStream( file );
        }
        else
        {
            error = true;
        }
    }
    else
    {
        tempFile = new KTempFile();
        tempFile->setAutoDelete(true);
        stream = tempFile->textStream();
        error = !stream;
    }
    if ( !error )
    {
        // ### TODO: try to get a better file format for KDE4 (XML?), one working with real relative paths (no / at start) and working with UTF-8
        *stream << "[Markers]" << endl;
        for( TQStringList::const_iterator it = _markerList.constBegin(); it!=_markerList.constEnd() ; ++it )
            *stream << (*it) << endl;
    }
    if ( error )
    {
        // ### KDE4 FIXME: strip the final \n of the message
        KMessageBox::error( this,
            i18n( "An error occurred while trying to write to file:\n%1\n" ).arg( url.prettyURL()) );
    }
    else if ( !localFile )
    {
        tempFile->close();
        if( !KIO::NetAccess::upload( tempFile->name(), url, this ) )
        {
            // ### KDE4 FIXME: strip the final \n of the message
            KMessageBox::error(this,
                i18n("An error occurred while trying to upload the file:\n%1\n").arg(url.prettyURL()));
        }
    }

    // We have finished so clean up
    if ( localFile )
    {
        delete stream;
        file->close();
        delete file;
    }
    else
    {
        delete tempFile;
    }
   
    checkSelected();
}

void CatalogManagerView::slotMarkPattern( )
{
  setPatternMarks(true);
}

void CatalogManagerView::slotUnmarkPattern( )
{
  setPatternMarks(false);
}

void CatalogManagerView::setPatternMarks(bool mark)
{
  CatManListItem * item = (CatManListItem*)currentItem( );
  if (!item)
    item = (CatManListItem*)_dirList["/"];
  if (!item->isDir( ))
    return;

  if (!markPatternDialog)
    markPatternDialog = new MarkPatternDialog(this);

  markPatternDialog->setMode(mark);

  if (markPatternDialog->exec( ) != KDialog::Accepted)
    return;

  TQRegExp rx(markPatternDialog->pattern( ));
  rx.setWildcard(!markPatternDialog->useRegExp( ));
  rx.setCaseSensitive(markPatternDialog->isCaseSensitive( ));

  TQStringList fileList = item->allChildrenList(true);
  for (TQStringList::const_iterator it = fileList.constBegin( ); it != fileList.constEnd( ); ++it) {
    CatManListItem * i = _fileList[*it];

    TQString matchName;
    if (i->hasPo( ))
      matchName = i->poFile( );
    else if (i->hasPot( ) && markPatternDialog->includeTemplates( ))
      matchName = i->potFile( );
    matchName = TQFileInfo(matchName).baseName( );

    if (mark) {
      if (!matchName.isEmpty( ) && rx.exactMatch(matchName) && !i->marked( )) {
        i->setMarked(true);
        _markerList.append(i->package( ));
      }
    } else {
      if (!matchName.isEmpty( ) && rx.exactMatch(matchName) && i->marked( )) {
        i->setMarked(false);
        _markerList.remove(i->package( ));
      }
    }
  }
}

void CatalogManagerView::statistics()
{
   CatManListItem* i=(CatManListItem*) currentItem();

   if(!i)
     i=(CatManListItem*)_dirList["/"];

   if(isActive() && i->isDir())
   {
      if(KMessageBox::warningContinueCancel(this
         ,i18n("The Catalog Manager is still updating information about the files.\n"
"If you continue, it will try to update all necessary files, however this can take "
"a long time and may lead to wrong results. Please wait until all files are updated."),i18n("Warning")
         ,KStdGuiItem::cont()) == KMessageBox::Cancel)
      {
        return;
      }
   }

   TQStringList doList;

   if( i->isFile() ) doList.append(i->package());
   else doList = i->allChildrenList(true);

   showStatistics( i, doList );
}

void CatalogManagerView::markedStatistics()
{
   CatManListItem* i=(CatManListItem*) currentItem();

   if(!i)
     i=(CatManListItem*)_dirList["/"];

   if(isActive() && i->isDir())
   {
      if(KMessageBox::warningContinueCancel(this
         ,i18n("The Catalog Manager is still updating information about the files.\n"
"If you continue, it will try to update all necessary files, however this can take "
"a long time and may lead to wrong results. Please wait until all files are updated."),i18n("Warning")
         ,KStdGuiItem::cont()) == KMessageBox::Cancel)
      {
        return;
      }
   }

   TQStringList doList;

   if( i->isFile() ) doList.append(i->package());
   else doList = i->allChildrenList(true);

   TQStringList markedDoList;
   TQStringList::const_iterator it;
   for( it = doList.constBegin(); it != doList.constEnd(); ++it )
   {
         CatManListItem* item = _fileList[(*it)];
	 if( item->marked() ) markedDoList.append(item->package());
   }

   showStatistics( i, markedDoList );
}

void CatalogManagerView::showStatistics( CatManListItem  *i, TQStringList &childrenList )
{
   KLocale *locale = KGlobal::locale();

      TQString msg;
      int totalPackages=0;
      int totalPo=0;
      int totalNoPot=0;
      int needworkPo=0;
      int totalMsgid=0;
      int totalFuzzy=0;
      int totalUntranslated=0;

      TQStringList::const_iterator it;
      for( it = childrenList.constBegin(); it != childrenList.constEnd(); ++it )
      {
         CatManListItem* item = _fileList[(*it)];

         /*
         KASSERT1(item,KDEBUG_FATAL,KBABEL_CATMAN,"CatalogManagerView::statistics: item not in list %s"
                  ,(*it).ascii());
           */
         // be sure, that the information is updated
         _updateNesting++;
         item->checkUpdate();
         _updateNesting--;
	 if( _stop ) return;

         totalPackages++;

         int fuzzy=item->fuzzy();
         int total=item->total();
         int untrans=item->untranslated();

         if(item->hasPo())
            totalPo++;

         if(!item->hasPot())
            totalNoPot++;


         if(fuzzy || untrans)
            needworkPo++;

         totalMsgid+=total;
         totalFuzzy+=fuzzy;
         totalUntranslated+=untrans;
      }

      double percent;

      const TQString name=i->package(false);
      if(name.isEmpty())
          msg = i18n("Statistics for all:\n");
      else
          msg = i18n("Statistics for %1:\n").arg(name);

      msg+=i18n("Number of packages: %1\n").arg(locale->formatNumber(totalPackages, 0));

      percent=100.0-((double)needworkPo*100.0)/totalPackages;
      msg+=i18n("Complete translated: %1 % (%2)\n").arg(locale->formatNumber(percent,2)).arg(locale->formatNumber(totalPackages-needworkPo, 0));

      percent=100.0-((double)totalPo*100.0)/totalPackages;
      msg+=i18n("Only template available: %1 % (%2)\n").arg(locale->formatNumber(percent,2)).arg(locale->formatNumber(totalPackages-totalPo,0));
      percent=((double)totalNoPot*100.0)/totalPackages;
      msg+=i18n("Only PO file available: %1 % (%2)\n").arg(locale->formatNumber(percent,02)).arg(locale->formatNumber(totalNoPot, 0));

      msg+=i18n("Number of messages: %1\n").arg(locale->formatNumber(totalMsgid, 0));

      long int totalTranslated = totalMsgid - totalFuzzy - totalUntranslated;
      percent=((double)totalTranslated*100.0)/totalMsgid;
      msg+=i18n("Translated: %1 % (%2)\n").arg(locale->formatNumber(percent,2)).arg(locale->formatNumber(totalTranslated, 0));

      percent=((double)totalFuzzy*100.0)/totalMsgid;
      msg+=i18n("Fuzzy: %1 % (%2)\n").arg(locale->formatNumber(percent,2)).arg(locale->formatNumber(totalFuzzy, 0));

      percent=((double)totalUntranslated*100.0)/totalMsgid;
      msg+=i18n("Untranslated: %1 % (%2)\n").arg(locale->formatNumber(percent,2)).arg(locale->formatNumber(totalUntranslated, 0));

   KMessageBox::information(this,msg,i18n("Statistics"));
}

void CatalogManagerView::checkSyntax()
{
   CatManListItem* item=(CatManListItem*) currentItem();

   if(!item)
      item=(CatManListItem*) _dirList["/"];

   if(item->isFile())
   {
      if(!item->hasPo())
         return;

      Msgfmt::Status status;
      TQString output;
      Msgfmt msgfmt;

      status=msgfmt.checkSyntax(item->poFile(),output);

      switch(status)
      {
         case Msgfmt::Ok:
         {
            KMessageBox::information(this,i18n("The file is syntactically correct.\nOutput of \"msgfmt --statistics\":")+"\n"+output);
            break;
         }
         case Msgfmt::SyntaxError:
         {
            KMessageBox::information(this,i18n("The file has syntax errors.\nOutput of \"msgfmt --statistics\":")+"\n"+output);
            break;
         }
         case Msgfmt::HeaderError:
         {
            KMessageBox::information(this,i18n("The file has header syntax error.\nOutput of \"msgfmt --statistics\":")+"\n"+output);
            break;
         }
         case Msgfmt::Error:
         {
            KMessageBox::error(this,i18n("An error occurred while processing \"msgfmt --statistics\""));
            break;
         }
         case Msgfmt::NoExecutable:
         {
            KMessageBox::sorry(this,i18n("Cannot execute msgfmt. Please make sure that you have msgfmt in your PATH."));
            break;
         }
	 case Msgfmt::Unsupported:
	 {
    	    KMessageBox::sorry(this,i18n("You can use gettext tools only for checking PO files."));
	    break;
	 }
      }

   }
   else
   {
      Msgfmt::Status status;
      TQString output;
      Msgfmt msgfmt;

      status=msgfmt.checkSyntaxInDir(item->poFile(), "*.po", output);

      TQString name=item->package(false);

      switch(status)
      {
         case Msgfmt::Ok:
         {
            TQString msg;
            if(!name.isEmpty())
            {
               msg=i18n("All files in folder %1 are syntactically correct.\n"
"Output of \"msgfmt --statistics\":\n").arg(name)+output;
            }
            else
            {
               msg=i18n("All files in the base folder are syntactically correct.\n"
"Output of \"msgfmt --statistics\":\n")+output;
            }
            KMessageBox::information(this,msg);
            break;
         }
         case Msgfmt::SyntaxError:
         {
            TQString msg;
            if(!name.isEmpty())
            {
               msg=i18n("At least one file in folder %1 has syntax errors.\n"
"Output of \"msgfmt --statistics\":\n").arg(name)+output;
            }
            else
            {
               msg=i18n("At least one file in the base folder has syntax errors.\n"
"Output of \"msgfmt --statistics\":\n")+output;
            }
            KMessageBox::information(this,msg);
            break;
         }
         case Msgfmt::HeaderError:
         {
            TQString msg;
            if(!name.isEmpty())
            {
               msg=i18n("At least one file in folder %1 has header syntax errors.\n"
"Output of \"msgfmt --statistics\":\n").arg(name)+output;
            }
            else
            {
               msg=i18n("At least one file in the base folder has header syntax errors.\n"
"Output of \"msgfmt --statistics\":\n")+output;
            }
            KMessageBox::information(this,msg);
            break;
         }
         case Msgfmt::Error:
         {
            TQString msg;
            if(!name.isEmpty())
            {
              msg=i18n("An error occurred while processing \"msgfmt --statistics *.po\" "
"in folder %1").arg(name);
            }
            else
            {
              msg=i18n("An error occurred while processing \"msgfmt --statistics *.po\" "
"in the base folder");
            }
            KMessageBox::error(this,msg);
            break;
         }
         case Msgfmt::NoExecutable:
         {
            KMessageBox::sorry(this,i18n("Cannot execute msgfmt. Please make sure that you have msgfmt in your PATH."));
            break;
         }
	 case Msgfmt::Unsupported:
	 {
    	    KMessageBox::sorry(this,i18n("You can use gettext tools only for checking PO files."));
	    break;
	 }
      }
   }
}

void CatalogManagerView::roughTranslation()
{
    TQPtrList<CatManListItem> result;
    CatManListItem *current = static_cast<CatManListItem *>(currentItem());
    if( current->isDir() )
    {
	TQStringList s = current->allChildrenList(true);
	TQStringList::const_iterator it;
	for( it = s.constBegin() ; it != s.constEnd(); ++it )
	{
	    CatManListItem *item = _fileList[(*it)];
	    if( item ) result.append( item );
	}
    }
    else
    {
	result.append( current );
    }

    MultiRoughTransDlg* dialog=new MultiRoughTransDlg( _dictBox, result, this );
    dialog->exec();
    delete dialog;
}

void CatalogManagerView::markedRoughTranslation()
{
    if( _markerList.count() == 0 ) return;

    TQPtrList<CatManListItem> result;

    TQStringList::const_iterator it;
    for( it = _markerList.constBegin() ; it != _markerList.constEnd(); ++it )
    {
	CatManListItem *item = _fileList[(*it)];
	result.append( item );
    }

    MultiRoughTransDlg* dialog=new MultiRoughTransDlg( _dictBox, result, this );
    dialog->exec();
    delete dialog;
}

void CatalogManagerView::mailFiles()
{
  CatManListItem* item = (CatManListItem*)currentItem();
  if(item->isDir()) {
    TQStringList filesToSend;
    TQStringList childrenList = item->allChildrenList(true);

    TQStringList::const_iterator it;
    for (it = childrenList.constBegin(); it != childrenList.constEnd(); ++it) {
      CatManListItem* i = _fileList[(*it)];
      if (i->hasPo()) {
	filesToSend << i->poFile();
      }
    }
    mailer->sendFiles(filesToSend, item->text(0));
  } else {
    if (item->hasPo()) {
      mailer->sendOneFile(item->poFile());
    }
  }
}

void CatalogManagerView::mailMarkedFiles()
{
  if (_markerList.count() == 0)
    return;

  TQStringList filesToSend;
  TQStringList::const_iterator it;
  for (it = _markerList.constBegin(); it != _markerList.constEnd(); ++it) {
    CatManListItem* i = _fileList[(*it)];
    if (i->hasPo()) {
      filesToSend << i->poFile();
    }
  }
  mailer->sendFiles(filesToSend);
}

void CatalogManagerView::packageFiles( )
{
    CatManListItem* item = (CatManListItem*)currentItem();
    if(item->isDir()) {
        TQStringList filesToPackage;
        TQStringList childrenList = item->allChildrenList(true);

        TQStringList::const_iterator it;
        for (it = childrenList.constBegin(); it != childrenList.constEnd(); ++it) {
          CatManListItem* i = _fileList[(*it)];
          if (i->hasPo()) {
	    filesToPackage << i->poFile();
          }
        }
	TQString packageFileName = KFileDialog::getSaveFileName(TQString(),"*.tar.bz2\n*.tar.gz",this);
	mailer->buildArchive( filesToPackage, packageFileName, TQString(), false );
    }
    else {
        if (item->hasPo()) {
          TQStringList fileToPackage(item->poFile());
	  TQString packageFileName = KFileDialog::getSaveFileName(TQString(),"*.tar.bz2\n*.tar.gz",this);
	  mailer->buildArchive( fileToPackage, packageFileName, TQString(), false );
        }
    }
}
void CatalogManagerView::packageMarkedFiles( )
{
    if (_markerList.count() == 0)
      return;

    TQStringList filesToPackage;
    TQStringList::const_iterator it;
    for (it = _markerList.constBegin(); it != _markerList.constEnd(); ++it) {
      CatManListItem* i = _fileList[(*it)];
      if (i->hasPo()) {
        filesToPackage << i->poFile();
      }
    }

    TQString packageFileName = KFileDialog::getSaveFileName(TQString(),"*.tar.bz2\n*.tar.gz",this);
    mailer->buildArchive( filesToPackage, packageFileName, TQString(), false );
}

// CVS
void CatalogManagerView::cvsUpdate( )
{
  doCVSCommand( CVS::Update );
}

void CatalogManagerView::cvsUpdateMarked( )
{
  doCVSCommand( CVS::Update, true );
}

void CatalogManagerView::cvsCommit( )
{
  doCVSCommand( CVS::Commit );
}

void CatalogManagerView::cvsCommitMarked( )
{
  doCVSCommand( CVS::Commit, true );
}

void CatalogManagerView::cvsStatus( )
{
  doCVSCommand( CVS::Status );
}

void CatalogManagerView::cvsStatusMarked( )
{
  doCVSCommand( CVS::Status, true );
}

void CatalogManagerView::cvsUpdateTemplate( )
{
  doCVSCommand( CVS::Update, false, true );
}

void CatalogManagerView::cvsUpdateMarkedTemplate( )
{
  doCVSCommand( CVS::Update, true, true );
}

void CatalogManagerView::cvsCommitTemplate( )
{
  doCVSCommand( CVS::Commit, false, true );
}

void CatalogManagerView::cvsCommitMarkedTemplate( )
{
  doCVSCommand( CVS::Commit, true, true );
}

void CatalogManagerView::cvsDiff( )
{
  doCVSCommand( CVS::Diff, false, false );
}

void CatalogManagerView::doCVSCommand( CVS::Command cmd, bool marked, bool templates )
{
  KSharedConfig* config = _project->sharedConfig();
  if ( marked ) {
    if ( _markerList.isEmpty() ) return;
    TQStringList fileList;
    TQStringList::const_iterator it;
    for ( it = _markerList.constBegin( ); it != _markerList.constEnd( ); ++it ) {
      CatManListItem * i = _fileList[(*it)];
      if ( templates && i->hasPot( ) )
        fileList << i->potFile( );
      else if ( !templates && i->hasPo( ) )
        fileList << i->poFile( );
    }
    cvshandler->execCVSCommand( this, cmd, fileList, templates, config );
  } else {
    const TQString basedir = ( templates ? _settings.potBaseDir : _settings.poBaseDir );
    TQString cvsItem;
    CatManListItem * item = (CatManListItem*)currentItem( );
    if ( ( cmd == CVS::Commit || cmd == CVS::Diff ) && item->isDir( ) ) {
      // all children including directories
      TQStringList cvsItems = item->allChildrenFileList (true, false, true);
      if ( !cvsItems.isEmpty( ) )
        cvshandler->execCVSCommand( this, cmd, cvsItems, templates, config );
    } else {
      if ( templates && item->hasPot( ) )
        cvsItem = item->potFile( );
      else if ( !templates && item->hasPo( ) )
        cvsItem = item->poFile( );

      if ( !cvsItem.isEmpty( ) )
        cvshandler->execCVSCommand( this, cmd, cvsItem, templates, config );
    }
  }
}

//SVN
void CatalogManagerView::svnUpdate( )
{
    doSVNCommand( SVN::Update );
}

void CatalogManagerView::svnUpdateMarked( )
{
    doSVNCommand( SVN::Update, true );
}

void CatalogManagerView::svnCommit( )
{
    doSVNCommand( SVN::Commit );
}

void CatalogManagerView::svnCommitMarked( )
{
    doSVNCommand( SVN::Commit, true );
}

void CatalogManagerView::svnStatusRemote( )
{
    doSVNCommand( SVN::StatusRemote );
}

void CatalogManagerView::svnStatusRemoteMarked( )
{
    doSVNCommand( SVN::StatusRemote, true );
}

void CatalogManagerView::svnStatusLocal( )
{
    doSVNCommand( SVN::StatusLocal );
}

void CatalogManagerView::svnStatusLocalMarked( )
{
    doSVNCommand( SVN::StatusLocal, true );
}

void CatalogManagerView::svnInfo()
{
    doSVNCommand( SVN::Info );
}

void CatalogManagerView::svnInfoMarked()
{
    doSVNCommand( SVN::Info, true );
}

void CatalogManagerView::svnUpdateTemplate( )
{
    doSVNCommand( SVN::Update, false, true );
}

void CatalogManagerView::svnUpdateMarkedTemplate( )
{
    doSVNCommand( SVN::Update, true, true );
}

void CatalogManagerView::svnCommitTemplate( )
{
    doSVNCommand( SVN::Commit, false, true );
}

void CatalogManagerView::svnCommitMarkedTemplate( )
{
    doSVNCommand( SVN::Commit, true, true );
}

void CatalogManagerView::svnDiff( )
{
    doSVNCommand( SVN::Diff, false, false );
}

void CatalogManagerView::doSVNCommand( SVN::Command cmd, bool marked, bool templates )
{
    KSharedConfig* config = _project->sharedConfig();
    if ( marked ) {
        if ( _markerList.isEmpty() ) return;
        TQStringList fileList;
        TQStringList::const_iterator it;
        for ( it = _markerList.constBegin( ); it != _markerList.constEnd( ); ++it ) {
            CatManListItem * i = _fileList[(*it)];
            if ( templates && i->hasPot( ) )
                fileList << i->potFile( );
            else if ( !templates && i->hasPo( ) )
                fileList << i->poFile( );
        }
        svnhandler->execSVNCommand( this, cmd, fileList, templates, config );
    } else {
        const TQString basedir = ( templates ? _settings.potBaseDir : _settings.poBaseDir );
        TQString svnItem;
        CatManListItem * item = (CatManListItem*)currentItem( );
        if ( ( cmd == SVN::Commit || cmd == SVN::Diff ) && item->isDir( ) ) {
      // all children including directories
            TQStringList svnItems = item->allChildrenFileList (true, false, true);
            if ( !svnItems.isEmpty( ) )
                svnhandler->execSVNCommand( this, cmd, svnItems, templates, config );
        } else {
            if ( templates && item->hasPot( ) )
                svnItem = item->potFile( );
            else if ( !templates && item->hasPo( ) )
                svnItem = item->poFile( );

            if ( !svnItem.isEmpty( ) )
                svnhandler->execSVNCommand( this, cmd, svnItem, templates, config );
    }
  }
}

void CatalogManagerView::showLog()
{
   _logWindow->show();
}

TQString CatalogManagerView::find( FindOptions &options, TQStringList &rest )
{
   CatManListItem* i=(CatManListItem*) currentItem();

   if(!i || options.inAllFiles)
     i=(CatManListItem*)_dirList["/"];

   TQValueList<TQString> foundFiles;
   _stopSearch = false;

   const TQString search = options.findStr.lower().simplifyWhiteSpace();
   TQStringList searchWords = TQStringList::split(' ', search);

   TQStringList childrenList;
   if( i->isFile() ) childrenList.append(i->name());
   else childrenList =i->allChildrenList(true);

   emit prepareFindProgressBar(childrenList.size());

   TQStringList::const_iterator it;
   for( it = childrenList.constBegin(); it != childrenList.constEnd(); ++it )
   {
	 CatManListItem* item = _fileList[(*it)];

	 if( !item )
	 {
	    kdWarning(KBABEL_CATMAN) << "The file information not found, skipping" << endl;
	    continue;
	 }

	 // skip if not marked and we lookup in marked
	 if( options.inMarkedFiles && !item->marked() )
	 {
	    kdDebug(KBABEL_CATMAN) << "Skipping due marking " << item->name() << endl;
    	    emit signalSearchedFile(1);
	    continue;
	 }

	 bool doSearch = options.isRegExp || options.inTemplates; // for regexp and templates we do not support index search
	 if( item->wordsUpdated() )
	    doSearch = doSearch || hasMatchingWords(item->wordList(), searchWords);
	 else doSearch = true; // we do not have index, we need to search
	 if( doSearch )
	 {
	    TQString itemFile;
	    if( options.inTemplates )
	    {
		if( item->hasPot() ) itemFile=item->potFile();
	    } else {
		if( item->hasPo() ) itemFile=item->poFile();
	    }

	    if( itemFile.isNull() )
	    {
		emit signalSearchedFile(1);
		continue;
	    }

    	    if( PoInfo::findInFile( itemFile , options ) )
	    {
    		emit signalSearchedFile(1);
		rest.clear();
		if( _stopSearch )
		{
		    // if we are stopped, return what we found and clear the rest
		    _stopSearch = false;
		    return itemFile;
		}
		const TQString foundItemFile = itemFile;

		it++;
		while( it != childrenList.constEnd() )
		{
        	    CatManListItem *item = _fileList[(*it)];

		    itemFile = TQString();
		    if( options.inTemplates )
		    {
			if( item->hasPot() ) itemFile=item->potFile();
		    } else {
			if( item->hasPo() )itemFile=item->poFile();
		    }
		    if( options.inMarkedFiles && !item->marked() )
			itemFile=TQString();

		    if( !itemFile.isNull())
		    {
			if( item->wordsUpdated() &&  !options.inTemplates )
			{
			    if( options.isRegExp || hasMatchingWords(item->wordList(), searchWords) )
			    {
				rest.append( itemFile );
			    }
			    else kdDebug(KBABEL_CATMAN) << "Don't try to lookup in " << itemFile << endl;
			} else {
			    rest.append( itemFile ); // there is no word index, add the file
			}
		    } else emit signalSearchedFile(1);

		    it++;
		}
    		return foundItemFile;

	    }
	} else kdDebug(KBABEL_CATMAN) << "Skipping " << item->poFile() << endl;
	emit signalSearchedFile(1);
	if( _stop || _stopSearch ) {
	    _stopSearch = false;
	    rest.clear();
	    if( _updateNesting == 0 && !_stop ) emit updateFinished();
	    return TQString();
	}
   }
   return TQString();
}

bool CatalogManagerView::hasMatchingWords( TQStringList &itemWords, TQStringList &searchWords)
{
    for( TQStringList::const_iterator it1 = searchWords.constBegin() ; it1 != searchWords.constEnd() ; ++it1 )
	for( TQStringList::const_iterator it2 = itemWords.constBegin() ; it2 != itemWords.constEnd() ; ++it2 )
	    if( *it1 == *it2
		|| (*it1).contains(*it2)
		|| (*it2).contains(*it1) ) return true;
    return false;
}

void CatalogManagerView::showContentsMenu(TQListViewItem *i, const TQPoint &point, int)
{
   CatManListItem* item = (CatManListItem*) i;

   if(!item)
      return;

  if(item->isDir())
   {
      _dirContentsMenu->exec(point);
   }
   else
   {
      _fileContentsMenu->exec(point);
   }
}

void CatalogManagerView::checkSelected()
{
   CatManListItem* item=(CatManListItem*)selectedItem();
   if(!item) return;

   const uint actionValue =
      NEEDS_PO * item->hasPo() + NEEDS_POT * item->hasPot() +
      NEEDS_MARK * item->marked() + NEEDS_DIR * item->isDir() +
      NEEDS_PO_CVS * m_validPOCVSRepository + NEEDS_POT_CVS * m_validPOTCVSRepository +
      NEEDS_PO_SVN * m_validPOSVNRepository + NEEDS_POT_SVN * m_validPOTSVNRepository;

   emit selectedChanged(actionValue);
}

void CatalogManagerView::activateItem(TQListViewItem *)
{
   CatManListItem* item=(CatManListItem*) currentItem();

   if(!item)
      return;

   if(item->isDir())
   {
      item->setOpen(!item->isOpen());
      return;
   }

   if(item->hasPo())
   {
      emit openFile(item->poFile(),item->package());
   }
   else if(item->hasPot())
   {
      emit openTemplate(item->potFile(),item->poFile(),item->package());
   }
   else
   {
      kdError(KBABEL_CATMAN) << "CatalogManagerView::activateItem: item has no file?" << endl;
   }

}

void CatalogManagerView::slotOpenFile()
{
   CatManListItem* item=(CatManListItem*) currentItem();

   if(item && item->isFile())
   {
      activateItem(item);
   }
}

void CatalogManagerView::slotOpenFileInNewWindow()
{
   CatManListItem* item=(CatManListItem*) currentItem();

   if(item && item->isFile())
   {
	TQString filename;
	if(item->hasPo())
	{
    	    emit openFileInNewWindow(item->poFile(),item->package());
	}
	else if(item->hasPot())
	{
    	    emit openTemplateInNewWindow(item->potFile(),item->poFile(),item->package());
	}
    }
}

void CatalogManagerView::slotOpenTemplate()
{
   CatManListItem* item=(CatManListItem*) currentItem();

   if(item && item->isFile())
   {
      emit openFile(item->potFile(),item->package());
   }
}

void CatalogManagerView::slotDeleteFile()
{
   CatManListItem* item=(CatManListItem*) currentItem();

   if(item && item->isFile() && item->hasPo() && !item->hasPot())
   {
      const TQString msg=i18n("Do you really want to delete the file %1?").arg(item->poFile());
      if(KMessageBox::warningContinueCancel(this,msg,i18n("Warning"),KGuiItem( i18n("Delete"), "editdelete"))== KMessageBox::Continue)
      {
         if(!TQFile::remove(item->poFile()))
         {
             KMessageBox::sorry(this,i18n("Was not able to delete the file %1!").arg(item->poFile()));
         }
      }
   }
}

void CatalogManagerView::toggleMark()
{
    CatManListItem* i = (CatManListItem*) currentItem();
    if( i && i->isDir() ) slotToggleMarksInDir();
    else slotToggleMark();
}

void CatalogManagerView::slotToggleMark()
{
   CatManListItem* item=(CatManListItem*) currentItem();

   if(item && item->isFile())
   {
      bool wasMarked=item->marked();
      item->setMarked(!wasMarked);

      if(wasMarked)
      {
         _markerList.remove(item->package());
      }
      else
      {
         _markerList.append(item->package());
      }
   }

    checkSelected();
}

void CatalogManagerView::slotToggleMarksInDir()
{
   CatManListItem* i=(CatManListItem*) currentItem();

   if(i && i->isDir())
   {
      const TQStringList contentList = i->allChildrenList(true);

      TQStringList::const_iterator it;
      for( it = contentList.constBegin(); it != contentList.constEnd(); ++it )
      {
         CatManListItem* item = _fileList[(*it)];

         if ( item == 0 )
             kdFatal(KBABEL_CATMAN) << "CatalogManagerView::slotToggleMarkInDir: item not in list" << endl;

         const bool wasMarked=item->marked();
         item->setMarked(!wasMarked);

         if(wasMarked)
         {
            _markerList.remove(item->package());
         }
         else
         {
            _markerList.append(item->package());
         }
      }
   }

    checkSelected();
}


void CatalogManagerView::slotClearMarksInDir()
{
   CatManListItem* i=(CatManListItem*) currentItem();

   if(i && i->isDir())
   {
      const TQStringList contentList=i->contentsList(true);

      TQStringList::const_iterator it;
      for( it = contentList.constBegin(); it != contentList.constEnd(); ++it )
      {
         CatManListItem* item = _fileList[(*it)];

         if ( item == 0 )
           kdFatal(KBABEL_CATMAN) << "CatalogManagerView::slotClearMarkInDir: item not in list" << endl;

         if(item->marked())
         {
            _markerList.remove(item->package());
         }
         item->setMarked(false);
      }
   }

    checkSelected();
}


void CatalogManagerView::slotDirCommand(int index)
{
   CatManListItem* item=(CatManListItem*) currentItem();


   if(index>=0 && item && item->isDir())
   {
      TQString cmd=*(_settings.dirCommands).at(index);
      cmd.replace("@PACKAGE@",item->name());
      cmd.replace("@PODIR@",item->poFile());
      cmd.replace("@POTDIR@",item->potFile());
      cmd.replace("@POFILES@",current().join(" "));
      cmd.replace("@MARKEDPOFILES@",marked().join(" "));

      kdDebug(KBABEL_CATMAN) << cmd << endl;

      KProcess* proc = new KShellProcess();
      _pendingProcesses.append(proc);

      connect( proc,TQT_SIGNAL( processExited(KProcess *) ), this
               ,TQT_SLOT( processEnded(KProcess*) ) );
      connect( proc,TQT_SIGNAL( receivedStdout(KProcess*,char*,int) ), this
               ,TQT_SLOT( showOutput(KProcess*,char*,int) ) );
      connect( proc,TQT_SIGNAL( receivedStderr(KProcess*,char*,int) ), this
               ,TQT_SLOT( showOutput(KProcess*,char*,int) ) );

      *proc << "cd" << item->poFile() << ";" << cmd;
      proc->start(KProcess::NotifyOnExit,KProcess::AllOutput);
   }
}

void CatalogManagerView::slotFileCommand(int index)
{
   CatManListItem* item=(CatManListItem*) currentItem();

   if(index>=0 && item && item->isFile())
   {
      CatManListItem* parent = (CatManListItem*)item->parent();

      TQString cmd=*(_settings.fileCommands).at(index);
      cmd.replace("@PACKAGE@",item->name());
      cmd.replace("@POFILE@",item->poFile());
      cmd.replace("@POTFILE@",item->potFile());
      cmd.replace("@PODIR@",parent->poFile());
      cmd.replace("@POTDIR@",parent->potFile());
      cmd.replace("@POEMAIL@",item->text(COL_TRANSLATOR));

      kdDebug(KBABEL_CATMAN) << cmd << endl;

      KProcess* proc = new KShellProcess();
      _pendingProcesses.append(proc);

      connect( proc,TQT_SIGNAL( processExited(KProcess *) ), this
               ,TQT_SLOT( processEnded(KProcess*) ) );
      connect( proc,TQT_SIGNAL( receivedStdout(KProcess*,char*,int) ), this
               ,TQT_SLOT( showOutput(KProcess*,char*,int) ) );
      connect( proc,TQT_SIGNAL( receivedStderr(KProcess*,char*,int) ), this
               ,TQT_SLOT( showOutput(KProcess*,char*,int) ) );

      *proc << "cd" << parent->poFile() << ";" << cmd;
      proc->start(KProcess::NotifyOnExit,KProcess::AllOutput);
   }

}


void CatalogManagerView::updateFile(TQString fileWithPath, bool force)
{
   TQString relFile;
   if(fileWithPath.startsWith(_settings.poBaseDir))
   {
      relFile=fileWithPath.mid(_settings.poBaseDir.length());
   }
   else if(fileWithPath.startsWith(_settings.potBaseDir))
   {
      relFile=fileWithPath.mid(_settings.potBaseDir.length());
   }
   else
   {
      return;
   }

   if(relFile.endsWith(".pot"))
   {
      relFile.truncate(relFile.length()-4);
   }
   else if(relFile.endsWith(".po"))
   {
      relFile.truncate(relFile.length()-3);
   }

   CatManListItem* item=_fileList[relFile];

   if(item)
   {
	_updateNesting++;
	if( force ) item->forceUpdate();
        else item->checkUpdate();
        _updateNesting--;
   }

}

void CatalogManagerView::updateAfterSave(TQString fileWithPath, PoInfo &newInfo)
{
   TQString relFile;
   if(fileWithPath.startsWith(_settings.poBaseDir))
   {
      relFile=fileWithPath.mid(_settings.poBaseDir.length());
   }
   else if(fileWithPath.startsWith(_settings.potBaseDir))
   {
      relFile=fileWithPath.mid(_settings.potBaseDir.length());
   }
   else
   {
      return;
   }

   if(relFile.endsWith(".pot"))
   {
      relFile.truncate(relFile.length()-4);
   }
   else if(relFile.endsWith(".po"))
   {
      relFile.truncate(relFile.length()-3);
   }

   CatManListItem* item=_fileList[relFile];

   if(item)
   {
      item->updateAfterSave(newInfo);
   }

}

void CatalogManagerView::buildTree()
{
   // in case we were called after settings update
   disconnect( this, TQT_SIGNAL( updateFinished() ), this, TQT_SLOT(buildTree() ) );

   emit signalBuildTree(false); // announce start of building

   clear();

   if(isActive())
      return;

   _updateNesting++;

   _active=true;
   _stop=false;


   CatManListItem* root = new CatManListItem(this, this,_settings.poBaseDir,_settings.potBaseDir);
   _dirList.insert("/",root);
   //root->setSelectable(false);

   TQFileInfo fileInfo(_settings.poBaseDir);
   if(!fileInfo.isDir())
   {
      KMessageBox::error(this,i18n("You have not specified a valid folder "
"for the base folder of the PO files:\n%1\n"
"Please check your settings in the project settings dialog.").arg(_settings.poBaseDir));

      _active=false;
      _updateNesting--;
      if( _updateNesting == 0 ) emit updateFinished();
      return;
   }

   cvshandler->setPOBaseDir( _settings.poBaseDir );
   svnhandler->setPOBaseDir( _settings.poBaseDir );
   mailer->setPOBaseDir(_settings.poBaseDir);

   fileInfo.setFile(_settings.potBaseDir);
   if(!fileInfo.isDir() && !_settings.potBaseDir.isEmpty())
   {
      KMessageBox::error(this,i18n("You have not specified a valid folder "
"for the base folder of the PO template files:\n%1\n"
"Please check your settings in the project settings dialog.").arg(_settings.potBaseDir));
   }

   cvshandler->setPOTBaseDir( _settings.potBaseDir );
   svnhandler->setPOTBaseDir( _settings.potBaseDir );

   setCursor(KCursor::waitCursor());

   //"/" is the root item
   buildDir("/",true);   // build dir without updating the items...

    if( _stop ) {
        _active = false;
	_updateNesting--;
	if( _updateNesting == 0 ) emit updateFinished();
	return;
    }

   _dirWatch->addDir(_settings.poBaseDir);
   if(!_settings.potBaseDir.isEmpty())
	   _dirWatch->addDir(_settings.potBaseDir);

   emit signalBuildTree(true); // announce beginning of tree building

   unsetCursor();

    if( _stop ) {
        _active = false;
	_updateNesting--;
	if( _updateNesting == 0 ) emit updateFinished();
	return;
    }

    updateMarkerList();

   const int files=_fileList.count()+_dirList.count();

   _readInfoCount = 0;

   emit prepareProgressBar(i18n("Reading file information"),files);

   root->setOpen(true);

    if( _stop ) {
        _active = false;
	_updateNesting--;
	if( _updateNesting == 0 ) emit updateFinished();
	return;
    }

   // first read information about the files...
   TQDictIterator<CatManListItem> it( _fileList ); // iterator for dict

   int i=0;
   while ( it.current() && !_stop)
   {
      it.current()->checkUpdate(true);
      ++i;
      ++it;
   }

   // ...then update directories
   TQDictIterator<CatManListItem> dit( _dirList ); // iterator for dict

   while ( dit.current() && !_stop)
   {
      dit.current()->checkUpdate();
      ++i;
      ++dit;
   }

   emit clearProgressBar();

   _dirWatch->startScan();
   pause(false);

   _active=false;

   _updateNesting--;

   if( _updateNesting == 0 )
   {
	emit updateFinished();
   }
}

bool CatalogManagerView::buildDir(TQString relDir,bool fast)
{
   if( _stop ) return false;

   bool haveTemplateDir=true;
   TQFileInfo fileInfo;

   fileInfo.setFile(_settings.potBaseDir);
   if(!fileInfo.isDir())
   {
      haveTemplateDir=false;
   }

   bool potHasFiles=false;
   if(haveTemplateDir)
   		potHasFiles=buildDir(_settings.potBaseDir,relDir,".pot",fast);

   bool poHasFiles=buildDir(_settings.poBaseDir,relDir,".po",fast);

   return (poHasFiles | potHasFiles);
}


bool CatalogManagerView::buildDir(const TQString& baseDir,const TQString& relDir
                                                , const TQString extension , bool fast)
{
      if( _stop ) return false;

      bool havePoFiles=false;

      CatManListItem* thisItem=_dirList[relDir];
      if(!thisItem)
      {
         kdFatal(KBABEL_CATMAN) << "null pointer to this item" << endl;
         return false;
      }

      const TQString poBaseDir=_settings.poBaseDir;
      const TQString potBaseDir=_settings.potBaseDir;

      // traverse directory in poBaseDir
      TQDir dir(baseDir+relDir);
      TQStringList entryList=dir.entryList("*"+extension,TQDir::Files,TQDir::Name);

      TQStringList::const_iterator it;

      for (  it = entryList.constBegin(); it != entryList.constEnd() && !_stop ; ++it )
      {
	  if( _stop ) return false;

          havePoFiles=true;

          TQString file=relDir+(*it);
          file.remove(TQRegExp(extension+"$"));
          CatManListItem* item = _fileList[file];
          if(!item)
          {
             item = new CatManListItem(this,thisItem,poBaseDir+file+".po",potBaseDir+file+".pot",file);
             _fileList.insert(file,item);
             _readInfoFileList.prepend(file);

             if(_markerList.contains(file))
             {
                item->setMarked(true);
             }

             if(!fast)
             {
                item->checkUpdate();
             }
          }
      }

      entryList=dir.entryList(TQDir::Dirs,TQDir::Name);

      for (  it = entryList.constBegin(); it != entryList.constEnd() && !_stop ; ++it )
      {
	  if( _stop ) return false;

          if((*it)=="." || (*it)=="..")
          {
             continue;
          }

          TQString subDir=relDir+(*it)+"/";
          if(!_dirWatch->contains(baseDir+subDir))
          {
              _dirWatch->addDir(baseDir+subDir);
          }

          bool otherHasFiles=true;

          CatManListItem* item = _dirList[subDir];
          if(!item && !_stop)
          {
             item = new CatManListItem(this, thisItem,poBaseDir+subDir,potBaseDir+subDir,subDir);
             _dirList.insert(subDir,item);

             otherHasFiles=false;
          }

	  if( _stop ) return false;

          // recursive call
          if(!buildDir(baseDir,subDir,extension,fast) && !otherHasFiles)
          {
             kdDebug(KBABEL_CATMAN) << "skipping " << subDir << endl;
             deleteDirItem(subDir);
			 item=0;
          }
          else
             havePoFiles=true;

      } // end looking up directories in po base dir

      return havePoFiles;
}


void CatalogManagerView::updateDir(TQString relDir)
{
      if( _stop ) return;

      kdDebug(KBABEL_CATMAN) << "updating dir " << relDir << endl;

      bool havePoFiles=false;

      CatManListItem* thisItem=_dirList[relDir];
      if(!thisItem)
      {
         kdFatal(KBABEL_CATMAN) << "null pointer to this item" << endl;
         return;
      }

      TQStringList contentList = thisItem->contentsList(true);

      const TQString poBaseDir=_settings.poBaseDir;
      const TQString potBaseDir=_settings.potBaseDir;

      // first lookup template directory
      TQDir dir(potBaseDir+relDir);
      TQStringList entryList=dir.entryList("*.pot",TQDir::Files,TQDir::Name);

      TQStringList::const_iterator it;

      for (  it = entryList.constBegin(); it != entryList.constEnd(); ++it )
      {
          if( _stop ) return;

          havePoFiles=true;

          TQString file=relDir+(*it);
          file.remove(TQRegExp(".pot$"));
          CatManListItem* item = _fileList[file];
          if(!item)
          {
             item = new CatManListItem(this, thisItem,poBaseDir+file+".po",potBaseDir+file+".pot",file);
             _fileList.insert(file,item);

             if(_markerList.contains(file))
             {
                item->setMarked(true);
             }

             item->checkUpdate();
          }
		  else
		  {
			 item->checkUpdate();
		  }

          contentList.remove(file);
      }

      entryList=dir.entryList(TQDir::Dirs,TQDir::Name);

      for (  it = entryList.constBegin(); it != entryList.constEnd(); ++it )
      {
          if( _stop ) return;

          if((*it)=="." || (*it)=="..")
          {
             continue;
          }

          bool newDirAdded=false;

          TQString subDir=relDir+(*it)+"/";
          if(!_dirWatch->contains(potBaseDir+subDir))
          {
              _dirWatch->addDir(potBaseDir+subDir);

              newDirAdded=true;
          }

          CatManListItem* item = _dirList[subDir];
          if(!item && newDirAdded)
          {
             item = new CatManListItem(this, thisItem,poBaseDir+subDir,potBaseDir+subDir,subDir);
             _dirList.insert(subDir,item);

              if(!buildDir(subDir,false))
              {
                   kdDebug(KBABEL_CATMAN) << "skipping " << subDir << endl;
                   deleteDirItem(subDir);
                   item=0;
              }
          }
		  else if(newDirAdded)
		  {
			  updateDir(subDir);
		  }


		  // if directory was already here, but no item
		  // -> directory contains no files
          if(item && !newDirAdded)
          {
             havePoFiles=true;
          }

      } // end looking up directories in template dir

      // now traverse directory in poBaseDir
      dir.setPath(poBaseDir+relDir);
      entryList=dir.entryList("*.po",TQDir::Files,TQDir::Name);

      for (  it = entryList.constBegin(); it != entryList.constEnd(); ++it )
      {
          havePoFiles=true;

          if( _stop ) return;

          TQString file=relDir+(*it);
          file.remove(TQRegExp(".po$"));
          CatManListItem* item = _fileList[file];
          if(!item)
          {
             item = new CatManListItem(this, thisItem,poBaseDir+file+".po",potBaseDir+file+".pot",file);
             _fileList.insert(file,item);

             if(_markerList.contains(file))
             {
                item->setMarked(true);
             }

             item->checkUpdate();
          }
		  else
		  {
			 item->checkUpdate();
		  }

          contentList.remove(file);
      }

      entryList=dir.entryList(TQDir::Dirs,TQDir::Name);

      for (  it = entryList.constBegin(); it != entryList.constEnd(); ++it )
      {
          if( _stop ) return;

          if((*it)=="." || (*it)=="..")
          {
             continue;
          }

          bool newDirAdded=false;

          TQString subDir=relDir+(*it)+"/";
          if(!_dirWatch->contains(poBaseDir+subDir))
          {
               _dirWatch->addDir(poBaseDir+subDir);
               newDirAdded=true;
          }

          CatManListItem* item = _dirList[subDir];

          bool templateHasFiles=(bool)item;

          if(!item && newDirAdded)
          {
             item = new CatManListItem(this, thisItem,poBaseDir+subDir,potBaseDir+subDir,subDir);
             _dirList.insert(subDir,item);

              if(!buildDir(subDir,false) && !templateHasFiles)
              {
                   kdDebug(KBABEL_CATMAN) << "skipping " << subDir << endl;
                   deleteDirItem(subDir);
				   item=0;
              }
          }
		  else if(newDirAdded)
		  {
			  updateDir(subDir);
		  }

		  // if directory was already here, but no item
		  // -> directory contains no files
          if(item && !newDirAdded)
          {
             havePoFiles=true;
          }


      } // end looking up directories in po base dir


      // check, if something in the directory has been deleted
      // but only if we traversed also the template directory
      if(contentList.count()>0)
      {
          TQStringList::const_iterator it;
          for( it = contentList.constBegin(); it != contentList.constEnd(); ++it )
          {
             TQFileInfo po(poBaseDir+(*it));
             TQFileInfo pot(potBaseDir+(*it));

             if(!po.exists() && !pot.exists())
             {
                CatManListItem* item = _fileList[(*it)];
                if(item)
                {
                   if(item->marked())
                      _markerList.remove(item->package());

                   _fileList.remove((*it));
                   delete item;
                }
             }
          }
      }

      if(!havePoFiles)
      {
	     deleteDirItem(relDir);

          // if this directory has to be removed, check, if
		  // the parent directory has to be removed too
          const int index=relDir.findRev("/",relDir.length()-2);
          if(index<0)
          {
            relDir="/";
          }
          relDir=relDir.left(index+1);
          updateDir(relDir);
      }
}

void CatalogManagerView::directoryChanged(const TQString& dir)
{
   pause(true);

   TQString relDir, relDirPo, relDirPot;
   if(dir.startsWith(_settings.poBaseDir))
   {
      relDirPo=dir.mid(_settings.poBaseDir.length());
   }
   if(dir.startsWith(_settings.potBaseDir))
   {
      relDirPot=dir.mid(_settings.potBaseDir.length());
   }
   
   if( relDirPo.isEmpty() )
   {
    // use POT
    relDir = relDirPot;
   }
   else if( relDirPot.isEmpty() )
   {
    // use PO
    relDir = relDirPo;
   }
   else 
   {
    // both PO and POT usable, find out the correct one
    if( relDirPo.left(1) == "/" )
    {
	relDir = relDirPo;
    }
    else
    {
	relDir = relDirPot;
    }
   }

   if(relDir.right(1)!="/")
   {
      relDir+="/";
   }

   kdDebug(KBABEL_CATMAN) << "directory changed: " << relDir << endl;

   TQFileInfo fileInfo(_settings.potBaseDir);

   CatManListItem* thisItem=_dirList[relDir];
   if(!thisItem)
   {
      // if this item is not in the list search for next existing parent item
      TQString prevRelDir;
      do
      {
          prevRelDir=relDir;
          const int index=relDir.findRev("/",relDir.length()-2);
          if(index<0)
          {
            relDir="/";
          }
          relDir=relDir.left(index+1);

          thisItem=_dirList[relDir];
      }
      while(relDir!="/" && !thisItem);

      if(!thisItem)
      {
         kdFatal(KBABEL_CATMAN) << "null pointer to this item: " << relDir << endl;
         return;
      }
      else
      {
          // if a parent item dir is found, create the needed item in this dir
          // and build the tree from this item on
          kdDebug(KBABEL_CATMAN) << "building dir: " << prevRelDir << endl;
          CatManListItem* item = new CatManListItem(this, thisItem,_settings.poBaseDir+prevRelDir
                     ,_settings.potBaseDir+prevRelDir,prevRelDir);
          _dirList.insert(prevRelDir,item);


          if(!buildDir(prevRelDir,false))
          {
             deleteDirItem(prevRelDir);
          }
      }
   }
   else
   {
      updateDir(relDir);
   }

   pause(false);
}


void CatalogManagerView::directoryDeleted(const TQString& dir)
{
   pause(true);

   TQString relDir, relDirPo, relDirPot;
   if(dir.startsWith(_settings.poBaseDir))
   {
      relDirPo=dir.mid(_settings.poBaseDir.length());
   }
   if(dir.startsWith(_settings.potBaseDir))
   {
      relDirPot=dir.mid(_settings.potBaseDir.length());
   }
   
   if( relDirPo.isEmpty() )
   {
    // use POT
    relDir = relDirPot;
   }
   else if( relDirPot.isEmpty() )
   {
    // use PO
    relDir = relDirPo;
   }
   else 
   {
    // both PO and POT usable, find out the correct one
    if( relDirPo.left(1) == "/" )
    {
	relDir = relDirPo;
    }
    else
    {
	relDir = relDirPot;
    }
   }

   if(relDir.right(1)!="/")
   {
      relDir+="/";
   }

   kdDebug(KBABEL_CATMAN) << "directory deleted: " << relDir << endl;

   CatManListItem* thisItem=_dirList[relDir];
   if(thisItem)
   {
	  // we have to take care, if one directory still exists
	  const bool poDeleted=!thisItem->hasPo();
	  const bool potDeleted=!thisItem->hasPot();

	  // if neither the po- nor the pot-directory exists any more
	  // delete all sub items
	  if(poDeleted && potDeleted)
	  {
		  deleteDirItem(relDir);
	  }
	  else
	  {
      	 TQStringList childList = thisItem->contentsList();

         CatManListItem* item;
         TQStringList::const_iterator it;
         for( it = childList.constBegin();it !=  childList.constEnd(); ++it )
         {
            item=_fileList[(*it)];
            if(item)
            {
		       if( (poDeleted && !item->hasPot()) ||
						(potDeleted && !item->hasPo()) )
			   {
                  _fileList.remove((*it));
                  delete item;
			   }
			   else
			   {
			      item->checkUpdate();
			   }
            }
            else
            {
               item=_dirList[(*it)];
               if(item)
               {
                  if( (poDeleted && !item->hasPot()) ||
						(potDeleted && !item->hasPo()) )
				  {
					  deleteDirItem((*it));
				  }
               }
               else
               {
                  kdDebug(KBABEL_CATMAN) << "directoryDeleted: don't have item "
						  << (*it) << endl;
               }
            }
         }
      }
   }

   pause(false);
}

void CatalogManagerView::fileInfoRead( TQString filename )
{
    if( _readInfoFileList.find( filename ) != _readInfoFileList.end() ) {
	emit progress( ++_readInfoCount);
	_readInfoFileList.remove( filename );
    }

    if( _readInfoFileList.isEmpty() ) emit clearProgressBar();
}

void CatalogManagerView::setSettings(CatManSettings newSettings)
{
   CatManSettings oldSettings=_settings;
   _settings=newSettings;

   if(_settings.poBaseDir.right(1)=="/")
      _settings.poBaseDir.truncate(_settings.poBaseDir.length()-1);
   if(_settings.potBaseDir.right(1)=="/")
      _settings.potBaseDir.truncate(_settings.potBaseDir.length()-1);

   _dirCommandsMenu->clear();
   int counter=0;
   for ( TQStringList::const_iterator it = _settings.dirCommandNames.constBegin()
                        ; it != _settings.dirCommandNames.constEnd(); ++it )
   {
      _dirCommandsMenu->insertItem((*it),counter);
      counter++;
   }
   _dirCommandsMenu->insertSeparator();
   _dirCommandsMenu->insertItem(i18n("Log Window"),this,TQT_SLOT(showLog()));

   _fileCommandsMenu->clear();
   counter=0;
   for ( TQStringList::const_iterator it = _settings.fileCommandNames.constBegin()
                        ; it != _settings.fileCommandNames.constEnd(); ++it )
   {
      _fileCommandsMenu->insertItem((*it),counter);
      counter++;
   }
   _fileCommandsMenu->insertSeparator();
   _fileCommandsMenu->insertItem(i18n("Log Window"),this,TQT_SLOT(showLog()));

   const bool pathChanged = (oldSettings.poBaseDir!=_settings.poBaseDir)
           || (oldSettings.potBaseDir!=_settings.potBaseDir);
   
   if(pathChanged)
   {
      if( !isActive() ) {
        TQTimer::singleShot(100,this,TQT_SLOT(buildTree()));
      } else {
        stop();
        connect( this, TQT_SIGNAL( updateFinished() ), this, TQT_SLOT(buildTree() ) );
      }
   }

   toggleColumn( COL_MARKER, _settings.flagColumn );
   toggleColumn( COL_FUZZY, _settings.fuzzyColumn );
   toggleColumn( COL_UNTRANS, _settings.untranslatedColumn );
   toggleColumn( COL_TOTAL, _settings.totalColumn );
   // FIXME: follow CVS/SVN status
   toggleColumn( COL_CVS_OR_SVN, _settings.cvsColumn );
   toggleColumn( COL_REVISION, _settings.revisionColumn );
   toggleColumn( COL_TRANSLATOR, _settings.translatorColumn );
}


CatManSettings CatalogManagerView::settings() const
{
   return _settings;
}


void CatalogManagerView::hideEvent(TQHideEvent*)
{
   pause(true);

   if(_dirWatch)
      _dirWatch->stopScan();
}

void CatalogManagerView::showEvent(TQShowEvent*)
{
   TQTimer::singleShot(1,this,TQT_SLOT(checkUpdate()));

   pause(false);

   if(_dirWatch)
      _dirWatch->startScan(true);
}

void CatalogManagerView::contentsMousePressEvent(TQMouseEvent* event)
{
   if(event->button() == Qt::LeftButton)
      _pressPos=event->pos();

   TQListView::contentsMousePressEvent( event );
}

void CatalogManagerView::contentsMouseMoveEvent(TQMouseEvent* event)
{
   if(event->state() & Qt::LeftButton)
   {
      const int delay = KGlobalSettings::dndEventDelay();
      if(TQABS( event->pos().x() - _pressPos.x() ) >= delay ||
         TQABS( event->pos().y() - _pressPos.y() ) >= delay)
      {
         CatManListItem* item = (CatManListItem*)itemAt(contentsToViewport(_pressPos));
         if(item && item->isFile())
         {
            // always add the po-file and if existing the pot-file to the drag and
            // let the user decide what to do, when dropping into kbabel
            TQStrList uri;
            uri.append(TQUriDrag::localFileToUri(item->poFile()));
            if(item->hasPot())
               uri.append(TQUriDrag::localFileToUri(item->potFile()));

            TQUriDrag* drag = new TQUriDrag(uri,this);
            TQPixmap icon=KGlobal::iconLoader()->loadIcon("txt",KIcon::Desktop);
            drag->setPixmap(icon,TQPoint(icon.width()/2,icon.height()/2));
            drag->drag();
         }
         else
         {
            TQListView::contentsMouseMoveEvent(event);
         }
      }
      else
      {
         TQListView::contentsMouseMoveEvent(event);
      }
   }
   else
   {
      TQListView::contentsMouseMoveEvent(event);
   }
}

void CatalogManagerView::readMarker( KConfig* config)
{
   KConfigGroupSaver cs(config,"CatalogManager");

   _markerList = config->readListEntry("Marker");
}

void CatalogManagerView::saveMarker( KConfig* config) const
{
   KConfigGroupSaver cs(config,"CatalogManager");

   config->writeEntry("Marker",_markerList);
   config->sync();
}


void CatalogManagerView::deleteDirItem(TQString relDir)
{
	CatManListItem* thisItem=_dirList[relDir];

	if(!thisItem)
		return;

	_dirList.remove(relDir);

	TQStringList childList = thisItem->allChildrenList();

	TQStringList::const_iterator it;
	for( it = childList.constBegin();it !=  childList.constEnd(); ++it )
    {
		if(!_fileList.remove((*it)))
			_dirList.remove((*it));
	}


	// delete the item with all sub item
	delete thisItem;
}



void CatalogManagerView::processEnded(KProcess* proc)
{
   _pendingProcesses.removeRef(proc);
}


void CatalogManagerView::showOutput(KProcess*, char *buffer, int buflen)
{
   const TQCString output(buffer,buflen+1);

   _logView->insert(output);
}

void CatalogManagerView::columnClicked(TQListViewItem * item, const TQPoint &, int c)
{
    if( item && c == COL_MARKER )
    {
       slotToggleMark();
    }
}

CatManListItem *CatalogManagerView::itemBelow( CatManListItem *item )
{
    if( item->isDir() )
    {
	if( item->firstChild() )
	    return static_cast<CatManListItem *>( item->firstChild() );
	else
	{
	    while( !static_cast<CatManListItem *>( item->nextSibling() ) )
	    {
		item = static_cast<CatManListItem *>( item->parent() );
		if( !item ) return item;
	    }
	    return static_cast<CatManListItem *>( item->nextSibling() );
	}
    }
    else
    {
	while( !static_cast<CatManListItem *>( item->nextSibling() ) )
	{
	    item =  static_cast<CatManListItem *>( item->parent());
	    if( !item ) return item;
	}
	return static_cast<CatManListItem *>( item->nextSibling() );
    }
}

CatManListItem *CatalogManagerView::itemAbove( CatManListItem *item )
{
    if( item->isDir() )
    {
	if( item->firstChild() )
	    return static_cast<CatManListItem *>( item->lastChild() );
	else
	{
	    while( !static_cast<CatManListItem *>( item->previousSibling() ) )
	    {
		item = static_cast<CatManListItem *>( item->parent() );
		if( !item ) return item;
	    }
	    return static_cast<CatManListItem *>( item->previousSibling() );
	}
    }
    else
    {
	while( !static_cast<CatManListItem *>( item->previousSibling() ) )
	{
	    item =  static_cast<CatManListItem *>( item->parent());
	    if( !item ) return item;
	}
	return static_cast<CatManListItem *>( item->previousSibling() );
    }
}

void CatalogManagerView::gotoNextUntranslated()
{
    CatManListItem *i;
    CatManListItem *current = static_cast<CatManListItem *>(currentItem());
    while( ( i = itemBelow(current)) )
    {
	if( i->untranslated() > 0 )
	{
	    setCurrentItem(i);
	    ensureItemVisible(i);
	    return;
	} else current = i;
    }
}

void CatalogManagerView::gotoPreviousUntranslated()
{
    CatManListItem *i;
    CatManListItem *current = static_cast<CatManListItem *>(currentItem());
    while( (i = itemAbove(current)) )
    {
	if( i->untranslated() > 0 )
	{
	    setCurrentItem(i);
	    ensureItemVisible(i);
	    return;
	} else current = i;
    }
}

void CatalogManagerView::gotoNextFuzzy()
{
    CatManListItem *i;
    CatManListItem *current = static_cast<CatManListItem *>(currentItem());
    while( ( i = itemBelow(current)) )
    {
	if( i->fuzzy() > 0 )
	{
	    setCurrentItem(i);
	    ensureItemVisible(i);
	    return;
	} else current = i;
    }
}

void CatalogManagerView::gotoPreviousFuzzy()
{
    CatManListItem *i;
    CatManListItem *current = static_cast<CatManListItem *>(currentItem());
    while( (i = itemAbove(current)) )
    {
	if( i->fuzzy() > 0 )
	{
	    setCurrentItem(i);
	    ensureItemVisible(i);
	    return;
	} else current = i;
    }
}

void CatalogManagerView::gotoNextFuzzyOrUntranslated()
{
    CatManListItem *i;
    CatManListItem *current = static_cast<CatManListItem *>(currentItem());
    while( ( i = itemBelow(current)) )
    {
	if( i->untranslated() > 0 || i->fuzzy() > 0 )
	{
	    setCurrentItem(i);
	    ensureItemVisible(i);
	    return;
	} else current = i;
    }
}

void CatalogManagerView::gotoPreviousFuzzyOrUntranslated()
{
    CatManListItem *i;
    CatManListItem *current = static_cast<CatManListItem *>(currentItem());
    while( (i = itemAbove(current)) )
    {
	if( i->untranslated() > 0 || i->fuzzy() > 0 )
	{
	    setCurrentItem(i);
	    ensureItemVisible(i);
	    return;
	} else current = i;
    }
}

void CatalogManagerView::gotoNextError()
{
    CatManListItem *i;
    CatManListItem *current = static_cast<CatManListItem *>(currentItem());
    while( ( i = itemBelow(current)) )
    {
	if( i->hasErrors() )
	{
	    setCurrentItem(i);
	    ensureItemVisible(i);
	    return;
	} else current = i;
    }
}

void CatalogManagerView::gotoPreviousError()
{
    CatManListItem *i;
    CatManListItem *current = static_cast<CatManListItem *>(currentItem());
    while( (i = itemAbove(current)) )
    {
	if( i->hasErrors() )
	{
	    setCurrentItem(i);
	    ensureItemVisible(i);
	    return;
	} else current = i;
    }
}

void CatalogManagerView::gotoNextTemplate()
{
    CatManListItem *i;
    CatManListItem *current = static_cast<CatManListItem *>(currentItem());
    while( ( i = itemBelow(current)) )
    {
	if( i->hasPot() && !i->hasPo() )
	{
	    setCurrentItem(i);
	    ensureItemVisible(i);
	    return;
	} else current = i;
    }
}

void CatalogManagerView::gotoPreviousTemplate()
{
    CatManListItem *i;
    CatManListItem *current = static_cast<CatManListItem *>(currentItem());
    while( (i = itemAbove(current)) )
    {
	if( i->hasPot() && !i->hasPo() )
	{
	    setCurrentItem(i);
	    ensureItemVisible(i);
	    return;
	} else current = i;
    }
}

void CatalogManagerView::gotoNextPo()
{
    CatManListItem *i;
    CatManListItem *current = static_cast<CatManListItem *>(currentItem());
    while( ( i = itemBelow(current)) )
    {
	if( i->hasPo() )
	{
	    setCurrentItem(i);
	    ensureItemVisible(i);
	    return;
	} else current = i;
    }
}

void CatalogManagerView::gotoPreviousPo()
{
    CatManListItem *i;
    CatManListItem *current = static_cast<CatManListItem *>(currentItem());
    while( (i = itemAbove(current)) )
    {
	if( i->hasPo() )
	{
	    setCurrentItem(i);
	    ensureItemVisible(i);
	    return;
	} else current = i;
    }
}

void CatalogManagerView::gotoNextMarked()
{
    CatManListItem *i;
    CatManListItem *current = static_cast<CatManListItem *>(currentItem());
    while( ( i = itemBelow(current)) )
    {
	if( i->marked() )
	{
	    setCurrentItem(i);
	    ensureItemVisible(i);
	    return;
	} else current = i;
    }
}

void CatalogManagerView::gotoPreviousMarked()
{
    CatManListItem *i;
    CatManListItem *current = static_cast<CatManListItem *>(currentItem());
    while( (i = itemAbove(current)) )
    {
	if( i->marked() )
	{
	    setCurrentItem(i);
	    ensureItemVisible(i);
	    return;
	} else current = i;
    }
}

TQStringList CatalogManagerView::current()
{
    TQStringList result;
    CatManListItem *current = static_cast<CatManListItem *>(currentItem());
    if( current->isDir() )
    {
	TQStringList s = current->allChildrenList(true);
	TQStringList::const_iterator it;
	for( it = s.constBegin() ; it != s.constEnd(); ++it )
	{
	    CatManListItem *item = _fileList[(*it)];
	    result.append( item->poFile() );
	}
    }
    else
    {
	if( current->hasPo() ) result.append( current->poFile() );
    }
    return result;
}

TQStringList CatalogManagerView::marked()
{
    TQStringList result;

    TQStringList::const_iterator it;
    for( it = _markerList.constBegin() ; it != _markerList.constEnd(); ++it )
    {
	CatManListItem *item = _fileList[(*it)];
	result.append( item->poFile() );
    }
    return result;
}

void CatalogManagerView::updateCurrent()
{
    CatManListItem *current = static_cast<CatManListItem *>(currentItem());
    if( !current->hasPo() && !current->hasPot() )
    {
	if( current->isFile() )
	{
	    _fileList.remove(current->package());
	    delete current;
	}
	else
	{
	    directoryDeleted(current->package());
	}
    }
    else
    {
	if( current->isDir() )
	{
	    directoryChanged(current->poFile() );
	}

	// check, if the item didn't get lost by the update in directoryChanged()
	CatManListItem *new_current = static_cast<CatManListItem *>(currentItem());
        if (new_current == current)
	{
	    current->forceUpdate();
	}
    }
}

void CatalogManagerView::updateFiles( const TQStringList& files )
{
  TQStringList::ConstIterator it;
  for ( it = files.constBegin( ); it != files.constEnd( ); ++it ) {
    updateFile( *it, true );
  }
}

CVSHandler * CatalogManagerView::cvsHandler( )
{
  return cvshandler;
}

SVNHandler * CatalogManagerView::svnHandler( )
{
   return svnhandler;
}

void CatalogManagerView::validateUsingTool( const KDataToolInfo &tool, const TQString& command )
{
    TQStringList result;
    CatManListItem *current = static_cast<CatManListItem *>(currentItem());
    if( current->isDir() )
    {
	const TQStringList s = current->allChildrenList(true);
	TQStringList::const_iterator it;
	for( it = s.constBegin() ; it != s.constEnd(); ++it )
	{
	    CatManListItem *item = _fileList[(*it)];
	    if( item && item->hasPo() ) result.append( item->package() );
	}
    }
    else
    {
	result.append( current->package() );
    }

    validate_internal( result, tool, command );
}

void CatalogManagerView::validateMarkedUsingTool( const KDataToolInfo &tool, const TQString& command )
{
    validate_internal( _markerList, tool, command );
}

void CatalogManagerView::validate_internal( const TQStringList& files, const KDataToolInfo &tool, const TQString& )
{
    if( files.isEmpty() ) return;

    KDataTool* t = tool.createTool();

    if( !t )
    {
	KMessageBox::error( this, i18n("Cannot instantiate a validation tool.\n"
	"Please check your installation."), i18n("Validation Tool Error") );
	return;
    }

    // setup options
    if( !_validateOptionsDlg )
    {
	_validateOptionsDlg = new KDialogBase( this, "validation options",
	    true, i18n("Validation Options"), KDialogBase::Ok|KDialogBase::Cancel);
	_validateOptions = new ValidationOptions(_validateOptionsDlg);
	_validateOptionsDlg->setMainWidget( _validateOptions );
	_validateOptions->resize( _validateOptions->sizeHint() );

	// setup stored values
	_validateOptions->markAsFuzzy->setChecked( _markAsFuzzy );
	_validateOptions->ignoreFuzzy->setChecked( _ignoreFuzzy );
    }

    if( _validateOptionsDlg->exec() != TQDialog::Accepted )
    {
	delete t;

	return;
    }

    if( !_validateDialog )
    {
	_validateDialog = new ValidateProgressDialog(_settings.ignoreURL, this);
	connect( _validateDialog, TQT_SIGNAL( errorDoubleClicked(const TQString,const int)),
	    this, TQT_SLOT(showError( const TQString, const int )));
    }

    _markAsFuzzy = _validateOptions->markAsFuzzy->isChecked();
    _ignoreFuzzy = _validateOptions->ignoreFuzzy->isChecked();

    _validateDialog->setMarkAsFuzzy(_markAsFuzzy);
    _validateDialog->setIgnoreFuzzy(_ignoreFuzzy);

    TQPtrList<CatManListItem> fileitems;

    for( TQValueListConstIterator<TQString> it=files.begin() ; it!=files.end() ; ++it )
    {
	CatManListItem* item=_fileList[ (*it) ];
	if( item ) {
	    fileitems.append(item);
	} else kdDebug(KBABEL_CATMAN) << "Cannot find the file item for " << (*it) << endl;
    }

    _validateDialog->validate(tool, fileitems);

    delete t;
}

void CatalogManagerView::showError( const TQString package, const int num )
{
    CatManListItem* item = _fileList[ package];
    if( !item )
    {
	kdWarning() << "Can not find error package: " << package << endl;
	return;
    }

    emit gotoFileEntry( item->poFile(), package, num );
}

void CatalogManagerView::updateMarkerList()
{
    TQStringList newMarkers;

    for( TQStringList::const_iterator it = _markerList.constBegin(); it != _markerList.constEnd(); ++it ) {
        if( _fileList[ (*it) ] )
	    newMarkers.append( (*it) );
    }

    _markerList = newMarkers;
}

void CatalogManagerView::slotValidPOCVSRepository( bool valid )
{
  m_validPOCVSRepository = valid;
  slotToggleCVSOrSVNColumn(valid);
  // set initial state for CVS menu entries
  emit selectedChanged(NEEDS_PO + NEEDS_PO_CVS * m_validPOCVSRepository);
}

void CatalogManagerView::slotValidPOSVNRepository( bool valid )
{
    m_validPOSVNRepository = valid;
    slotToggleCVSOrSVNColumn(valid);
  // set initial state for SVN menu entries
    emit selectedChanged(NEEDS_PO + NEEDS_PO_SVN * m_validPOSVNRepository);
}

void CatalogManagerView::slotValidPOTCVSRepository( bool valid )
{
    m_validPOTCVSRepository = valid;
  // set initial state for CVS menu entries
  // ### TODO: something missing here?
}

void CatalogManagerView::slotValidPOTSVNRepository( bool valid )
{
    m_validPOTSVNRepository = valid;
  // set initial state for SVN menu entries
  // ### TODO: something missing here?
}

void CatalogManagerView::slotToggleCVSOrSVNColumn( bool show )
{
#if 0
  toggleColumn( COL_CVS_OR_SVN, show );
#else
  // ### HACK
  toggleColumn( COL_CVS_OR_SVN, m_validPOCVSRepository || m_validPOSVNRepository );

#endif
}

void CatalogManagerView::toggleColumn( uint column, bool show )
{
  if ( show ) {
    setColumnWidthMode( column, Maximum );
    setColumnWidth( column, -1 );
    // ensure that the column heading is always fully visible
    setColumnText( column, COLTEXT(column));
  } else {
    setColumnWidthMode( column, Manual );
    setColumnWidth( column, 0 );
  }
}

#include "catalogmanagerview.moc"
// kate: space-indent on; indent-width 4; replace-tabs on;
