Logo Search packages:      
Sourcecode: kdesdk version File versions

catalog.cpp

/* ****************************************************************************
  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>

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

**************************************************************************** */
#include <qtextstream.h>
#include <qfile.h>
#include <qdir.h>
#include <qfileinfo.h>
#include <qregexp.h>
#include <qstring.h>
#include <qtextcodec.h>
#include <qdatetime.h>

#include <kconfig.h>
#include <kdatatool.h>
#include <kglobal.h>
#include <klibloader.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <kmimetype.h>
#include <kapplication.h>
#include <kio/netaccess.h>
#include <krfcdate.h>
#include <ktrader.h>
#include <kurl.h>

#include "kbprojectmanager.h"
#include "catalog.h"
#include "catalog_private.h"
#include "catalogitem.h"
#include "diff.h"
#include "findoptions.h"
#include "catalogview.h"
#include "editcmd.h"

#include "resources.h"
#include "version.h"

// from libgettext
extern "C"
{
#include "libgettext/fstrcmp.h"
}

using namespace KBabel;

00066 Catalog::Catalog(QObject* parent, const char* name, QString projectFile)
        : QObject(parent,name)
{
   d = new CatalogPrivate(ProjectManager::open(projectFile));
   readPreferences();
}

00073 Catalog::Catalog(const Catalog& c): QObject(c.parent(),c.name()
)
{
   kdFatal() << "Copy constructor of Catalog, please report how to reproduce to the authors" << endl;
}

Catalog::~Catalog()
{
    delete d;
}

00084 QStringList Catalog::msgid(uint index, const bool noNewlines) const
{
   if (  d->_entries.isEmpty() )
        return QString::null;
    uint max=d->_entries.count()-1;
   if(index > max)
      index=max;

   return d->_entries[index].msgid(noNewlines);
}

00095 QStringList Catalog::msgstr(uint index, const bool noNewlines) const
{
    if (  d->_entries.isEmpty() )
        return QString::null;

   uint max=d->_entries.count()-1;
   if(index > max)
      index=max;

   return d->_entries[index].msgstr(noNewlines);
}

00107 QString Catalog::comment(uint index) const
{
    if (  d->_entries.isEmpty() )
        return QString::null;
   uint max=d->_entries.count()-1;
   if(index > max)
      index=max;
   return d->_entries[index].comment();
}

00117 QString Catalog::context(uint index) const
{
    QString c = comment(index);

    QStringList lines = QStringList::split("\n",c);

    QString result;
    for( QStringList::Iterator it=lines.begin(); it!=lines.end(); it++)
    {
      if( (*it).startsWith( "#:") )
      {
          result+=(*it)+"\n";
      }
    }
    return result.stripWhiteSpace();
}

00134 CatalogItem Catalog::header() const
{
   return d->_header;
}

00139 QString Catalog::lastTranslator() const
{
    return headerInfo( d->_header ).lastTranslator;
}

00144 int Catalog::indexForMsgid(const QString& id) const
{
    int i=0;
      QValueVector<CatalogItem>::ConstIterator it = d->_entries.begin();

    while(it != d->_entries.end() && !((*it).msgid().contains(id)))
    {
        ++it;
        i++;
    }

    if(it == d->_entries.end())
        i=-1;

    return i;
}

00161 QStringList Catalog::tagList(uint index)
{
    if (  d->_entries.isEmpty() )
        return QStringList();

   uint max=d->_entries.count()-1;
   if(index > max)
      index=max;

   return d->_entries[index].tagList(*(d->_tagExtractor));
}


00174 QStringList Catalog::argList(uint index)
{
    if (  d->_entries.isEmpty() )
        return QStringList();

   uint max=d->_entries.count()-1;
   if(index > max)
      index=max;

   return d->_entries[index].argList(*(d->_argExtractor));
}


/*
bool Catalog::setMsgstr(uint index,QString msgstr)
{
    kdWarning() << "Catalog::setMsgstr()" << endl;

   bool untranslatedChanged=false;

   if(_entries[index].isUntranslated() && !msgstr.isEmpty())
   {
      _untransIndex.remove(index);
      untranslatedChanged=true;
   }
   else if(msgstr.isEmpty())
   {
      QValueList<uint>::Iterator it;

      // insert index in the right place in the list
      it = _untransIndex.begin();
      while(it != _untransIndex.end() && index > (*it))
      {
         ++it;
      }
      _untransIndex.insert(it,index);

      untranslatedChanged=true;
   }

   _entries[index].setMsgstr(msgstr);

   setModified(true);

   if(untranslatedChanged)
      emit signalNumberOfUntranslatedChanged(numberOfUntranslated());

   return untranslatedChanged;
}
*/

/*
bool Catalog::setComment(uint index,QString comment)
{
    kdWarning() << "Catalog::setComment()" << endl;
   bool fuzziesChanged=false;


   bool wasFuzzy=_entries[index].isFuzzy();

   _entries[index].setComment(comment);

   bool isFuzzy=_entries[index].isFuzzy();

   if(wasFuzzy && !isFuzzy)
   {
      _fuzzyIndex.remove(index);
      fuzziesChanged=true;
   }
   else if(isFuzzy)
   {
      QValueList<uint>::Iterator it;

      // insert index in the right place in the list
      it = _fuzzyIndex.begin();
      while(it != _fuzzyIndex.end() && index > (*it))
      {
         ++it;
      }
      _fuzzyIndex.insert(it,index);

      fuzziesChanged=true;
   }

   setModified(true);

   if(fuzziesChanged)
      emit signalNumberOfFuzziesChanged(numberOfFuzzies());


   return fuzziesChanged;
}
*/

00268 bool Catalog::setHeader(CatalogItem newHeader)
{
   if(newHeader.isValid())
   {
      d->_header=newHeader;
      setModified(true);

      emit signalHeaderChanged();

      return true;
   }

   return false;
}

00283 KURL Catalog::currentURL() const
{
   return d->_url;
}

00288 void Catalog::setCurrentURL(const KURL& url)
{
   d->_url=url;
}


00294 CatalogItem Catalog::updatedHeader(CatalogItem oldHeader, bool usePrefs) const
{
   QStringList headerList=oldHeader.msgstrAsList();
   QStringList commentList=QStringList::split('\n',oldHeader.comment());

   QStringList::Iterator it,ait;
   QString temp;
   bool found;
   if(!usePrefs || d->_saveSettings.updateLastTranslator)
   {
      found=false;

      temp="Last-Translator: "+d->_identitySettings.authorName;
      if(!d->_identitySettings.authorEmail.isEmpty())
      {
         temp+=(" <"+d->_identitySettings.authorEmail+">");
      }
      temp+="\\n";
      for( it = headerList.begin(); it != headerList.end(); ++it )
      {
         if((*it).contains(QRegExp("^ *Last-Translator:.*")))
         {
            (*it).replace(QRegExp("^ *Last-Translator:.*"),temp);
            found=true;
         }
       }
       if(!found)
       {
          headerList.append(temp);
       }
   }
   if(!usePrefs || d->_saveSettings.updateRevisionDate)
   {
      found=false;

      temp="PO-Revision-Date: "+dateTime()+"\\n";

      for( it = headerList.begin(); it != headerList.end(); ++it )
      {
         if((*it).contains(QRegExp("^ *PO-Revision-Date:.*")))
         {
            (*it).replace(QRegExp("^ *PO-Revision-Date:.*"),temp);
            found=true;
         }
       }
       if(!found)
       {
          headerList.append(temp);
       }
   }
   if(!usePrefs || d->_saveSettings.updateProject)
   {
      found=false;

      temp="Project-Id-Version: "+d->_saveSettings.projectString+"\\n";
      temp.replace( "@PACKAGE@", packageName());

      for( it = headerList.begin(); it != headerList.end(); ++it )
      {
         if((*it).contains(QRegExp("^ *Project-Id-Version:.*")))
         {
            (*it).replace(QRegExp("^ *Project-Id-Version:.*"),temp);
            found=true;
         }
       }
       if(!found)
       {
          headerList.append(temp);
       }
   }
   if(!usePrefs || d->_saveSettings.updateLanguageTeam)
   {
      found=false;

      temp="Language-Team: "+d->_identitySettings.languageName;
      if(!d->_identitySettings.mailingList.isEmpty())
      {
         temp+=(" <"+d->_identitySettings.mailingList+">");
      }
      temp+="\\n";
      for( it = headerList.begin(); it != headerList.end(); ++it )
      {
         if((*it).contains(QRegExp("^ *Language-Team:.*")))
         {
            (*it).replace(QRegExp("^ *Language-Team:.*"),temp);
            found=true;
         }
       }
       if(!found)
       {
          headerList.append(temp);
       }
   }
   if(!usePrefs || d->_saveSettings.updateCharset)
   {
      found=false;

        QString encodingStr;
      if(d->_saveSettings.useOldEncoding && d->fileCodec)
      {
              encodingStr = charsetString(d->fileCodec);
      }
      else
      {
          encodingStr=charsetString(d->_saveSettings.encoding);
      }
      temp="Content-Type: text/plain; charset="+encodingStr+"\\n";
      QString charsettemp="; charset="+encodingStr+"\\n";

      for( it = headerList.begin(); it != headerList.end(); ++it )
      {
         if((*it).contains(QRegExp("^ *Content-Type:.*;\\s*charset=")))
         {
            (*it).replace(QRegExp(";\\s*charset\\s*=\\s*[^\\\"\\n]+"),charsettemp);
            found=true;
         }
       }
       if(!found)
       {
          headerList.append(temp);
       }
   }
   if(!usePrefs || d->_saveSettings.updateEncoding)
   {
      found=false;

      temp="Content-Transfer-Encoding: 8bit\\n";

      for( it = headerList.begin(); it != headerList.end(); ++it )
      {
         if((*it).contains(QRegExp("^ *Content-Transfer-Encoding:.*")))
         {
            (*it).replace(QRegExp("^ *Content-Transfer-Encoding:.*"),temp);
            found=true;
         }
       }
       if(!found)
       {
          headerList.append(temp);
       }
   }

   temp="X-Generator: KBabel %1\\n";
   temp=temp.arg(VERSION);
   found=false;

   for( it = headerList.begin(); it != headerList.end(); ++it )
   {
      if((*it).contains(QRegExp("^ *X-Generator:.*")))
      {
         (*it).replace(QRegExp("^ *X-Generator:.*"),temp);
         found=true;
      }
    }
    if(!found)
    {
       headerList.append(temp);
    }
    
   // ensure MIME-Version header
   temp="MIME-Version: 1.0\\n";
   found=false;
   for( it = headerList.begin(); it != headerList.end(); ++it )
   {
      if((*it).contains(QRegExp("^ *MIME-Version:")))
      {
          found=true;
          break;
      }
   }
   if( !found )
   {
      headerList.append(temp);
   }


   temp="Plural-Forms: %1\\n";
   temp=temp.arg(d->_identitySettings.gnuPluralFormHeader);
   found=false;

   // update plural form header
   if( !d->_identitySettings.gnuPluralFormHeader.isEmpty() )
   {
      for( it = headerList.begin(); it != headerList.end(); ++it )
      {
          if((*it).contains(QRegExp("^ *Plural-Forms:")))
          {
            (*it).replace(QRegExp("^ *Plural-Forms:.*"),temp);
            found=true;
          }
      }
    }
    if(!found && !d->_identitySettings.gnuPluralFormHeader.isEmpty() )
    {
       headerList.append(temp);
    }

   QString msgstr;
   for( it = headerList.begin(); it != headerList.end(); ++it )
   {
      msgstr+=("\n"+(*it));
   }

   msgstr.remove(0,1);// remove first newline

   oldHeader.setMsgstr(msgstr);

   //comment = description, copyrights
   if(!usePrefs || (d->_saveSettings.FSFCopyright != NoChange))
   {
      found=false;

      for( it = commentList.begin(); it != commentList.end(); ++it )
      {
         if((*it).contains(QRegExp("^# *Copyright \\(C\\).*Free Software Foundation, Inc")))
         {
            found=true;
          break;
         }
       }
       if(found)
       {
          if((*it).contains(QRegExp("^# *Copyright \\(C\\) YEAR Free Software Foundation, Inc\\.")))
          {
            //template string
            if(d->_saveSettings.FSFCopyright == Remove) (*it).replace(" YEAR Free Software Foundation, Inc","");
            else (*it).replace("YEAR", QDate::currentDate().toString("yyyy"));
          } else
          if( d->_saveSettings.FSFCopyright == Update )
          {
                //update years
                QString cy = QDate::currentDate().toString("yyyy");
                if( !(*it).contains( QRegExp(cy)) ) // is the year already included?
                {
                  int index = (*it).findRev( QRegExp("[\\d]+[\\d\\-, ]*") );
                  if( index == -1 )
                  {
                      KMessageBox::information(0,i18n("Free Software Foundation Copyright does not contain any year. "
                      "It will not be updated."));
                  } else {
                      (*it).insert(index+1, QString(", ")+cy);
                  }
                }
          }
      }
   }

   if(!usePrefs || d->_saveSettings.updateDescription)
   {
      temp="# "+d->_saveSettings.descriptionString;
      temp.replace( "@PACKAGE@", packageName());
      temp.replace( "@LANGUAGE@", d->_identitySettings.languageName);

      found=false;
      bool foundTemplate=false;

      for( it = commentList.begin(); it != commentList.end(); ++it )
      {
         if((*it).contains(QRegExp("^"+temp+"$")))
         {
            found=true;
         }
       if((*it).contains(QRegExp("^# SOME DESCRIPTIVE TITLE.$")))
       {
          ait = it;
          foundTemplate = true;
       }
       }
       if(foundTemplate) commentList.remove(ait);
       if(!found) commentList.prepend(temp);
   }

   if(!usePrefs || d->_saveSettings.updateTranslatorCopyright)
   {
      QStringList foundAuthors;

      temp="# "+d->_identitySettings.authorName;
      if(!d->_identitySettings.authorEmail.isEmpty())
      {
         temp+=(" <"+d->_identitySettings.authorEmail+">");
      }
      temp+=", "+QDate::currentDate().toString("yyyy")+".";

      for( it = commentList.begin(); it != commentList.end(); ++it )
      {
         if((*it).contains(QRegExp("^#.*<.+@.+>,\\s*([\\d]+[\\d\\-, ]*)|(YEAR)"))) // email address followed by year
         {
          foundAuthors.append( (*it) );
         }
       }

       for( it = foundAuthors.begin() ; it != foundAuthors.end() ; ++it )
          commentList.remove( (*it) );

      it=qFind(foundAuthors.begin(), foundAuthors.end(), QString("# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR."));
      if( it != foundAuthors.end() ) foundAuthors.remove( it );

       if(foundAuthors.size()>0)
       {
          found = false;
        bool foundAuthor = false;

        QString cy = QDate::currentDate().toString("yyyy");

        for( it = foundAuthors.begin() ; it!=foundAuthors.end(); ++it )
        {
            if( (*it).contains(QRegExp(d->_identitySettings.authorName+".*"
                +d->_identitySettings.authorEmail)))
            {
                foundAuthor = true;
                if( (*it).contains(QRegExp(cy))) found = true;
                else ait = it;
            }
         }
         if( !found )
            if( !foundAuthor ) foundAuthors.append(temp);
            else
            {
                //update years
                int index = (*ait).findRev( QRegExp("[\\d]+[\\d\\-, ]*") );
                if( index == -1 ) (*ait)+=", "+cy;
                else (*ait).insert(index+1, QString(", ")+cy);
            }

      }
      else foundAuthors.append(temp);
      it=commentList.end();
      do
          --it;
      while( (*it).contains( QRegExp( "^#\\s*$")) || (*it).contains( QRegExp( "^#[:,\\.]")));
      it++;
      for( ait = foundAuthors.begin() ; ait != foundAuthors.end() ; ++ait )
      {
          QString s = (*ait);

          // ensure dot at the end of copyright
          if( !s.endsWith(".") ) s += ".";
          commentList.insert(it, s);
      }
   }

   QString comment;
   for( it = commentList.begin(); it != commentList.end(); ++it )
   {
      comment+=("\n"+(*it));
   }

   comment.remove(0,1);// remove first newline

   oldHeader.setComment(comment);

   return oldHeader;
}

