/* **************************************************************************** This file is part of KBabel Copyright (C) 1999-2000 by Matthias Kiefer 2001-2005 by Stanislav Visnovsky Copyright (C) 2006 by Nicolas GOUTTE This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. **************************************************************************** */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 "kbprojectsettings.h" #include "resources.h" #include "version.h" #include "stringdistance.h" #include using namespace KBabel; Catalog::Catalog(QObject* parent, const char* name, QString projectFile) : QObject(parent,name) { if ( projectFile.isEmpty() ) projectFile = KBabel::ProjectManager::defaultProjectName(); d = new CatalogPrivate(ProjectManager::open(projectFile)); readPreferences(); } 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; } QString Catalog::msgctxt(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].msgctxt(); } 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); } 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); } 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(); } 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(); } CatalogItem Catalog::header() const { return d->_header; } QString Catalog::lastTranslator() const { return headerInfo( d->_header ).lastTranslator; } int Catalog::indexForMsgid(const QString& id) const { int i=0; QValueVector::ConstIterator it = d->_entries.begin(); while(it != d->_entries.end() && !((*it).msgid(true).contains(id))) { ++it; i++; } if(it == d->_entries.end()) i=-1; return i; } 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)); } 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::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::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; } */ bool Catalog::setHeader(CatalogItem newHeader) { if(newHeader.isValid()) { // normalize the values - ensure every key:value pair is only on a single line QString values = newHeader.msgstr().first(); values.replace ("\n", ""); values.replace ("\\n", "\\n\n"); kdDebug () << "Normalized header: " << values << endl; d->_header=newHeader; d->_header.setMsgstr (values); setModified(true); emit signalHeaderChanged(); return true; } return false; } KURL Catalog::currentURL() const { return d->_url; } void Catalog::setCurrentURL(const KURL& url) { d->_url=url; } 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; const IdentitySettings identityOptions = identitySettings(); const SaveSettings saveOptions = saveSettings(); if(!usePrefs || saveOptions.updateLastTranslator) { found=false; temp="Last-Translator: "+identityOptions.authorName; if(!identityOptions.authorEmail.isEmpty()) { temp+=(" <"+identityOptions.authorEmail+">"); } temp+="\\n"; for( it = headerList.begin(); it != headerList.end(); ++it ) { if((*it).contains(QRegExp("^ *Last-Translator:.*"))) { (*it) = temp; found=true; break; } } if(!found) { headerList.append(temp); } } if(!usePrefs || saveOptions.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) = temp; found=true; break; } } if(!found) { headerList.append(temp); } } if(!usePrefs || saveOptions.updateProject) { found=false; temp="Project-Id-Version: "+saveOptions.projectString+"\\n"; temp.replace( "@PACKAGE@", packageName()); for( it = headerList.begin(); it != headerList.end(); ++it ) { if((*it).contains(QRegExp("^ *Project-Id-Version:.*"))) { (*it) = temp; found=true; break; } } if(!found) { headerList.append(temp); } } if(!usePrefs || saveOptions.updateLanguageTeam) { found=false; temp="Language-Team: "+identityOptions.languageName; if(!identityOptions.mailingList.isEmpty()) { temp+=(" <"+identityOptions.mailingList+">"); } temp+="\\n"; for( it = headerList.begin(); it != headerList.end(); ++it ) { if((*it).contains(QRegExp("^ *Language-Team:.*"))) { (*it) = temp; found=true; break; } } if(!found) { headerList.append(temp); } } if(!usePrefs || saveOptions.updateCharset) { found=false; QString encodingStr; if(saveOptions.useOldEncoding && d->fileCodec) { encodingStr = charsetString(d->fileCodec); } else { encodingStr=charsetString(saveOptions.encoding); } temp = "Content-Type: text/plain; charset=" + encodingStr + "\\n"; it = headerList.begin(); while( it != headerList.end() ) { if( (*it).find( QRegExp( "^ *Content-Type:.*" ) ) != -1 ) { if ( found ) { // We had already a Content-Type, so we do not need a duplicate it = headerList.remove( it ); } else { found=true; QRegExp regexp( "^ *Content-Type:(.*/.*);?\\s*charset=.*$" ); QString mimeType; if ( regexp.search( *it ) ) { mimeType = regexp.cap( 1 ).stripWhiteSpace(); } if ( mimeType.isEmpty() ) { mimeType = "text/plain"; } temp = "Content-Type: "; temp += mimeType; temp += "; charset="; temp += encodingStr; temp += "\\n"; (*it) = temp; } } ++it; } if(!found) { headerList.append(temp); } } if(!usePrefs || saveOptions.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) = temp; found=true; break; } } 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) = temp; found=true; break; } } 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:"))) { (*it) = temp; found=true; break; } } if( !found ) { headerList.append(temp); } temp="Plural-Forms: %1\\n"; temp=temp.arg(identityOptions.gnuPluralFormHeader); found=false; // update plural form header if( !identityOptions.gnuPluralFormHeader.isEmpty() ) { for( it = headerList.begin(); it != headerList.end(); ++it ) { if((*it).contains(QRegExp("^ *Plural-Forms:"))) { (*it) = temp; found=true; break; } } if( !found ) { headerList.append(temp); } } oldHeader.setMsgstr( headerList.join( "\n" ) ); //comment = description, copyrights if(!usePrefs || (saveOptions.FSFCopyright != ProjectSettingsBase::NoChange)) { found=false; for( it = commentList.begin(); it != commentList.end(); ++it ) { // U+00A9 is the Copyright sign if ( (*it).find( QRegExp("^# *Copyright (\\(C\\)|\\x00a9).*Free Software Foundation, Inc") ) != -1 ) { found=true; break; } } if(found) { if ( (*it).find( QRegExp("^# *Copyright (\\(C\\)|\\x00a9) YEAR Free Software Foundation, Inc\\.") ) != -1 ) { //template string if( saveOptions.FSFCopyright == ProjectSettingsBase::Remove) (*it).remove(" YEAR Free Software Foundation, Inc"); else (*it).replace("YEAR", QDate::currentDate().toString("yyyy")); } else if( saveOptions.FSFCopyright == ProjectSettingsBase::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 || saveOptions.updateDescription ) && ( !saveOptions.descriptionString.isEmpty() ) ) { temp = "# "+saveOptions.descriptionString; temp.replace( "@PACKAGE@", packageName()); temp.replace( "@LANGUAGE@", identityOptions.languageName); temp = temp.stripWhiteSpace(); // The description strings has often buggy variants already in the file, these must be removed QString regexpstr = "^#\\s+" + QRegExp::escape( saveOptions.descriptionString.stripWhiteSpace() ) + "\\s*$"; regexpstr.replace( "@PACKAGE@", ".*" ); regexpstr.replace( "@LANGUAGE@", ".*" ); //kdDebug() << "REGEXPSTR: " << regexpstr << endl; QRegExp regexp ( regexpstr ); // The buggy variants exist in English too (of a time before KBabel got a translation for the corresponding language) QRegExp regexpUntranslated ( "^#\\s+Translation of .* into .*\\s*$" ); kdDebug () << "Temp is '" << temp << "'" << endl; found=false; //not used anyway bool foundTemplate=false; it = commentList.begin(); while ( it != commentList.end() ) { kdDebug () << "testing '" << (*it) << "'" << endl; bool deleteItem = false; if ( (*it) == temp ) { kdDebug () << "Match " << endl; if ( found ) deleteItem = true; else found=true; } else if ( regexp.search( *it ) >= 0 ) { // We have a similar (translated) string (from another project or another language (perhaps typos)). Remove it. deleteItem = true; } else if ( regexpUntranslated.search( *it ) >= 0 ) { // We have a similar (untranslated) string (from another project or another language (perhaps typos)). Remove it. deleteItem = true; } else if ( (*it) == "# SOME DESCRIPTIVE TITLE." ) { // We have the standard title placeholder, remove it deleteItem = true; } if ( deleteItem ) it = commentList.remove( it ); else ++it; } if(!found) commentList.prepend(temp); } // kdDebug() << "HEADER COMMENT: " << commentList << endl; if ( (!usePrefs || saveOptions.updateTranslatorCopyright) && ( ! identityOptions.authorName.isEmpty() ) && ( ! identityOptions.authorEmail.isEmpty() ) ) // An email address can be used as ersatz of a name { QStringList foundAuthors; temp = "# "; temp += identityOptions.authorName; if(!identityOptions.authorEmail.isEmpty()) { temp+=(" <"+identityOptions.authorEmail+">"); } temp+=", "+QDate::currentDate().toString("yyyy")+"."; // ### TODO: it would be nice if the entry could start with "COPYRIGHT" and have the "(C)" symbol (both not mandatory) QRegExp regexpAuthorYear( "^#.*(<.+@.+>)?,\\s*([\\d]+[\\d\\-, ]*|YEAR)" ); QRegExp regexpYearAlone( "^# , \\d{4}.?\\s*$" ); it = commentList.begin(); while ( it != commentList.end() ) { bool deleteItem = false; if ( (*it).find ( "copyright", 0, false ) != -1 ) { // We have a line with a copyright. It should not be moved. } else if ( (*it).find ( regexpYearAlone ) != -1 ) { // We have found a year number that is preceeded by a comma. // That is typical of KBabel 1.10 (and earlier?) when there is neither an author name nor an email // Remove the entry deleteItem = true; } else if ( (*it) == "# FIRST AUTHOR , YEAR." ) { // Typical placeholder, remove it. deleteItem = true; } else if ( (*it).find ( regexpAuthorYear ) != -1 ) // email address followed by year { if ( foundAuthors.find( (*it) ) == foundAuthors.end() ) { // The author line is new (and not a duplicate), so add it to the author line list foundAuthors.append( (*it) ); } // Delete also non-duplicated entry, as now all what is needed will be processed in foundAuthors deleteItem = true; } if ( deleteItem ) it = commentList.remove( it ); else ++it; } if( !foundAuthors.isEmpty() ) { found = false; bool foundAuthor = false; const QString cy = QDate::currentDate().toString("yyyy"); ait = foundAuthors.end(); for( it = foundAuthors.begin() ; it!=foundAuthors.end(); ++it ) { if ( (*it).find( QRegExp( QRegExp::escape( identityOptions.authorName )+".*" + QRegExp::escape( identityOptions.authorEmail ) ) ) != -1 ) { foundAuthor = true; if( (*it).find( cy ) != -1 ) found = true; else ait = it; } } if( !found ) { if ( !foundAuthor ) foundAuthors.append(temp); else if ( ait != foundAuthors.end() ) { //update years const int index = (*ait).findRev( QRegExp("[\\d]+[\\d\\-, ]*") ); if ( index == -1 ) (*ait)+=", "+cy; else (*ait).insert(index+1, QString(", ")+cy); } else kdDebug() << "INTERNAL ERROR: author found but iterator dangling!" << endl; } } else foundAuthors.append(temp); it=commentList.end(); do --it; while( ( it != commentList.begin() ) && ( (*it).find( QRegExp( "^#(\\s*$|[:,\\.])" ) ) == -1 ) ); ++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); } } oldHeader.setComment( commentList.join( "\n" ) ); return oldHeader; } 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 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()); } } void Catalog::removeFuzzyStatus(uint index) { setFuzzy(index,false); } void Catalog::setModified(bool flag) { bool old=d->_modified; d->_modified=flag; if(old!=d->_modified) emit signalModified(flag); } QString Catalog::packageName() const { if( !d->_packageName.isNull() ) return d->_packageName; QString package=d->_url.fileName(); int index=package.find(QRegExp("(\\."+identitySettings().languageCode+")?\\.pot?$")); if(index>0) package=package.left(index); return package; } void Catalog::setPackage(const QString& package ) { const int pos = package.findRev( '/' ); if( pos < 0 ) { d->_packageDir = QString(); d->_packageName = package; } else { d->_packageDir = package.left( pos + 1 ); // We want the / included d->_packageName = package.mid( pos + 1 ); // We do not want / } kdDebug() << k_funcinfo << " " << package << " => " << d->_packageDir << " + " << d->_packageName << endl; } QString Catalog::packageDir() const { QString result; if( !d->_packageDir.isNull() ) result=d->_packageDir; else result=d->_url.directory(false); return result; } QString Catalog::encoding() const { SaveSettings options = saveSettings(); QString encodingStr; if(options.useOldEncoding && d->fileCodec) { encodingStr = charsetString(d->fileCodec); } else { encodingStr= charsetString(options.encoding); } return encodingStr; } 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(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 == RECOVERED_HEADER_ERROR ) { const uint entries = numberOfEntries(); if ( !entries ) { // KBabel cannot work correctly with not any entry kdWarning() << k_funcinfo << " No entries! Assuming parse error!" << endl; delete filter; return NO_ENTRY_ERROR; } //kdDebug( KBABEL ) << k_funcinfo << " Success (full or partial) " << entries << endl; setModified(false); d->_url=url; if( package.isEmpty() ) { d->_packageName=QString::null; d->_packageDir=QString::null; } else setPackage(package); emit signalFileOpened(d->_readOnly); emit signalNumberOfFuzziesChanged(numberOfFuzzies()); emit signalNumberOfUntranslatedChanged(numberOfUntranslated()); emit signalTotalNumberChanged( entries ); } delete filter; return error; } else { return OS_ERROR; } } 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(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; KIO::NetAccess::removeTempFile(target); return STOPPED; } // Templates should not have recoverable errors (or they are bad templates) if( error == OK ) { const uint entries = numberOfEntries(); if ( !entries ) { // KBabel cannot work correctly with not any entry kdWarning() << k_funcinfo << " No entries! Assuming parse error!" << endl; delete filter; KIO::NetAccess::removeTempFile(target); return NO_ENTRY_ERROR; } setModified(false); d->_url = saveURL; if( package.isEmpty() ) { d->_packageName=QString::null; d->_packageDir=QString::null; } else setPackage(package); emit signalFileOpened(d->_readOnly); emit signalNumberOfFuzziesChanged(numberOfFuzzies()); emit signalNumberOfUntranslatedChanged(numberOfUntranslated()); emit signalTotalNumberChanged( entries ); } delete filter; // and remove the temp file KIO::NetAccess::removeTempFile(target); return error; } else { return OS_ERROR; } } 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; // ### KDE4: return "lines" not "output" const QStringList lines = QStringList::split("\n",output); for ( QStringList::const_iterator it = lines.constBegin(); it != lines.constEnd(); ++it ) { if( (*it).find(QRegExp("^.+:\\d+:")) >= 0 ) { const int begin=(*it).find(":",0)+1; const int end=(*it).find(":",begin); const 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; } void Catalog::clearErrorList() { QValueList::Iterator it; for(it = d->_errorIndex.begin(); it != d->_errorIndex.end(); ++it) { d->_entries[(*it)].setSyntaxError(false); d->_entries[(*it)].clearErrors(); } d->_errorIndex.clear(); } void Catalog::removeFromErrorList(uint index) { if(d->_errorIndex.contains(index)) { d->_errorIndex.remove(index); d->_entries[index].setSyntaxError(false); d->_entries[index].clearErrors(); } } QStringList Catalog::itemStatus(uint index, bool recheck, QPtrList 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(); } 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(); } 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::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; } 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::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(); } void Catalog::clear() { d->_errorIndex.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(); } uint Catalog::numberOfEntries() const { return d->_entries.count(); } uint Catalog::numberOfFuzzies() const { return d->_fuzzyIndex.count(); } 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(); } bool Catalog::hasError(uint index, DocPosition& pos) const { if( d->_errorIndex.contains(index) ) { pos.item=index; pos.form=0; return true; } return false; } PluralFormType Catalog::pluralForm(uint index) const { if ( d->_entries.isEmpty() ) return NoPluralForm; if(index > numberOfEntries()) return NoPluralForm; return static_cast(d->_entries[index].pluralForm()); } 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; } int Catalog::nextFuzzy(uint startIndex, DocPosition& pos) const { pos.item=findNextInList(d->_fuzzyIndex,startIndex); pos.form=0; return pos.item; } int Catalog::prevFuzzy(uint startIndex, DocPosition& pos) const { pos.item=findPrevInList(d->_fuzzyIndex,startIndex); pos.form=0; return pos.item; } int Catalog::nextUntranslated(uint startIndex, DocPosition& pos) const { pos.item=findNextInList(d->_untransIndex,startIndex); pos.form=0; return pos.item; } int Catalog::prevUntranslated(uint startIndex, DocPosition& pos) const { pos.item=findPrevInList(d->_untransIndex,startIndex); pos.form=0; return pos.item; } int Catalog::nextError(uint startIndex, DocPosition& pos) const { pos.item=findNextInList(d->_errorIndex,startIndex); pos.form=0; return pos.item; } int Catalog::prevError(uint startIndex, DocPosition& pos) const { pos.item=findPrevInList(d->_errorIndex,startIndex); pos.form=0; return pos.item; } void Catalog::registerView(CatalogView* view) { if(d->_views.containsRef(view)==0) { d->_views.append(view); } } void Catalog::removeView(CatalogView* view) { d->_views.removeRef(view); } 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(saveSettings()); emit signalSettingsChanged(identitySettings()); emit signalSettingsChanged(miscSettings()); emit signalSettingsChanged(tagSettings()); } Project::Ptr Catalog::project() const { return d->_project; } void Catalog::readPreferences() { getNumberOfPluralForms(); 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) ; } void Catalog::savePreferences() { 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->_project->identitySettings(); } SaveSettings Catalog::saveSettings() const { return d->_project->saveSettings(); } MiscSettings Catalog::miscSettings() const { return d->_project->miscSettings(); } TagSettings Catalog::tagSettings() const { return d->_tagSettings; } bool Catalog::isGeneratedFromDocbook() const { return d->_generatedFromDocbook; } QString Catalog::package() const { return packageDir()+packageName(); } bool Catalog::isReadOnly() const { return d->_readOnly; } void Catalog::setSettings(SaveSettings settings) { d->_project->setSettings(settings); } void Catalog::setSettings(IdentitySettings settings) { IdentitySettings oldsettings = d->_project->identitySettings(); QString oldLanguageCode = oldsettings.languageCode; int oldForms = oldsettings.numberOfPluralForms; d->_project->setSettings(settings); if(oldLanguageCode != settings.languageCode) { getNumberOfPluralForms(); } if(oldForms != settings.numberOfPluralForms) { getNumberOfPluralForms(); } emit signalSettingsChanged(settings); } void Catalog::setSettings(MiscSettings settings) { d->_project->setSettings(settings); emit signalSettingsChanged(settings); } void Catalog::setSettings(TagSettings settings) { d->_tagSettings=settings; emit signalSettingsChanged(settings); } void Catalog::generateIndexLists() { d->_fuzzyIndex.clear(); d->_untransIndex.clear(); clearErrorList(); uint counter=0; for ( QValueVector::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++; } } int Catalog::findNextInList(const QValueList& list,uint index) const { QValueList::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; } int Catalog::findPrevInList(const QValueList& list,uint index) const { QValueList::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; } if ( it == list.constBegin() ) { // Decremeniting the iterator at the begin is undefined, so break the loop break; } } return prevIndex; } QString Catalog::dateTime() const { const QDateTime dt = QDateTime::currentDateTime(); QString dateTimeString; const SaveSettings options = d->_project->saveSettings(); switch(options.dateFormat) { case Qt::LocalDate: { dateTimeString = KGlobal::locale()->formatDateTime( dt ); break; } case Qt::ISODate: { dateTimeString = dt.toString("yyyy-MM-dd hh:mm"); QTime t; const int offset = KRFCDate::localUTCOffset(); const int correction = offset < 0 ? -60 : 60 ; t = t.addSecs( offset * correction ); dateTimeString += ( offset < 0 ? "-" : "+" ); dateTimeString += t.toString("hhmm"); break; } case Qt::TextDate: { dateTimeString = options.customDateFormat; const QDate date = dt.date(); const QTime time = dt.time(); // 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; // AM or PM int hour = time.hour(); if( hour > 12 ) { zone="PM"; hour -= 12; } else { zone="AM"; } 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->_project->identitySettings().timeZone ); QTime t; const int offset = KRFCDate::localUTCOffset(); const int correction = offset < 0 ? -60 : 60; t = t.addSecs( offset * correction ); dateTimeString.replace( "%z", ( offset < 0 ? "-" : "+" ) + t.toString("hhmm") ); break; } } kdDebug(KBABEL) << "New date: " << dateTimeString << endl; return dateTimeString; } ConversionStatus Catalog::saveFile() { if(d->_url.isEmpty()) { kdFatal(KBABEL) << "fatal error: empty filename" << endl; return NO_FILE; } return saveFileAs(d->_url,true); } 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->_project->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) { if( !KIO::NetAccess::upload( tempFile, 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; } QString Catalog::saveTempFile() { QString filename = kapp->tempSaveName("/temp/kbabel_temp.po"); if( writeFile(filename) != OK ) { filename = QString::null; } return filename; } 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(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; } 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(); } 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(); } 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(); } 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 ); } } void Catalog::applyBeginCommand(uint index, Part part, CatalogView* view) { applyEditCommand( new BeginCommand(index,part), view ); } void Catalog::applyEndCommand(uint index, Part part, CatalogView* view) { applyEditCommand( new EndCommand(index,part), view ); } void Catalog::processCommand(EditCommand* cmd,CatalogView* view, bool undo) { kdDebug(KBABEL) << "Catalog::processCommand()" << endl; 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::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::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()); } } } } } 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() )); MiscSettings miscOptions = miscSettings(); 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 = miscOptions.contextInfo.search(targetStr); contextInfoLength = miscOptions.contextInfo.matchedLength(); if(contextInfoPos >= 0) { targetStr.remove(contextInfoPos,contextInfoLength); if(docPos.offset > (uint)contextInfoPos) docPos.offset -= contextInfoLength; } } if(findOpts->ignoreAccelMarker && targetStr.contains(miscOptions.accelMarker)) { accelMarkerPos = targetStr.find(miscOptions.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++; docPos.form = 0; } } 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++; docPos.form = 0; } } break; case Comment: if(findOpts->inMsgid) { if(docPos.item >= numberOfEntries()-1) { endReached=true; } else { docPos.part = Msgid; docPos.item++; docPos.form = 0; } } 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++; docPos.form = 0; } } 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; } 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() )); MiscSettings miscOptions = miscSettings(); 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 = miscOptions.contextInfo.search(targetStr); contextInfoLength = miscOptions.contextInfo.matchedLength(); if(contextInfoPos >= 0) { targetStr.remove(contextInfoPos,contextInfoLength); if(docPos.offset > (uint)contextInfoPos) docPos.offset -= contextInfoLength; } } if(findOpts->ignoreAccelMarker && targetStr.contains(miscOptions.accelMarker)) { accelMarkerPos = targetStr.find(miscOptions.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; } 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 = LevenshteinDistance()(id, (*it)); 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 = LevenshteinDistance()( id, (*it) ); 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 = wordDiff(idForDiff,id); //esp for plural forms *result = r.replace("\\n" + QString(QChar(0x00B6)) + "", "\\n\n"); 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; } void Catalog::setDiffList( const QValueList& 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::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; } QValueList Catalog::asDiffList() { QValueList list; for ( QValueVector::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; } int Catalog::defaultNumberOfPluralForms() const { return d->numberOfPluralForms; } void Catalog::getNumberOfPluralForms() { IdentitySettings options = identitySettings(); if(options.numberOfPluralForms > 0) { d->numberOfPluralForms = options.numberOfPluralForms; return; } QString lang=options.languageCode; if(lang.isEmpty()) { d->numberOfPluralForms=-1; return; } d->numberOfPluralForms = getNumberOfPluralForms(lang); } 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; } 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 } bool Catalog::isModified() const { return d->_modified; } void Catalog::setEntries(QValueVector entries) { d->_entries=entries; // update the project for entries for ( QValueVector::Iterator it = d->_entries.begin(); it != d->_entries.end(); ++it) { it->setProject( d->_project ); } } void Catalog::setObsoleteEntries(QValueList entries) { d->_obsoleteEntries=entries; } QValueList Catalog::obsoleteEntries() const { return d->_obsoleteEntries; } void Catalog::setCatalogExtraData(const QStringList& data) { d->_catalogExtra = data; } QStringList Catalog::catalogExtraData() const { return d->_catalogExtra; } QString Catalog::importPluginID() const { return d->_importID; } QTextCodec* Catalog::fileCodec() const { return d->fileCodec; } void Catalog::setGeneratedFromDocbook(const bool generated) { d->_generatedFromDocbook = generated; } void Catalog::setFileCodec( QTextCodec* codec ) { d->fileCodec = codec; } void Catalog::setErrorIndex( const QValueList& list ) { d->_errorIndex = list; } void Catalog::setImportPluginID( const QString& id ) { d->_importID = id; } void Catalog::stop() { if( d->_active ) emit signalStopActivity(); } void Catalog::stopInternal() { d->_stop = true; } bool Catalog::isActive() { return d->_active; } void Catalog::setMimeTypes( const QString& mimeTypes ) { d->_mimeTypes = mimeTypes; } QString Catalog::mimeTypes() const { return d->_mimeTypes; } void Catalog::wordCount (uint &total, uint &fuzzy, uint &untranslated) const { total = 0; fuzzy = 0; untranslated = 0; QRegExp separator( "[ \n\t]+" ); for ( QValueVector::Iterator it = d->_entries.begin(); it != d->_entries.end(); ++it) { // find out the number of words for this message // join all forms together QString message = (*it).msgid ().join (" "); // remove tags first d->_tagExtractor->setString( message ); message = d->_tagExtractor->plainString(false); QStringList words = QStringList::split ( separator, message ); total += words.count(); if ( (*it).isFuzzy() ) fuzzy += words.count(); else if ( (*it).isUntranslated() ) untranslated += words.count(); } } #include "catalog.moc" // kate: space-indent on; indent-width 4; replace-tabs on;