00648 void Catalog::setFuzzy(uint index, bool on)
{
    if (  d->_entries.isEmpty() )
        return;

   uint max=d->_entries.count()-1;
   if(index > max)
       return;

   if(d->_entries[index].isFuzzy() != on)
   {
      applyBeginCommand( index, Comment, 0 );

      QPtrList<EditCommand> editList;
      if(on)
      {
          editList=d->_entries[index].addFuzzy(false);
      }
      else
      {
          editList=d->_entries[index].removeFuzzy(false);
          d->_fuzzyIndex.remove(index);
      }

      for ( EditCommand* cmd=editList.first(); cmd != 0; cmd=editList.next() )
      {
         cmd->setIndex(index);
         applyEditCommand(cmd,0);
      }

      setModified(true);

      applyEndCommand( index, Comment, 0 );

      emit signalNumberOfFuzziesChanged(numberOfFuzzies());
   }

}

00687 void Catalog::removeFuzzyStatus(uint index)
{
    setFuzzy(index,false);
}


00693 void Catalog::setModified(bool flag)
{
    bool old=d->_modified;
    d->_modified=flag;

    if(old!=d->_modified)
       emit signalModified(flag);
}


00703 QString Catalog::packageName() const
{
    if( !d->_packageName.isNull() ) return d->_packageName;

    QString package=d->_url.fileName();

    int index=package.find(QRegExp("(\\."+d->_identitySettings.languageCode+")?\\.pot?$"));

    if(index>0)
      package=package.left(index);

    return package;
}

00717 void Catalog::setPackage(const QString& package )
{
    int pos=package.findRev("/");
    if( pos<0 )
    {
      d->_packageDir = "";
      d->_packageName = package;
      d->_packageName.replace( QRegExp("^/+"),"");
    }
    else
    {
      d->_packageDir = package.left(pos);
      if( !d->_packageDir.endsWith("/") ) d->_packageDir+="/";
      d->_packageName = package.right(package.length()-pos);
      d->_packageName.replace( QRegExp("^/+"),"");
    }
}

00735 QString Catalog::packageDir() const
{
    QString result;
    if( !d->_packageDir.isNull() ) result=d->_packageDir;
    else result=d->_url.directory(false);

    return result;
}

00744 QString Catalog::encoding() const
{
    QString encodingStr;
    if(d->_saveSettings.useOldEncoding && d->fileCodec)
    {
          encodingStr = charsetString(d->fileCodec);
    }
    else
    {
        encodingStr= charsetString(d->_saveSettings.encoding);
    }

    return encodingStr;
}

00759 ConversionStatus Catalog::openURL(const KURL& url, const QString& package)
{
   QString target;
   ConversionStatus error = OK;

   if(KIO::NetAccess::download(url, target, NULL))
   {
       CatalogImportPlugin* filter=0;

       // gimme plugin for this MIME type
       KMimeType::Ptr mime = KMimeType::findByURL( url, 0, true );
       kdDebug() << "Found mimetype: " << mime->name() <<  endl;
       KTrader::OfferList offers = KTrader::self()->query("KBabelFilter", "('"+mime->name()+"' in [X-KDE-Import])");
       KService::Ptr ptr = offers.first();

       // we have no offer for this MIME type
       if( !ptr )
       {
          kdDebug(KBABEL) << "No plugin for this type, will try PO" << endl;
          offers = KTrader::self()->query("KBabelFilter", "('application/x-gettext' in [X-KDE-Import])");
          ptr = offers.first();
          if( !ptr )
          {
            kdDebug(KBABEL) << "No plugin for PO files, giving up" << endl;
            KIO::NetAccess::removeTempFile(target);
            return NO_PLUGIN;
          }
       }

       // try to load the library, if unsuccesfull, we have an installation problem
       KLibFactory *factory = KLibLoader::self()->factory( ptr->library().local8Bit() );
       if (!factory)
       {
          kdDebug(KBABEL) << "No factory" << endl;
          KIO::NetAccess::removeTempFile(target);
          return OS_ERROR;
       }

       // create the filter
       filter = static_cast<CatalogImportPlugin*>(factory->create(0, 0));

       // provide progress bar indication
      connect( filter, SIGNAL( signalResetProgressBar(QString,int) ),
          this, SIGNAL( signalResetProgressBar(QString,int) ));
      connect( filter, SIGNAL( signalProgress(int) ),
          this, SIGNAL( signalProgress(int) ));
      connect( filter, SIGNAL( signalClearProgressBar() ),
          this, SIGNAL( signalClearProgressBar() ));

      connect( this, SIGNAL( signalStopActivity() ),
          filter, SLOT( stop() ));

      // load in the file (target is always local)
      d->_active = true;
      kdDebug(KBABEL) << "openURL active" << endl;
        error = filter->open(target,mime->name(),this);
      // we should be not freed yet
      d->_active = false;
      kdDebug(KBABEL) << "openURL not active" << endl;
      if( error == STOPPED )
      {
          delete filter;
          return STOPPED;
      }

      if( error == OK || error == RECOVERED_PARSE_ERROR || error == HEADER_ERROR )
      {
          setModified(false);
          d->_url=url;

          if( package.isNull() )
          {
            d->_packageName=QString::null;
            d->_packageDir=QString::null;
          }
          else setPackage(package);

          emit signalFileOpened(d->_readOnly);
          emit signalNumberOfFuzziesChanged(numberOfFuzzies());
          emit signalNumberOfUntranslatedChanged(numberOfUntranslated());
          emit signalTotalNumberChanged(numberOfEntries());
      }

      delete filter;

        return error;
   }
   else
   {
      return OS_ERROR;
   }
}

00852 ConversionStatus Catalog::openURL(const KURL& openUrl, const KURL& saveURL, const QString& package)
{
   QString target;
   ConversionStatus error = OK;

   if(KIO::NetAccess::download(openUrl, target, NULL))
   {
       CatalogImportPlugin* filter=0;

       // gimme plugin for this MIME type
       KMimeType::Ptr mime = KMimeType::findByURL( openUrl, 0, true );
       KTrader::OfferList offers = KTrader::self()->query("KBabelFilter", "('"+mime->name()+"' in [X-KDE-Import])");
       KService::Ptr ptr = offers.first();

       // we have no offer for this MIME type
       if( !ptr )
       {
          kdDebug(KBABEL) << "No plugin for this type" << endl;
          KIO::NetAccess::removeTempFile(target);
          return NO_PLUGIN;
       }

       // try to load the library, if unsuccesfull, we have an installation problem
       KLibFactory *factory = KLibLoader::self()->factory( ptr->library().local8Bit() );
       if (!factory)
       {
          kdDebug(KBABEL) << "No factory" << endl;
          KIO::NetAccess::removeTempFile(target);
          return OS_ERROR;
       }

       // create the filter
       filter = static_cast<CatalogImportPlugin*>(factory->create(0, 0));

       // provide progress bar indication
      connect( filter, SIGNAL( signalResetProgressBar(QString,int) ),
          this, SIGNAL( signalResetProgressBar(QString,int) ));
      connect( filter, SIGNAL( signalProgress(int) ),
          this, SIGNAL( signalProgress(int) ));
      connect( filter, SIGNAL( signalClearProgressBar() ),
          this, SIGNAL( signalClearProgressBar() ));

      connect( this, SIGNAL( signalStopActivity() ),
          filter, SLOT( stop() ));

      // load in the file (target is always local)
      d->_active = true;
      kdDebug(KBABEL) << "openURL - template active" << endl;
        error = filter->open(target,mime->name(),this);
      // we should be not freed yet
      kdDebug(KBABEL) << "openURL - template not active" << endl;
      d->_active = false;
      if( error == STOPPED )
      {
          delete filter;
          return STOPPED;
      }

      if( error == OK )
      {
           setModified(false);
           d->_url = saveURL;
         if( package.isNull() )
         {
            d->_packageName=QString::null;
            d->_packageDir=QString::null;
         }
         else setPackage(package);

           emit signalFileOpened(d->_readOnly);
           emit signalNumberOfFuzziesChanged(numberOfFuzzies());
           emit signalNumberOfUntranslatedChanged(numberOfUntranslated());
           emit signalTotalNumberChanged(numberOfEntries());
        }

      delete filter;

        // and remove the temp file
        KIO::NetAccess::removeTempFile(target);

        return error;
   }
   else
   {
      return OS_ERROR;
   }
}

00940 Msgfmt::Status Catalog::checkSyntax(QString& output, bool clearErrors)
{
   if( !d->_mimeTypes.contains( "application/x-gettext" ) )
      return Msgfmt::Unsupported;

   QString filename;
   bool tempFileUsed=false;

   if(d->_url.isLocalFile() && !isModified())
   {
      filename=d->_url.path(0);
   }
   else
   {
      tempFileUsed=true;
      filename=saveTempFile();
   }

   Msgfmt msgfmt;
   Msgfmt::Status result = msgfmt.checkSyntax( filename , output, pluralFormType() != KDESpecific );

   if( clearErrors) clearErrorList();

   if( result==Msgfmt::SyntaxError )
   {
      int currentIndex=-1;
      int currentLine=0;

      if( !d->_header.msgstr().isEmpty() )
         currentLine=d->_header.totalLines()+1;

      QStringList lines = QStringList::split("\n",output);
      for ( QStringList::Iterator it = lines.begin(); it != lines.end(); ++it )
      {
         if( (*it).contains(QRegExp("^.+:\\d+:")) )
         {
            int begin=(*it).find(":",0)+1;
            int end=(*it).find(":",begin);

            QString line=(*it).mid(begin,end-begin);

            while( line.toInt() > currentLine )
            {
               currentIndex++;
               currentLine += ( d->_entries[currentIndex].totalLines() + 1 );
            }

          if( currentIndex == -1 )
          {
            // header error
            result = Msgfmt::HeaderError;
            continue;
          }

            if( !d->_errorIndex.contains(currentIndex) )
            {
               d->_errorIndex.append(currentIndex);
                     d->_entries[currentIndex].setSyntaxError(true);
            }
         }
      }
   }

   if(tempFileUsed)
      QFile::remove(filename);

   return result;
}

01009 void Catalog::clearErrorList()
{
      QValueList<uint>::Iterator it;
      for(it = d->_errorIndex.begin(); it != d->_errorIndex.end(); ++it)
      {
            d->_entries[(*it)].setSyntaxError(false);
            d->_entries[(*it)].clearErrors();
      }

      d->_errorIndex.clear();
}

01021 void Catalog::removeFromErrorList(uint index)
{
      if(d->_errorIndex.contains(index))
      {
            d->_errorIndex.remove(index);
            d->_entries[index].setSyntaxError(false);
            d->_entries[index].clearErrors();
      }
}

01031 QStringList Catalog::itemStatus(uint index, bool recheck, QPtrList<KDataTool> whatToCheck)
{
    if (  d->_entries.isEmpty() )
        return QStringList();

      uint max=d->_entries.count()-1;
      if(index > max)
            index=max;

      CatalogItem& item = d->_entries[index];

      if(recheck)
      {
          for( KDataTool* t = whatToCheck.first(); t ; t=whatToCheck.next() )
          {
            t->run("validate", (void*)(&item), "CatalogItem", "application/x-kbabel-catalogitem" );
          }
      }

      return item.errors();
}

01053 QStringList Catalog::itemStatus(uint index)
{
       if (  d->_entries.isEmpty() )
        return QStringList();

      uint max=d->_entries.count()-1;
      if(index > max)
            index=max;

      CatalogItem& item = d->_entries[index];

      return item.errors();
}

01067 bool Catalog::checkUsingTool(KDataTool* tool, bool clearErrors)
{
      if(clearErrors)
            clearErrorList();

      kdDebug(KBABEL) << "checkUsingTool active" << endl;
      d->_active=true;
      d->_stop=false;
      connect( this, SIGNAL( signalStopActivity() ), this, SLOT( stopInternal() ));

      int index = 0;
      bool hasErrors=false;

      emit signalResetProgressBar(i18n("validating file"),100);

      for ( QValueVector<CatalogItem>::Iterator it = d->_entries.begin();
                              it != d->_entries.end(); ++it, index++ )
      {
          if( !tool->run( "validate", (void*)(&(*it)), "CatalogItem", "application/x-kbabel-catalogitem" ))
          {
            if( !d->_errorIndex.contains(index) )
            {
                  d->_errorIndex.append(index);
                  hasErrors=true;
            }
          }
          if( d->_stop ) break;
          emit signalProgress((index*100)/d->_entries.count());
      }

      if( hasErrors && !clearErrors ) qHeapSort(d->_errorIndex);

      kdDebug(KBABEL) << "checkUsingTool not active" << endl;
      d->_active=false;
      d->_stop=false;
      disconnect( this, SIGNAL( signalStopActivity() ), this, SLOT( stopInternal() ));

      emit signalClearProgressBar();

      return !hasErrors;
}

01109 void Catalog::modifyUsingTool(KDataTool* tool, const QString& command)
{
    kdDebug(KBABEL) << "modifyUsingTool active" << endl;
    d->_active=true;
    d->_stop=false;
    connect( this, SIGNAL( signalStopActivity() ), this, SLOT( stopInternal() ));

    int index = 0;
    bool modified = false;

    emit signalResetProgressBar(i18n("applying tool"),100);

    for ( QValueVector<CatalogItem>::Iterator it = d->_entries.begin();
                        it != d->_entries.end(); ++it, index++ )
    {
      CatalogItem dummyItem( *it );

      tool->run( command, (void*)(&dummyItem), "CatalogItem", "application/x-kbabel-catalogitem" );

      if( (*it).msgstr() != dummyItem.msgstr() || (*it).comment() != dummyItem.comment() )
      {
          if( !modified )
          {
            applyBeginCommand(0,Msgstr,0);
            modified = true;
          }

          if( (*it).msgstr() != dummyItem.msgstr() )
          {
            uint in = 0; // number of current lural form
            // go over all plural forms and test, which changed
            for ( QStringList::Iterator itorig = (*it).msgstr().begin()
                  , itchanged = dummyItem.msgstr().begin()
                  ; itorig != (*it).msgstr().end()
                  ; ++itorig, ++itchanged) {
                if( (*itorig) != (*itchanged) )
                {
                  EditCommand* cmd = new DelTextCmd(0,(*itorig),index);
                  cmd->setPart(Msgstr);
                  applyEditCommand(cmd,0);
                  cmd = new InsTextCmd(0,(*itchanged),index);
                  cmd->setPart(Msgstr);
                  applyEditCommand(cmd,0);
                }
                in++;
            }
          }

          if( (*it).comment() != dummyItem.comment() )
          {
            EditCommand* cmd = new DelTextCmd(0,(*it).comment(),0);
            cmd->setPart(Comment);
            cmd->setIndex(index);
            applyEditCommand(cmd,0);
            cmd = new InsTextCmd(0,dummyItem.comment(),0);
            cmd->setPart(Comment);
            cmd->setIndex(index);
            applyEditCommand(cmd,0);
            kdDebug(KBABEL) << "DummyItem comment is " << dummyItem.comment() << endl;
          }
      }

      if( d->_stop ) break;
      emit signalProgress((index*100)/d->_entries.count());
    }

    if( modified ) applyEndCommand(0, Msgstr, 0);

    kdDebug(KBABEL) << "modifyUsingTool not active" << endl;
    d->_active=false;
    d->_stop=false;
    disconnect( this, SIGNAL( signalStopActivity() ), this, SLOT( stopInternal() ));

    emit signalClearProgressBar();
}

01185 void Catalog::clear()
{
    d->_entries.clear();
    d->_url=KURL();
    d->_obsoleteEntries.clear();

    if(d->_undoList.count() > 0)
       emit signalUndoAvailable(false);
    if(d->_redoList.count() > 0)
       emit signalRedoAvailable(false);

    d->_undoList.clear();
    d->_redoList.clear();

    d->msgidDiffList.clear();
    d->msgstr2MsgidDiffList.clear();
    d->diffCache.clear();
}


01205 uint Catalog::numberOfEntries() const
{
   return d->_entries.count();
}

01210 uint Catalog::numberOfFuzzies() const
{
   return d->_fuzzyIndex.count();
}

01215 uint Catalog::numberOfUntranslated() const
{
   return d->_untransIndex.count();
}


bool Catalog::hasFuzzyInFront(uint index)  const
{
   if(findPrevInList(d->_fuzzyIndex,index)>=0)
   {
      return true;
   }

   return false;
}

bool Catalog::hasFuzzyAfterwards(uint index) const
{
   if(findNextInList(d->_fuzzyIndex,index)>=0)
   {
      return true;
   }

   return false;
}

bool Catalog::hasUntranslatedInFront(uint index) const
{
   if(findPrevInList(d->_untransIndex,index)>=0)
   {
      return true;
   }

   return false;
}

bool Catalog::hasUntranslatedAfterwards(uint index) const
{
   if(findNextInList(d->_untransIndex,index)>=0)
   {
      return true;
   }

   return false;
}

bool Catalog::hasErrorInFront(uint index)  const
{
   if(findPrevInList(d->_errorIndex,index)>=0)
   {
      return true;
   }

   return false;
}

bool Catalog::hasErrorAfterwards(uint index) const
{
   if(findNextInList(d->_errorIndex,index)>=0)
   {
      return true;
   }

   return false;
}

bool Catalog::isFuzzy(uint index) const
{
    if (  d->_entries.isEmpty() )
        return false;

   if(index > numberOfEntries())
      return false;

   return d->_entries[index].isFuzzy();
}


bool Catalog::isUntranslated(uint index) const
{
    if (  d->_entries.isEmpty() )
        return false;

   if(index > numberOfEntries())
      return false;

   return d->_entries[index].isUntranslated();
}

01304 bool Catalog::hasError(uint index, DocPosition& pos) const
{
    if( d->_errorIndex.contains(index) )
    {
      pos.item=index;
      pos.form=0;
      return true;
    }
   return false;
}

01315 PluralFormType Catalog::pluralForm(uint index) const
{
    if (  d->_entries.isEmpty() )
        return NoPluralForm;

    if(index > numberOfEntries())
        return NoPluralForm;

    return static_cast<PluralFormType>(d->_entries[index].pluralForm());
}

01326 PluralFormType Catalog::pluralFormType() const
{
    if (  d->_entries.isEmpty() )
        return NoPluralForm;

    for( uint i = 0 ; i < numberOfEntries(); i++)
    {
      if( d->_entries[i].pluralForm() != NoPluralForm )
          return d->_entries[i].pluralForm();
    }
    
    return NoPluralForm;
}

01340 int Catalog::nextFuzzy(uint startIndex, DocPosition& pos) const
{
   pos.item=findNextInList(d->_fuzzyIndex,startIndex);
   pos.form=0;
   return pos.item;
}

01347 int Catalog::prevFuzzy(uint startIndex, DocPosition& pos) const
{
   pos.item=findPrevInList(d->_fuzzyIndex,startIndex);
   pos.form=0;
   return pos.item;
}

01354 int Catalog::nextUntranslated(uint startIndex, DocPosition& pos) const
{
   pos.item=findNextInList(d->_untransIndex,startIndex);
   pos.form=0;
   return pos.item;
}

01361 int Catalog::prevUntranslated(uint startIndex, DocPosition& pos) const
{
   pos.item=findPrevInList(d->_untransIndex,startIndex);
   pos.form=0;
   return pos.item;
}


01369 int Catalog::nextError(uint startIndex, DocPosition& pos) const
{
   pos.item=findNextInList(d->_errorIndex,startIndex);
   pos.form=0;
   return pos.item;
}

01376 int Catalog::prevError(uint startIndex, DocPosition& pos) const
{
   pos.item=findPrevInList(d->_errorIndex,startIndex);
   pos.form=0;
   return pos.item;
}


01384 void Catalog::registerView(CatalogView* view)
{
   if(d->_views.containsRef(view)==0)
   {
      d->_views.append(view);
   }
}


void Catalog::removeView(CatalogView* view)
{
   d->_views.removeRef(view);
}


01399 void Catalog::updateViews(EditCommand* cmd,CatalogView* view2exclude)
{
    CatalogView* view;
    for ( view=d->_views.first(); view != 0; view=d->_views.next())
    {
       if(view!=view2exclude)
       {
          view->update(cmd);
       }
    }
}



bool Catalog::hasView() const
{
    if(d->_views.count()==0)
           return false;

    return true;
}

bool Catalog::isLastView() const
{
    if(d->_views.count()<=1)
           return true;

    return false;
}


void Catalog::useProject(Project::Ptr project)
{
    d->_project->config()->sync();
    d->_project = project;
    readPreferences();
    emit signalSettingsChanged(d->_saveSettings);
    emit signalSettingsChanged(d->_identitySettings);
    emit signalSettingsChanged(d->_miscSettings);
    emit signalSettingsChanged(d->_tagSettings);
}

Project::Ptr Catalog::project() const
{
    return d->_project;
}

01446 void Catalog::readPreferences()
{
    d->_saveSettings = d->_project->saveSettings();
    d->_identitySettings = d->_project->identitySettings();

    getNumberOfPluralForms();
    
    d->_miscSettings = d->_project->miscSettings();

    d->_project->config()->setGroup("Tags");
    d->_tagSettings.tagExpressions=d->_project->config()->readListEntry("TagExpressions");
    if( d->_tagSettings.tagExpressions.empty() ) d->_tagSettings.tagExpressions = Defaults::Tag::tagExpressions();
    d->_tagExtractor->setRegExpList(d->_tagSettings.tagExpressions) ;

    d->_tagSettings.argExpressions=d->_project->config()->readListEntry("ArgExpressions");
    if( d->_tagSettings.argExpressions.empty() ) d->_tagSettings.argExpressions = Defaults::Tag::argExpressions();
    d->_argExtractor->setRegExpList(d->_tagSettings.argExpressions) ;
}

01465 void Catalog::savePreferences()
{
   KConfigGroupSaver groupSaver(d->_project->config(),"Header");

   d->_project->setSettings(d->_saveSettings);
   d->_project->setSettings(d->_identitySettings);
   d->_project->setSettings(d->_miscSettings);

   d->_project->config()->setGroup("Tags");

   d->_project->config()->writeEntry( "TagExpressions", d->_tagSettings.tagExpressions );
   d->_project->config()->writeEntry( "ArgExpressions", d->_tagSettings.argExpressions );

   d->_project->config()->sync();
}

IdentitySettings Catalog::identitySettings() const
{
    return d->_identitySettings;

}

SaveSettings Catalog::saveSettings() const
{
    return d->_saveSettings;

}

MiscSettings Catalog::miscSettings() const
{
    return d->_miscSettings;

}

TagSettings Catalog::tagSettings() const
{
    return d->_tagSettings;

}

01505 bool Catalog::isGeneratedFromDocbook() const
{
    return d->_generatedFromDocbook;
}

01510 QString Catalog::package() const
{
    return packageDir()+packageName();
}

01515 bool Catalog::isReadOnly() const
{
    return d->_readOnly;
}

01520 void Catalog::setSettings(SaveSettings settings)
{
   d->_saveSettings=settings;

   emit signalSettingsChanged(settings);
}

01527 void Catalog::setSettings(IdentitySettings settings)
{
   QString oldLanguageCode = d->_identitySettings.languageCode;
   int oldForms =d->_identitySettings.numberOfPluralForms;


   d->_identitySettings=settings;

   if(oldLanguageCode != d->_identitySettings.languageCode)
   {
       getNumberOfPluralForms();
   }

    if(oldForms != d->_identitySettings.numberOfPluralForms)
    {
        getNumberOfPluralForms();
    }

   emit signalSettingsChanged(settings);
}

01548 void Catalog::setSettings(MiscSettings settings)
{
      d->_miscSettings=settings;

      emit signalSettingsChanged(settings);
}

01555 void Catalog::setSettings(TagSettings settings)
{
      d->_tagSettings=settings;

      emit signalSettingsChanged(settings);
}

01562 void Catalog::generateIndexLists()
{
   d->_fuzzyIndex.clear();
   d->_untransIndex.clear();
   clearErrorList();

   uint counter=0;
   for ( QValueVector<CatalogItem>::Iterator it = d->_entries.begin(); it != d->_entries.end(); ++it )
   {
       if((*it).isUntranslated())
       {
          d->_untransIndex.append(counter);
       }
       else if((*it).isFuzzy())
       {
          d->_fuzzyIndex.append(counter);
       }

       counter++;
   }

}

01585 int Catalog::findNextInList(const QValueList<uint>& list,uint index) const
{
    QValueList<uint>::ConstIterator it;

    int nextIndex=-1;

    // find index in List
    it=list.find(index);

    // if the given index is found in the list and not the last entry
    // in the list, return the next listentry
    if(it!=list.end() && it!=list.fromLast())
    {
       ++it;
       return (*it);
    }

    // if the index is not in the list, search the index in the list, that
    // is the nearest to the given index
    for( it = list.begin(); it != list.end(); ++it )
    {
       if((*it) > index)
       {
          nextIndex=(*it);
          break;
       }
    }


    return nextIndex;
}

01617 int Catalog::findPrevInList(const QValueList<uint>& list,uint index) const
{
    QValueList<uint>::ConstIterator it;

    int prevIndex=-1;

    it=list.find(index);

    // if the given index is found in the list and not the last entry
    // in the list, return the next listentry
    if(it!=list.end() && it!=list.begin())
    {
       --it;
       return (*it);
    }


    // if the index is not in the list, search the index in the list, that
    // is the nearest to the given index
    for( it = list.fromLast(); it != list.end(); --it )
    {
       if((*it) < index)
       {
          prevIndex=(*it);
          break;
       }
    }


    return prevIndex;
}


01650 QString Catalog::dateTime() const
{
    QString dateTimeString;
    QDate date=QDate::currentDate();
    QTime time=QTime::currentTime();

    switch(d->_saveSettings.dateFormat)
    {
       case Qt::LocalDate:
       {
          return KGlobal::locale()->formatDateTime(QDateTime::currentDateTime());
       }
       case Qt::ISODate:
          dateTimeString = Defaults::Save::customDateFormat();
          break;
       case Qt::TextDate:
          dateTimeString = d->_saveSettings.customDateFormat;
          break;
    }

    // the year
    dateTimeString.replace( "%Y", QString::number( date.year() ) );
    dateTimeString.replace( "%y", QString::number( date.year() ).right(2) );

    // the month
    if(date.month()<10)
    {
       dateTimeString.replace( "%m", "0"+QString::number( date.month() ) );
    }
    else
    {
       dateTimeString.replace( "%m", QString::number( date.month() ) );
    }

    dateTimeString.replace( "%f", QString::number( date.month() ) );

    dateTimeString.replace( "%b", date.longMonthName(date.month()) );
    dateTimeString.replace( "%h", date.longMonthName(date.month()) );

    // the day
    dateTimeString.replace( "%j", QString::number( date.dayOfYear() ) );
    dateTimeString.replace( "%e", QString::number( date.day() ) );
    if(date.day() < 10)
    {
       dateTimeString.replace( "%d", "0"+QString::number( date.day() ) );
    }
    else
    {
       dateTimeString.replace( "%d", QString::number( date.day() ) );
    }

    dateTimeString.replace( "%a", date.longDayName( date.dayOfWeek() ) );


    // hour
    dateTimeString.replace( "%k", QString::number( time.hour() ) );

    if(time.hour() < 10)
    {
       dateTimeString.replace( "%H", "0"+QString::number( time.hour() ) );
    }
    else
    {
       dateTimeString.replace( "%H", QString::number( time.hour() ) );
    }

    QString zone;
    int hour;
    if( time.hour() > 12 )
    {
       zone="PM";
       hour=time.hour()-12;
    }
    else
    {
       zone="AM";
       hour=time.hour();
    }

    dateTimeString.replace( "%I", QString::number( hour ) );

    if(hour < 10)
    {
       dateTimeString.replace( "%i", "0"+QString::number( hour ) );
    }
    else
    {
       dateTimeString.replace( "%i", QString::number( hour ) );
    }

    dateTimeString.replace( "%p", zone );

    // minutes
    if(time.minute() < 10)
    {
       dateTimeString.replace( "%M", "0"+QString::number( time.minute() ) );
    }
    else
    {
       dateTimeString.replace( "%M", QString::number( time.minute() ) );
    }

    // seconds
    if(time.second() < 10)
    {
       dateTimeString.replace( "%S", "0"+QString::number( time.second() ) );
    }
    else
    {
       dateTimeString.replace( "%S", QString::number( time.second() ) );
    }

    // timezone
    dateTimeString.replace( "%Z", d->_identitySettings.timeZone );
    QTime t;
    int sgn = KRFCDate::localUTCOffset() < 0 ? -1 : 1 ;
    t = t.addSecs( sgn*KRFCDate::localUTCOffset()*60 );
    dateTimeString.replace( "%z", (sgn<0 ? "-" : "+") +t.toString("hhmm"));

    return dateTimeString;
}


01773 ConversionStatus Catalog::saveFile()
{
   if(d->_url.isEmpty())
   {
      kdFatal(KBABEL) << "fatal error: empty filename" << endl;
      return NO_FILE;
   }

   return saveFileAs(d->_url,true);
}

01784 ConversionStatus Catalog::saveFileAs(const KURL &url, bool overwrite)
{
   if( d->_active ) return BUSY;

   ConversionStatus status=OK;

   bool newName=false;
   KURL targetURL=d->_url;

   if(url != d->_url)
   {
      newName = true;
      targetURL=url;
   }


   if(d->_saveSettings.autoUpdate)
   {
      d->_header=updatedHeader(d->_header);
      emit signalHeaderChanged();
   }


   if(targetURL.isLocalFile())
   {
      // test if the directory exists. If not, create it.
      QDir dir( targetURL.directory());

      QStringList dirList;
      while(!dir.exists() && !dir.dirName().isEmpty())
      {
         dirList.prepend(dir.dirName());
         dir.setPath(dir.path()+"/..");
      }
      for ( QStringList::Iterator it = dirList.begin(); it != dirList.end(); ++it )
      {
         if(!dir.mkdir(*it))
         {
            status=OS_ERROR;
            break;
         }
         dir.cd(*it);
      }

      if(status==OK)
      {
         status=writeFile(targetURL.path(0),overwrite);
      }
   }
   else
   {
      QString tempFile=kapp->tempSaveName(targetURL.path(0));

      status = writeFile(tempFile,overwrite);

      if(status == OK)
      {
         KURL temp(tempFile);
         if( !KIO::NetAccess::upload( temp.url(), targetURL, NULL ) )
         {
            status = OS_ERROR;
         }
      }

      QFile::remove(tempFile);
   }

   if(status == OK)
   {
      setModified(false);

      if(newName)
      {
         // if we saved a file, the catalog can not be any longer readOnly;
         d->_readOnly=false;

         d->_url=targetURL;

         emit signalFileOpened(d->_readOnly);
      }
   }

   return status;
}

01869 QString Catalog::saveTempFile()
{
   QString filename = kapp->tempSaveName("/temp/kbabel_temp.po");
   if( writeFile(filename) != OK )
   {
      filename = QString::null;
   }

   return filename;
}


01881 ConversionStatus Catalog::writeFile(QString localFile , bool overwrite)
{
   QFileInfo info(localFile);

   if(info.isDir())
      return NO_FILE;

   if(info.exists())
   {
      if(!overwrite || !info.isWritable())
      {
         return NO_PERMISSIONS;
      }
   }
   else // check if the directory is writable
   {
      QFileInfo dir(info.dirPath());
      if(!dir.isWritable())
      {
         return NO_PERMISSIONS;
      }
   }

    ConversionStatus error = OK;
    CatalogExportPlugin* filter=0;

    // gimme plugin for this MIME type
    KMimeType::Ptr mime = KMimeType::findByURL( KURL::fromPathOrURL( localFile ) );
    KTrader::OfferList offers = KTrader::self()->query("KBabelFilter", "('"+mime->name()+"' in [X-KDE-Export])");
    KService::Ptr ptr = offers.first();

       // we have no offer for this MIME type
    if( !ptr )
    {
      kdDebug(KBABEL) << "No plugin for this type" << endl;
      return NO_PLUGIN;
    }

    // try to load the library, if unsuccesfull, we have an installation problem
    KLibFactory *factory = KLibLoader::self()->factory( ptr->library().local8Bit() );
    if (!factory)
    {
      kdDebug(KBABEL) << "No factory" << endl;
      return OS_ERROR;
    }

    // create the filter
    filter = static_cast<CatalogExportPlugin*>(factory->create(0, 0));

    // provide progress bar indication
    connect( filter, SIGNAL( signalResetProgressBar(QString,int) ),
          this, SIGNAL( signalResetProgressBar(QString,int) ));
    connect( filter, SIGNAL( signalProgress(int) ),
          this, SIGNAL( signalProgress(int) ));
    connect( filter, SIGNAL( signalClearProgressBar() ),
          this, SIGNAL( signalClearProgressBar() ));

    connect( this, SIGNAL( signalStopActivity() ),
          filter, SLOT( stop() ));

    // load in the file (target is always local)
    kdDebug(KBABEL) << "writeFile active" << endl;
    d->_active = true;
    error = filter->save(localFile,mime->name(),this);
    // we should be not freed yet
    kdDebug(KBABEL) << "writeFile not active" << endl;
    d->_active = false;
    if( error == STOPPED ) return STOPPED;

    delete filter;

    return error;
}

01955 QTextCodec* Catalog::codecForFile(QString gettextHeader)
{
   QString charset;

   QString head = gettextHeader;

   QRegExp r("Content-Type:\\s*\\w+/[-\\w]+;\\s*charset\\s*=\\s*[^\\\"\\n]+");
   int begin=r.search(head);
   int len=r.matchedLength();
   if(begin<0) {
      kdDebug(KBABEL) << "no charset entry found" << endl;
      return 0;
   }

   head = head.mid(begin,len);

   QRegExp regexp("charset *= *([^\\\\\\\"]+)");
   if( regexp.search( head ) > -1 )
   {
       charset = regexp.cap(1);
   }

   QTextCodec* codec=0;

   if(!charset.isEmpty())
   {
      // "CHARSET" is the default charset entry in a template (pot).
      // characters in a template should be either pure ascii or
      // at least utf8, so utf8-codec can be used for both.
      if( charset == "CHARSET")
      {
          codec=QTextCodec::codecForName("utf8");
          kdDebug(KBABEL)
              << QString("file seems to be a template: using utf8 encoding.")
              << endl;
      }
      else
      {
         codec=QTextCodec::codecForName(charset.latin1());
      }

      if(!codec)
      {
         kdWarning() << "charset found, but no codec available, using UTF8 instead" << endl;
       codec=QTextCodec::codecForName("utf8");
      }
   }

   return codec;
}

PoInfo Catalog::headerInfo(const CatalogItem headerItem)
{
   QStringList header=headerItem.msgstrAsList();

   QStringList::Iterator it;

   PoInfo info;

   // extract information from the header
   for(it=header.begin();it!=header.end();++it)
   {
      if((*it).contains(QRegExp("^\\s*Project-Id-Version\\s*:\\s*.+\\s*$")))
      {
         info.project=(*it).replace(QRegExp("^\\s*Project-Id-Version\\s*:\\s*"),"");

         if(info.project.right(2)=="\\n")
            info.project.remove(info.project.length()-2,2);

         info.project=info.project.simplifyWhiteSpace();
      }
      else if((*it).contains(QRegExp("^\\s*POT-Creation-Date\\s*:\\s*.+\\s*$")))
      {
         info.creation=(*it).replace(QRegExp("^\\s*POT-Creation-Date\\s*:\\s*"),"");

         if(info.creation.right(2)=="\\n")
            info.creation.remove(info.creation.length()-2,2);

         info.creation=info.creation.simplifyWhiteSpace();
      }
      else if((*it).contains(QRegExp("^\\s*PO-Revision-Date\\s*:\\s*.+\\s*$")))
      {
         info.revision=(*it).replace(QRegExp("^\\s*PO-Revision-Date\\s*:\\s*"),"");

         if(info.revision.right(2)=="\\n")
            info.revision.remove(info.revision.length()-2,2);

         info.revision=info.revision.simplifyWhiteSpace();
      }
      else if((*it).contains(QRegExp("^\\s*Last-Translator\\s*:\\s*.+\\s*$")))
      {
         info.lastTranslator=(*it).replace(QRegExp("^\\s*Last-Translator\\s*:\\s*"),"");

         if(info.lastTranslator.right(2)=="\\n")
            info.lastTranslator.remove(info.lastTranslator.length()-2,2);

         info.lastTranslator=info.lastTranslator.simplifyWhiteSpace();
      }
      else if((*it).contains(QRegExp("^\\s*Language-Team\\s*:\\s*.+\\s*")))
      {
         info.languageTeam=(*it).replace(QRegExp("^\\s*Language-Team\\s*:\\s*"),"");

         if(info.languageTeam.right(2)=="\\n")
            info.languageTeam.remove(info.languageTeam.length()-2,2);

         info.languageTeam=info.languageTeam.simplifyWhiteSpace();
      }
      else if((*it).contains(QRegExp("^\\s*MIME-Version\\s*:\\s*.+\\s*")))
      {
         info.mimeVersion=(*it).replace(QRegExp("^\\s*MIME-Version\\s*:\\s*"),"");

         if(info.mimeVersion.right(2)=="\\n")
            info.mimeVersion.remove(info.mimeVersion.length()-2,2);

         info.mimeVersion=info.mimeVersion.simplifyWhiteSpace();
      }
      else if((*it).contains(QRegExp("^\\s*Content-Type\\s*:\\s*.+\\s*")))
      {
         info.contentType=(*it).replace(QRegExp("^\\s*Content-Type\\s*:\\s*"),"");

         if(info.contentType.right(2)=="\\n")
            info.contentType.remove(info.contentType.length()-2,2);

         info.contentType=info.contentType.simplifyWhiteSpace();
      }
      else if((*it).contains(QRegExp("^\\s*Content-Transfer-Encoding\\s*:\\s*.+\\s*")))
      {
         info.encoding=(*it).replace(QRegExp("^\\s*Content-Transfer-Encoding\\s*:\\s*"),"");

         if(info.encoding.right(2)=="\\n")
            info.encoding.remove(info.encoding.length()-2,2);

         info.encoding=info.encoding.simplifyWhiteSpace();
      }
      else
      {
          QString line=(*it);

         if(line.right(2)=="\\n")
            line.remove(line.length()-2,2);

         line=line.simplifyWhiteSpace();
         if(!info.others.isEmpty())
             info.others+='\n';

         info.others+=line;
      }


   }

   info.headerComment=headerItem.comment();

   return info;
}

bool Catalog::isUndoAvailable()
{
   return !d->_undoList.isEmpty();
}

bool Catalog::isRedoAvailable()
{
   return !d->_redoList.isEmpty();
}

02121 int Catalog::undo()
{
   if(!isUndoAvailable())
      return -1;

   int macroLevel = 0;

   EditCommand *command=0;
   do
   {
      command = d->_undoList.take();
      if ( !command )
      {
         kdError() << "undo command is NULL?" << endl;
         return -1;
      }

      processCommand( command, 0, true );

      macroLevel += command->terminator();

      if ( d->_undoList.isEmpty() )
      {
         emit signalUndoAvailable( false );
      }
      if(d->_redoList.isEmpty())
      {
         emit signalRedoAvailable(true);
      }
      d->_redoList.append(command);

    }
    while(macroLevel != 0);

    return command->index();
}

02158 int Catalog::redo()
{
   if(!isRedoAvailable())
      return -1;

   int macroLevel = 0;
   EditCommand *command=0;

   do
   {
      command = d->_redoList.take();
      if ( !command )
      {
         kdError() << "undo command is NULL?" << endl;
         return -1;
      }

      processCommand( command, 0,false );

      macroLevel += command->terminator();
      if ( d->_redoList.isEmpty() )
      {
         emit signalRedoAvailable( false );
      }
      if ( d->_undoList.isEmpty() )
      {
         emit signalUndoAvailable( true );
      }

      d->_undoList.append( command );
    }
    while (macroLevel != 0);

    return command->index();
}

02194 void Catalog::applyEditCommand(EditCommand* cmd, CatalogView* view)
{

    processCommand(cmd,view);
    setModified(true);

    if ( d->_undoList.isEmpty() )
    {
       emit signalUndoAvailable(true);
    }
    else if ( cmd->merge( d->_undoList.last() ) )
    {
       delete cmd;
       return;
    }


    d->_undoList.append(cmd);


    if ( !d->_redoList.isEmpty() )
    {
       d->_redoList.clear();
       emit signalRedoAvailable( false );
    }

}

02222 void Catalog::applyBeginCommand(uint index, Part part, CatalogView* view)
{
    applyEditCommand( new BeginCommand(index,part), view );
}

02227 void Catalog::applyEndCommand(uint index, Part part, CatalogView* view)
{
    applyEditCommand( new EndCommand(index,part), view );
}

02232 void Catalog::processCommand(EditCommand* cmd,CatalogView* view, bool undo)
{
    if(cmd->terminator()==0)
    {
       bool checkUntranslated=false;
       bool checkFuzzy=false;
       bool wasFuzzy=false;

       CatalogItem &item=d->_entries[cmd->index()];

       if(cmd->part() == Msgstr)
       {
          if( item.isUntranslated() )
          {
             d->_untransIndex.remove(cmd->index());

             emit signalNumberOfUntranslatedChanged(numberOfUntranslated());
          }
          else
          {
             checkUntranslated=true;
          }
       }
       else if(cmd->part() == Comment)
       {
          checkFuzzy=true;
          wasFuzzy=item.isFuzzy();
       }



       item.processCommand(cmd,undo);

       if(undo)
       {
          EditCommand* tmpCmd=0;
          DelTextCmd* delcmd = (DelTextCmd*) cmd;
          if (delcmd->type() == EditCommand::Delete )
          {
             tmpCmd = new InsTextCmd(delcmd->offset,delcmd->str,delcmd->pluralNumber);
          }
          else
          {
             tmpCmd = new DelTextCmd(delcmd->offset,delcmd->str,delcmd->pluralNumber);
          }

          tmpCmd->setIndex(cmd->index());
          tmpCmd->setPart(cmd->part());

          updateViews(tmpCmd,view);

          delete tmpCmd;
       }
       else
       {
          updateViews(cmd,view);
       }

       if(checkUntranslated && item.isUntranslated())
       {
          QValueList<uint>::Iterator it;

          // insert index in the right place in the list
          it = d->_untransIndex.begin();
          while(it != d->_untransIndex.end() && cmd->index() > (int)(*it))
          {
             ++it;
          }
          d->_untransIndex.insert( it,(uint)(cmd->index()) );

          emit signalNumberOfUntranslatedChanged(numberOfUntranslated());
       }
       else if(checkFuzzy)
       {
          if(wasFuzzy != item.isFuzzy())
          {
             if(wasFuzzy)
             {
                d->_fuzzyIndex.remove(cmd->index());
                emit signalNumberOfFuzziesChanged(numberOfFuzzies());
             }
             else
             {
                QValueList<uint>::Iterator it;

                // insert index in the right place in the list
                it = d->_fuzzyIndex.begin();
                while(it != d->_fuzzyIndex.end() && cmd->index() > (int)(*it))
                {
                   ++it;
                }
                d->_fuzzyIndex.insert( it,(uint)(cmd->index()) );

                emit signalNumberOfFuzziesChanged(numberOfFuzzies());
             }
          }
       }

    }
}

02333 bool Catalog::findNext(const FindOptions* findOpts, DocPosition& docPos, int& len)
{
      bool success = false; // true, when string found
      bool endReached=false;

      kdDebug(KBABEL) << "findNext active" << endl;
      d->_active=true;
      d->_stop=false;
      connect( this, SIGNAL( signalStopActivity() ), this, SLOT( stopInternal() ));

      len=0;
      int pos=0;

      QString searchStr = findOpts->findStr;
      QRegExp regexp(searchStr);

      if( findOpts->isRegExp ) {
            regexp.setCaseSensitive(findOpts->caseSensitive);
      }

      if( docPos.item == numberOfEntries()-1) {
            switch(docPos.part) {
                  case Msgid:
                        // FIXME: we should search in plurals as well
                        if(!findOpts->inMsgstr && !findOpts->inComment
                              && docPos.offset >= msgid(docPos.item).first().length() ) {
                              endReached=true;
                        }
                        break;
                  case Msgstr:
                        if(!findOpts->inComment && (int)(docPos.form+1) >= numberOfPluralForms(docPos.item)
                            && docPos.offset >= msgstr(docPos.item).last().length() ) {
                              endReached=true;
                        }
                        break;
                  case Comment:
                        if(docPos.offset >= comment(docPos.item).length() ) {
                              endReached=true;
                        }
                        break;
                  case UndefPart:
                        break;
            }
      }

      while(!success) {
        int accelMarkerPos = -1;
        int contextInfoLength = 0;
        int contextInfoPos = -1;
            QString targetStr;

            kapp->processEvents(10);

            if( d->_stop || endReached)
            {
                kdDebug(KBABEL) << "FindNext: endReached or stopped" << endl;
                disconnect( this, SIGNAL( signalStopActivity() ), this, SLOT( stopInternal() ));
                kdDebug(KBABEL) << "findNext not active" << endl;
                d->_active=false;
                d->_stop=false;
                return false;
            }

            switch(docPos.part) {
                  case Msgid:
                        // FIXME: should care about plural forms in msgid
                        targetStr = msgid(docPos.item).first();
                        break;
                  case Msgstr:
                        targetStr = *(msgstr(docPos.item).at(docPos.form));
                        break;
                  case Comment:
                        targetStr = comment(docPos.item);
                        break;
                  case UndefPart:
                        break;
            }

        if(findOpts->ignoreContextInfo)
        {
            contextInfoPos = d->_miscSettings.contextInfo.search(targetStr);
          contextInfoLength = d->_miscSettings.contextInfo.matchedLength();
            if(contextInfoPos >= 0)
            {
                targetStr.remove(contextInfoPos,contextInfoLength);

                if(docPos.offset > (uint)contextInfoPos)
                    docPos.offset -= contextInfoLength;
            }
        }

        if(findOpts->ignoreAccelMarker
                && targetStr.contains(d->_miscSettings.accelMarker))
        {
            accelMarkerPos = targetStr.find(d->_miscSettings.accelMarker);
            targetStr.remove(accelMarkerPos,1);

            if(docPos.offset > (uint)accelMarkerPos)
                docPos.offset--;
        }

            if( findOpts->isRegExp ) {
                  if ((pos=regexp.search(targetStr,docPos.offset)) >= 0 ) {
                      len = regexp.matchedLength();
                        if(findOpts->wholeWords) {
                              QString pre=targetStr.mid(pos-1,1);
                              QString post=targetStr.mid(pos+len,1);
                              if(!pre.contains(QRegExp("[a-zA-Z0-9]")) && !post.contains(QRegExp("[a-zA-Z0-9]")) ){
                                    success=true;
                                    docPos.offset=pos;
                              }
                        }
                        else {
                              success=true;
                              docPos.offset=pos;
                        }
                  }
            }
            else {
                  if( (pos=targetStr.find(searchStr,docPos.offset,findOpts->caseSensitive)) >= 0 ) {
                        len=searchStr.length();

                        if(findOpts->wholeWords) {
                              QString pre=targetStr.mid(pos-1,1);
                              QString post=targetStr.mid(pos+len,1);
                              if(!pre.contains(QRegExp("[a-zA-Z0-9]")) && !post.contains(QRegExp("[a-zA-Z0-9]")) ){
                                    success=true;
                                    docPos.offset=pos;
                              }
                        }
                        else {
                              success=true;
                              docPos.offset=pos;
                        }
                  }
            }


            if(!success) {
                  docPos.offset=0;
                  switch(docPos.part) {
                        case Msgid:
                        {
                              if(findOpts->inMsgstr) {
                                    docPos.part = Msgstr;
                                    docPos.form = 0;
                              }
                              else if(findOpts->inComment) {
                                    docPos.part = Comment;
                              }
                              else
                              {
                                    if(docPos.item >= numberOfEntries()-1)
                                    {
                                          endReached=true;
                                    }
                                    else
                                    {
                                          docPos.item++;
                                    }
                              }
                              break;
                        }
                        case Msgstr:
                              if( (int)docPos.form < numberOfPluralForms(docPos.item)-1 && pluralForm(docPos.item)==Gettext) {
                                  docPos.form++;
                              }
                              else
                              if(findOpts->inComment) {
                                    docPos.part = Comment;
                              }
                              else if(findOpts->inMsgid) {
                                    if(docPos.item >= numberOfEntries()-1)
                                    {
                                          endReached=true;
                                    }
                                    else
                                    {
                                          docPos.part = Msgid;
                                          docPos.item++;
                                    }
                              }
                              else {
                                    if(docPos.item >= numberOfEntries()-1)
                                    {
                                          endReached=true;
                                    }
                                    else
                                    {
                                          docPos.item++;
                                    }
                              }
                              break;
                        case Comment:
                              if(findOpts->inMsgid) {
                                    if(docPos.item >= numberOfEntries()-1)
                                    {
                                          endReached=true;
                                    }
                                    else
                                    {
                                          docPos.part = Msgid;
                                          docPos.item++;
                                    }
                              }
                              else if(findOpts->inMsgstr){
                                    if(docPos.item >= numberOfEntries()-1)
                                    {
                                          endReached=true;
                                    }
                                    else
                                    {
                                          docPos.part = Msgstr;
                                          docPos.form = 0;
                                          docPos.item++;
                                    }
                              }
                              else {
                                    if(docPos.item >= numberOfEntries()-1)
                                    {
                                          endReached=true;
                                    }
                                    else
                                    {
                                          docPos.item++;
                                    }
                              }
                              break;
                        case UndefPart:
                              break;
                  }
            }
        else
        {
            if(accelMarkerPos >= 0)
            {
                if(docPos.offset >= (uint)accelMarkerPos)
                {
                    docPos.offset++;
                }
                else if(docPos.offset+len > (uint)accelMarkerPos)
                {
                    len++;
                }
            }

            if(contextInfoPos >= 0)
            {
                if(docPos.offset >= (uint)contextInfoPos)
                {
                    docPos.offset+=contextInfoLength;
                }
                else if(docPos.offset+len > (uint)contextInfoPos)
                {
                    len+=contextInfoLength;
                }

            }
        }
      }

      disconnect( this, SIGNAL( signalStopActivity() ), this, SLOT( stopInternal() ));
      kdDebug(KBABEL) << "findNext not active" << endl;
      d->_active=false;
      d->_stop=false;

      return true;
}

02602 bool Catalog::findPrev(const FindOptions* findOpts, DocPosition& docPos, int& len)
{
      bool success = false;  // true, when found
      bool beginReached = false;

      kdDebug(KBABEL) << "findPrev active" << endl;
      d->_active=true;
      d->_stop=false;
      connect( this, SIGNAL( signalStopActivity() ), this, SLOT( stopInternal() ));

      len=0;
      int pos=0;

      QString searchStr = findOpts->findStr;
      QRegExp regexp(searchStr);

      if( findOpts->isRegExp ) {
            regexp.setCaseSensitive(findOpts->caseSensitive);
      }
      while(!success) {
        int accelMarkerPos = -1;
        int contextInfoLength = 0;
        int contextInfoPos = -1;
            QString targetStr;

            kapp->processEvents(10);

            if( d->_stop || beginReached)
            {
                kdDebug(KBABEL) << "FindNext: endReached or stopped" << endl;
                disconnect( this, SIGNAL( signalStopActivity() ), this, SLOT( stopInternal() ));
                kdDebug(KBABEL) << "findPrev active" << endl;
                d->_active=false;
                d->_stop=false;
                return false;
            }

            switch(docPos.part) {
                  case Msgid:
                        // FIXME: should care about plural forms in msgid
                        targetStr = msgid(docPos.item).first();
                        break;
                  case Msgstr:
                        targetStr = *(msgstr(docPos.item).at(docPos.form));
                        break;
                  case Comment:
                        targetStr = comment(docPos.item);
                        break;
                  case UndefPart:
                        break;
            }

        if(findOpts->ignoreContextInfo)
        {
            contextInfoPos = d->_miscSettings.contextInfo.search(targetStr);
          contextInfoLength = d->_miscSettings.contextInfo.matchedLength();
            if(contextInfoPos >= 0)
            {
                targetStr.remove(contextInfoPos,contextInfoLength);

                if(docPos.offset > (uint)contextInfoPos)
                    docPos.offset -= contextInfoLength;
            }
        }

        if(findOpts->ignoreAccelMarker
                && targetStr.contains(d->_miscSettings.accelMarker))
        {
            accelMarkerPos = targetStr.find(d->_miscSettings.accelMarker);
            targetStr.remove(accelMarkerPos,1);

            if(docPos.offset > (uint)accelMarkerPos)
                docPos.offset--;
        }

            if(docPos.offset <= 0) {
                  success=false;
            }
            else if( findOpts->isRegExp ) {
                  /*
                  don't work!?
                  if((pos=targetStr.findRev(regexp,docPos.offset)) >= 0 ) {
                        regexp.match(targetStr,pos,&len); // to get the length of the string
                  */
                  bool found=false;
                  int tmpPos=docPos.offset;
                  while(!found && tmpPos>=0)
                  {
                        if( (pos=regexp.search(targetStr,tmpPos)) >= 0 && (uint)pos < docPos.offset)
                              found=true;
                        else
                              tmpPos--;
                        len = regexp.matchedLength();
                  }
                  if(found) {
                        if(findOpts->wholeWords) {
                              QString pre=targetStr.mid(pos-1,1);
                              QString post=targetStr.mid(pos+len,1);
                              if(!pre.contains(QRegExp("[a-zA-Z0-9]")) && !post.contains(QRegExp("[a-zA-Z0-9]")) ){
                                    success=true;
                                    docPos.offset=pos;
                              }
                        }
                        else {
                              success=true;
                              docPos.offset=pos;
                        }
                  }
            }
            else if( (pos=targetStr.findRev(searchStr,docPos.offset-1,findOpts->caseSensitive)) >= 0
                      && (uint)pos < docPos.offset) {
                  len=searchStr.length();
                  if(findOpts->wholeWords) {
                        QString pre=targetStr.mid(pos-1,1);
                        QString post=targetStr.mid(pos+len,1);
                        if(!pre.contains(QRegExp("[a-zA-Z0-9]")) && !post.contains(QRegExp("[a-zA-Z0-9]")) ){
                              success=true;
                              docPos.offset=pos;
                        }
                  }
                  else {
                        success=true;
                        docPos.offset=pos;
                  }
            }

            if(!success) {
                  switch(docPos.part) {
                        case Comment:
                        {
                              if(findOpts->inMsgstr) {
                                    docPos.part = Msgstr;
                                    docPos.form = msgstr(docPos.item).count()-1;
                                    docPos.offset = msgstr(docPos.item).last().length();
                              }
                              else if(findOpts->inMsgid) {
                                    docPos.part = Msgid;
                                    // FIXME: should care about plural forms in msgid
                                    docPos.offset = msgid(docPos.item).first().length();
                              }
                              else
                              {
                                    if(docPos.item <= 0)
                                    {
                                          beginReached=true;
                                    }
                                    else
                                    {
                                          docPos.item--;
                                          docPos.offset = comment(docPos.item).length();
                                    }
                              }
                              break;
                        }
                        case Msgstr:
                              if(docPos.form != 0 ) {
                                  docPos.form--;
                                  docPos.offset = (*msgstr(docPos.item).at(docPos.form)).length();
                              }
                              else if(findOpts->inMsgid) {
                                    docPos.part = Msgid;
                                    // FIXME: should care about plural forms in msgid
                                    docPos.offset = msgid(docPos.item).first().length();
                              }
                              else if(findOpts->inComment) {
                                    if(docPos.item <= 0)
                                    {
                                          beginReached=true;
                                    }
                                    else
                                    {
                                          docPos.part = Comment;
                                          docPos.item--;
                                          docPos.offset = comment(docPos.item).length();
                                    }
                              }
                              else {
                                    if(docPos.item <= 0)
                                    {
                                          beginReached=true;
                                    }
                                    else
                                    {
                                          docPos.item--;
                                          docPos.offset = msgstr(docPos.item).last().length();
                                          docPos.form = msgstr(docPos.item).count()-1;
                                    }
                              }
                              break;
                        case Msgid:
                              if(findOpts->inComment) {
                                    if(docPos.item <= 0 )
                                    {
                                          beginReached=true;
                                    }
                                    else
                                    {
                                          docPos.part = Comment;
                                          docPos.item--;
                                          docPos.offset = comment(docPos.item).length();
                                    }
                              }
                              else if(findOpts->inMsgstr){
                                    if(docPos.item <= 0)
                                    {
                                          beginReached=true;
                                    }
                                    else
                                    {
                                          docPos.part = Msgstr;
                                          docPos.item--;
                                          docPos.offset = msgstr(docPos.item).last().length();
                                          docPos.form = msgstr(docPos.item).count()-1;
                                    }
                              }
                              else {
                                    if(docPos.item <= 0)
                                    {
                                          beginReached=true;
                                    }
                                    else
                                    {
                                          docPos.item--;
                                          // FIXME: should care about plural forms in msgid
                                          docPos.offset = msgid(docPos.item).first().length();
                                    }
                              }
                              break;
                        case UndefPart:
                              break;
                  }
            }
        else
        {
            if(accelMarkerPos >= 0)
            {
                if(docPos.offset >= (uint)accelMarkerPos)
                {
                    docPos.offset++;
                }
                else if(docPos.offset+len > (uint)accelMarkerPos)
                {
                    len++;
                }
            }

            if(contextInfoPos >= 0)
            {
                if(docPos.offset >= (uint)contextInfoPos)
                {
                    docPos.offset+=contextInfoLength;
                }
                else if(docPos.offset+len > (uint)contextInfoPos)
                {
                    len+=contextInfoLength;
                }

            }
        }
      }

      disconnect( this, SIGNAL( signalStopActivity() ), this, SLOT( stopInternal() ));
      kdDebug(KBABEL) << "findPrev active" << endl;
      d->_active=false;
      d->_stop=false;

      return true;
}


02872 Catalog::DiffResult Catalog::diff(uint entry, QString *result)
{
    if(!result)
    {
        kdWarning() << "0 pointer for result" << endl;
        return DiffNotFound;
    }

    if( d->msgidDiffList.isEmpty() )
    {
        return DiffNeedList;
    }

    // first look if the diff for this entry is in the cache
    QString *s = d->diffCache[entry];
    if(s)
    {
        if(s->isEmpty())
            return DiffNotFound;


        *result = *s;
        return DiffOk;
    }

    // then look if the same msgid is contained in the diff file
    // FIXME: should care about plural forms in msgid
    QString id = msgid(entry).first();
    id.replace( "\n","");
    if(d->msgidDiffList.contains(id))
    {
      // FIXME:: should care about plural forms in msgid
        *result = msgid(entry).first();

        return DiffOk;
    }

    connect( this, SIGNAL( signalStopActivity() ), this, SLOT( stopInternal() ));
    kdDebug(KBABEL) << "diffv active" << endl;
    d->_active=true;
    d->_stop=false;

    QString idForDiff;

    // then look if there are entries with the same translation
    kdWarning() << "Diff feature (2) does not work with plural forms" << endl;
    QString str = msgstr(entry).first();
    str.replace("\n","");
    if(d->msgstr2MsgidDiffList.contains(str))
    {
        QStringList list = d->msgstr2MsgidDiffList[str];

        if(list.count() == 1)
        {
            idForDiff = list.first();
        }
        else
        {
            // find the best matching id
            double bestWeight = 0.6;
            QString bestId;

            QStringList::ConstIterator it;
            for(it = list.begin(); it != list.end(); ++it)
            {
                double weight = fstrcmp( id.utf8(), (*it).utf8() );
                if(weight > bestWeight)
                {
                    bestWeight = weight;
                    bestId = (*it);
                }
            }

            if( !bestId.isEmpty() )
            {
                idForDiff = bestId;
            }
        }
    }
    else
    {
        emit signalResetProgressBar(i18n("searching matching message")
                ,100);

        // find the best matching id
        double bestWeight = 0.6;
        QString bestId;

        int counter=0;
        int oldPercent=0;
        int max = QMAX( d->msgidDiffList.count()-1, 1);

        QStringList::ConstIterator it;
        for(it = d->msgidDiffList.begin();
                it != d->msgidDiffList.end(); ++it)
        {
            counter++;
            int  percent = 100*counter/max;
            if(percent > oldPercent)
            {
                oldPercent = percent;
                emit signalProgress(percent);
            }

            double weight = fstrcmp( id.utf8(), (*it).utf8() );
            if(weight > bestWeight)
            {
                bestWeight = weight;
                bestId = (*it);
            }

            kapp->processEvents(10);

          if( d->_stop )
          {
            disconnect( this, SIGNAL( signalStopActivity() ), this, SLOT( stopInternal() ));
            kdDebug

            (KBABEL) << "diffv not active" << endl;
            d->_active=false;
            d->_stop=false;
            return DiffNotFound;
          }
        }

        if( !bestId.isEmpty() )
        {
            idForDiff = bestId;
        }

        emit signalClearProgressBar();

    }

    if( idForDiff.isEmpty() )
    {
        s = new QString(*result);
        if( !d->diffCache.insert(entry,s) )
            delete s;

      kdDebug     (KBABEL) << "diffv not active" << endl;
      d->_active=false;
      d->_stop=false;
        return DiffNotFound;
    }

    QString r = Diff::charDiff(idForDiff,id);


    // restore the newlines
    // FIXME: should care about plural forms in msgid
    QString orig = msgid(entry).first();
    if(orig.contains('\n'))
    {
        int pos=orig.find('\n');
        int oldPos=0;
        int rPos=0;
        int max = r.length();
        while(pos > 0)
        {
            int len = pos - oldPos;
            oldPos = pos+1;

            int p = 0;
            while(p < len && rPos < max)
            {
                QString m = r.mid(rPos,11);
                if(r[rPos]=='<' && m == "<KBABELDEL>")
                {
                    int endPos = r.find("</KBABELDEL>",rPos+11);
                    if(endPos > rPos)
                    {
                        rPos = endPos+12;
                    }
                    else
                    {
                        kdWarning(KBABEL) << "no closing diff remove tag found"
                                          << endl;
                        rPos+=11;
                    }
                }
                else if(r[rPos] == '<' && m == "<KBABELADD>")
                {
                    rPos+=11;
                }
                else if(r[rPos] == '<' && m == "</KBABELADD")
                {
                    rPos+=12;
                }
                else
                {
                    rPos++;
                    p++;
                }
            }

            if(rPos < max)
            {
                r.insert(rPos,'\n');
                max++;
                rPos++;
            }


            pos = orig.find('\n',pos+1);
        }
    }


    int pos = r.find("\\n");
    while(pos >= 0 )
    {
        int slash=1;
        while(pos-slash >= 0 && r[pos-slash]=='\\')
        {
            slash++;
        }
        if(slash%2 == 1 && r[pos+2]!='\n')
        {
            r.insert(pos+2,'\n');
            pos+=3;
        }
        else
        {
            pos+=2;
        }

        pos = r.find("\\n",pos);
    }

    // add newlines if line has gotten to long;
    pos = r.find('\n');
    int oldPos=0;
    const int maxLength = 60;
    while(pos >= 0)
    {
        int length=pos-oldPos;
        QString tmp = r.mid(oldPos, pos-oldPos);
        if(!tmp.contains("KBABELDEL>") )
        {
            oldPos = pos;
            pos = r.find('\n',pos+1);
            continue;
        }

        int n = tmp.contains("<KBABELADD>");
        length -= n*11;
        n = tmp.contains("<KBABELDEL>");
        length -= n*11;
        n = tmp.contains("</KBABELADD>");
        length -= n*12;
        n = tmp.contains("</KBABELDEL>");
        length -= n*12;
        while(length > maxLength)
        {
            int counter=0;
            while(counter < maxLength-10 && oldPos < pos)
            {
                if(r[oldPos]=='<' && r.mid(oldPos,11)=="<KBABELADD>")
                {
                    oldPos+=11;
                }
                else if(r[oldPos]=='<' && r.mid(oldPos,11)=="<KBABELDEL>")
                {
                    oldPos+=11;
                }
                else if(r[oldPos]=='<' && r.mid(oldPos,12)=="</KBABELADD>")
                {
                    oldPos+=12;
                }
                else if(r[oldPos]=='<' && r.mid(oldPos,12)=="</KBABELDEL>")
                {
                    oldPos+=12;
                }
                else
                {
                    counter++;
                    oldPos++;
                }
            }

            while(oldPos < pos-5 && !r[oldPos].isSpace() )
            {
                oldPos++;
            }
            oldPos++;
            if(oldPos < pos-5)
            {
                r.insert(oldPos,'\n');
                pos++;
            }
            else
            {
                break;
            }

            length=pos-oldPos;
            QString tmp = r.mid(oldPos, pos-oldPos);
            n = tmp.contains("<KBABELADD>");
            length -= n*11;
            n = tmp.contains("<KBABELDEL>");
            length -= n*11;
            n = tmp.contains("</KBABELADD>");
            length -= n*12;
            n = tmp.contains("</KBABELDEL>");
            length -= n*12;
        }

        oldPos=pos;
        pos = r.find('\n',pos+1);
    }

    // now the last line
    pos = r.length();

    int length=pos-oldPos;
    QString tmp = r.mid(oldPos, pos-oldPos);
    int n = tmp.contains("<KBABELADD>");
    length -= n*11;
    n = tmp.contains("<KBABELDEL>");
    length -= n*11;
    n = tmp.contains("</KBABELADD>");
    length -= n*12;
    n = tmp.contains("</KBABELDEL>");
    length -= n*12;
    while(length > maxLength)
    {
        int counter=0;
        while(counter < maxLength-10 && oldPos < pos)
        {
            if(r[oldPos]=='<' && r.mid(oldPos,11)=="<KBABELADD>")
            {
                oldPos+=11;
            }
            else if(r[oldPos]=='<' && r.mid(oldPos,11)=="<KBABELDEL>")
            {
                oldPos+=11;
            }
            else if(r[oldPos]=='<' && r.mid(oldPos,12)=="</KBABELADD>")
            {
                oldPos+=12;
            }
            else if(r[oldPos]=='<' && r.mid(oldPos,12)=="</KBABELDEL>")
            {
                oldPos+=12;
            }
            else
            {
                counter++;
                oldPos++;
            }
        }

        while(oldPos < pos-5 && !r[oldPos].isSpace() )
        {
            oldPos++;
        }
        oldPos++;
        if(oldPos < pos-5)
        {
            r.insert(oldPos,'\n');
            pos++;
        }
        else
        {
            break;
        }

        length=pos-oldPos;
        QString tmp = r.mid(oldPos, pos-oldPos);
        n = tmp.contains("<KBABELADD>");
        length -= n*11;
        n = tmp.contains("<KBABELDEL>");
        length -= n*11;
        n = tmp.contains("</KBABELADD>");
        length -= n*12;
        n = tmp.contains("</KBABELDEL>");
        length -= n*12;
    }

    *result = r;

    s = new QString(*result);
    if( !d->diffCache.insert(entry,s) )
        delete s;

    disconnect( this, SIGNAL( signalStopActivity() ), this, SLOT( stopInternal() ));
    kdDebug(KBABEL) << "diffv not active" << endl;
    d->_active=false;
    d->_stop=false;

    return DiffOk;
}

03266 void Catalog::setDiffList( const QValueList<DiffEntry>& list)
{
    connect( this, SIGNAL( signalStopActivity() ), this, SLOT( stopInternal() ));
    kdDebug(KBABEL) << "setDiffList active" << endl;
    d->_active=true;
    d->_stop=false;

    emit signalResetProgressBar(i18n("preparing messages for diff"),100);

    d->msgidDiffList.clear();
    d->msgstr2MsgidDiffList.clear();
    d->diffCache.clear();

    uint max = QMAX(list.count()-1,1);
    int oldPercent=0;
    uint counter=0;
    QValueList<DiffEntry>::ConstIterator it;
    for(it = list.begin(); it != list.end(); ++it)
    {
        int percent = (100*counter)/max;
        counter++;
        if(percent > oldPercent)
        {
            oldPercent = percent;
            emit signalProgress(percent);
            kapp->processEvents(10);
        }

        QString id = (*it).msgid;
        id.replace("\n","");
        QString str = (*it).msgstr;
        str.replace("\n","");
        d->msgidDiffList.append(id);

        if(!str.isEmpty())
        {
            if(d->msgstr2MsgidDiffList.contains(str))
            {
                QStringList sl = d->msgstr2MsgidDiffList[str];
                sl.append(id);
            }
            else
            {
                QStringList sl;
                sl.append(id);
                d->msgstr2MsgidDiffList.insert(str,sl);
            }
        }
    }

    emit signalClearProgressBar();

    disconnect( this, SIGNAL( signalStopActivity() ), this, SLOT( stopInternal() ));
    kdDebug(KBABEL) << "setDiffList not active" << endl;
    d->_active=false;
    d->_stop=false;
}

03324 QValueList<DiffEntry> Catalog::asDiffList()
{
    QValueList<DiffEntry> list;

    for ( QValueVector<CatalogItem>::Iterator it = d->_entries.begin();
                              it != d->_entries.end(); ++it)
      {
        DiffEntry e;
        e.msgid = (*it).msgid().first();
      kdWarning() << "Diff feature does not support plural forms" << endl;
        e.msgstr = (*it).msgstr().first();

        list.append(e);
    }

    return list;

}

03343 int Catalog::defaultNumberOfPluralForms() const
{
    return d->numberOfPluralForms;
}

03348 void Catalog::getNumberOfPluralForms()
{
    if(d->_identitySettings.numberOfPluralForms > 0)
    {
        d->numberOfPluralForms = d->_identitySettings.numberOfPluralForms;
        return;
    }

    QString lang=d->_identitySettings.languageCode;
    if(lang.isEmpty())
    {
        d->numberOfPluralForms=-1;
        return;
    }

    d->numberOfPluralForms = getNumberOfPluralForms(lang);
}

03366 int Catalog::getNumberOfPluralForms(const QString& lang)
{
    int nr=-1;

    KLocale locale("kdelibs");
    locale.setLanguage(lang);

    const char* formsString =
    "_: Dear translator, please do not translate this string in any form, but "
    "pick the _right_ value out of NoPlural/TwoForms/French... If not sure what "
    "to do mail thd@kde.org and coolo@kde.org, they will tell you. Better leave "
    "that out if unsure, the programs will crash!!\n"
    "Definition of PluralForm - to be set by the translator of kdelibs.po";

    QString formsTranslation = locale.translate(formsString);

    // no translation found
    if(formsTranslation == formsString || formsTranslation.isEmpty())
    {
        kdDebug(KBABEL) << "no translation of PluralForms found" << endl;
        return -1;
    }
    if ( formsTranslation == "NoPlural" )
      nr = 1;
    else if ( formsTranslation == "TwoForms" )
      nr = 2;
    else if ( formsTranslation == "French" )
      nr = 2;
    else if ( formsTranslation == "Gaeilge" || formsTranslation == "OneTwoRest" )
      nr = 3;
    else if ( formsTranslation == "Russian" )
      nr = 3;
    else if ( formsTranslation == "Polish" )
      nr = 3;
    else if ( formsTranslation == "Slovenian" )
      nr = 4;
    else if ( formsTranslation == "Lithuanian" )
      nr = 3;
    else if ( formsTranslation == "Czech" )
      nr = 3;
    else if ( formsTranslation == "Slovak" )
      nr = 3;
    else if ( formsTranslation == "Maltese" )
      nr = 4;
    else if ( formsTranslation == "Arabic" )
      nr = 4;
    else if ( formsTranslation == "Balcan" )
      nr = 3;
    else
    {
        kdDebug(KBABEL) << "unknown translation of PluralForms: "
            << formsTranslation << endl;
        nr=-1;
    }

    return nr;
}

03424 int Catalog::numberOfPluralForms( uint index ) const
{
    if( index > numberOfEntries() ) return -1;

    if (  d->_entries.isEmpty() )
        return -1;
    if( d->_entries[index].pluralForm() == NoPluralForm  ) return 1;

    if( d->numberOfPluralForms > 0 )  return d->numberOfPluralForms;

    return 2; //default
}

03437 bool Catalog::isModified() const
{
    return d->_modified;
}

03442 void Catalog::setEntries(QValueVector<CatalogItem> entries)
{
    d->_entries=entries;
    
    // update the project for entries
    for ( QValueVector<CatalogItem>::Iterator it = d->_entries.begin();
                              it != d->_entries.end(); ++it)
    {
      it->setProject( d->_project );
    }
}

03454 void Catalog::setObsoleteEntries(QValueList<CatalogItem> entries)
{
    d->_obsoleteEntries=entries;
}

03459 QValueList<CatalogItem> Catalog::obsoleteEntries() const
{
    return d->_obsoleteEntries;
}

03464 void Catalog::setCatalogExtraData(const QStringList& data)
{
    d->_catalogExtra = data;
}

03469 QStringList Catalog::catalogExtraData() const
{
    return d->_catalogExtra;
}

03474 QString Catalog::importPluginID() const
{
    return d->_importID;
}

03479 QTextCodec* Catalog::fileCodec() const
{
    return d->fileCodec;
}

03484 void Catalog::setGeneratedFromDocbook(const bool generated)
{
    d->_generatedFromDocbook = generated;
}

03489 void Catalog::setFileCodec( QTextCodec* codec )
{
    d->fileCodec = codec;
}

03494 void Catalog::setErrorIndex( const QValueList<uint>& list )
{
    d->_errorIndex = list;
}

03499 void Catalog::setImportPluginID( const QString& id )
{
    d->_importID = id;
}

03504 void Catalog::stop()
{
    if( d->_active )
      emit signalStopActivity();
}

03510 void Catalog::stopInternal()
{
    d->_stop = true;
}

03515 bool Catalog::isActive()
{
    return  d->_active;
}

03520 void Catalog::setMimeTypes( const QString& mimeTypes )
{
    d->_mimeTypes = mimeTypes;
}

03525 QString Catalog::mimeTypes() const
{
    return d->_mimeTypes;
}

#include "catalog.moc"

Generated by  Doxygen 1.6.0   Back to index